Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/behavioural-testing-in-rust-with-cucumber.md
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,7 @@ aligned with what is needed.

[^31]: Cucumber in cucumber – Rust – [Docs.rs](http://Docs.rs) — accessed on 14
July 2025 —
<https://docs.rs/cucumber/latest/cucumber/struct.Cucumber.html>
<https://docs.rs/cucumber/latest/cucumber/struct.Cucumber.html>

[^32]: CLI (command-line interface) - Cucumber Rust Book, accessed on
14 July 2025, <https://cucumber-rs.github.io/cucumber/main/cli.html>
Expand Down
15 changes: 8 additions & 7 deletions docs/netsuke-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ deserialization and easy debugging.

Rust

````rust
```rust
// In src/ast.rs

use serde::Deserialize;
Expand Down Expand Up @@ -551,7 +551,7 @@ targets:
- name: my_app
sources: "{{ glob('src/*.c') }}"
rule: compile
````
```

The value of `sources`, `{{ glob('src/*.c') }}`, is not a valid YAML string
from the perspective of a strict parser. Attempting to deserialize this
Expand Down Expand Up @@ -587,9 +587,10 @@ interference, ensuring a robust and predictable ingestion pipeline.
The AST structures are implemented in `src/ast.rs` and derive `Deserialize`.
Unknown fields are rejected to surface user errors early. `StringOrList`
provides a default `Empty` variant, so optional lists are trivial to represent.
The manifest version is parsed using the `semver` crate to validate that it
follows semantic versioning rules. Global and target variable maps now share
the `HashMap<String, String>` type for consistency. This keeps YAML manifests
The manifest version is parsed into a `semver::Version`. Using the library's
`Deserialize` implementation ensures any manifest with an invalid SemVer string
fails to load. Global and target variable maps now share the
`HashMap<String, String>` type for consistency. This keeps YAML manifests
concise while ensuring forward compatibility.

### 3.5 Testing
Expand Down Expand Up @@ -1000,14 +1001,14 @@ structures to the Ninja file syntax.

Code snippet

````ninja
```ninja
# Generated from an ir::Action
rule cc command = gcc -c -o $out $in description = CC $out depfile = $out.d
deps = gcc

```ninja

````
```

3. **Write Build Edges:** Iterate through the `graph.targets` map. For each
`ir::BuildEdge`, write a corresponding Ninja `build` statement. This
Expand Down
4 changes: 2 additions & 2 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ compilation pipeline from parsing to execution.
#[serde(deny_unknown_fields)]
to enable serde_yml parsing. *(done)*

- [ ] Implement parsing for the netsuke_version field and validate it using
the semver crate.
- [x] Implement parsing for the netsuke_version field and validate it using
the semver crate. *(done)*

- [ ] Support `phony` and `always` boolean flags on targets.

Expand Down
2 changes: 1 addition & 1 deletion examples/basic_c.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
netsuke_version: "1.0"
netsuke_version: "1.0.0"

vars:
cc: "{{ env('CC') | default('gcc') }}"
Expand Down
2 changes: 1 addition & 1 deletion examples/photo_edit.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
netsuke_version: "1.0"
netsuke_version: "1.0.0"

vars:
raw_dir: raw_photos
Expand Down
2 changes: 1 addition & 1 deletion examples/visual_design.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
netsuke_version: "1.0"
netsuke_version: "1.0.0"

vars:
src_dir: design/svg
Expand Down
2 changes: 1 addition & 1 deletion examples/website.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
netsuke_version: "1.0"
netsuke_version: "1.0.0"

vars:
pages_dir: pages
Expand Down
2 changes: 1 addition & 1 deletion examples/writing.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
netsuke_version: "1.0"
netsuke_version: "1.0.0"

vars:
chapters_dir: chapters
Expand Down
6 changes: 6 additions & 0 deletions tests/ast_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,9 @@ fn invalid_enum_variants() {
"#;
assert!(serde_yml::from_str::<NetsukeManifest>(yaml).is_err());
}

#[test]
fn invalid_manifest_version() {
let yaml = "netsuke_version: '1.0'";
assert!(serde_yml::from_str::<NetsukeManifest>(yaml).is_err());
}
6 changes: 6 additions & 0 deletions tests/data/invalid_version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
netsuke_version: "1.0"
targets:
- name: invalid
recipe:
kind: command
command: "echo invalid"
5 changes: 5 additions & 0 deletions tests/features/manifest.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ Feature: Manifest parsing
When the manifest file "tests/data/minimal.yml" is parsed
Then the manifest version is "1.0.0"
And the first target name is "hello"

Scenario: Invalid manifest version
When the manifest file "tests/data/invalid_version.yml" is parsed
Then manifest parsing should fail
And the manifest error message should contain "version"
16 changes: 16 additions & 0 deletions tests/steps/manifest_steps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,19 @@ fn first_target_name(world: &mut CliWorld, name: String) {
other => panic!("Expected StringOrList::String, got: {other:?}"),
}
}

#[then("manifest parsing should fail")]
fn manifest_parsing_should_fail(world: &mut CliWorld) {
assert!(world.manifest.is_none(), "expected parsing to fail");
assert!(world.manifest_error.is_some(), "error message missing");
}

#[expect(
clippy::needless_pass_by_value,
reason = "Cucumber requires owned String arguments"
)]
#[then(expr = "the manifest error message should contain {string}")]
fn manifest_error_contains(world: &mut CliWorld, text: String) {
let err = world.manifest_error.as_ref().expect("error");
assert!(err.contains(&text), "{err} does not contain {text}");
}