Skip to content

Implement initial AST structures#23

Merged
leynos merged 6 commits intomainfrom
codex/define-ast-data-structures-in-src/ast.rs
Jul 19, 2025
Merged

Implement initial AST structures#23
leynos merged 6 commits intomainfrom
codex/define-ast-data-structures-in-src/ast.rs

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jul 19, 2025

Summary

  • define manifest AST in src/ast.rs
  • expose AST module
  • note the decision in the design docs
  • mark the roadmap entry as done
  • add unit and behavioural tests for manifest parsing

Testing

  • make fmt
  • make lint
  • make test

https://chatgpt.com/codex/tasks/task_e_687afee89400832283cdd3ed610e1159

Summary by Sourcery

Implement initial manifest AST support: introduce AST structs with serde_yml deserialization, expose the AST module, rename actions to steps, update docs and dependencies, and add comprehensive unit and behavioural tests.

New Features:

  • Define core AST structures in src/ast.rs with serde_yml deserialization
  • Expose the ast module in the library

Enhancements:

  • Rename 'actions' to 'steps' across code, diagrams, and docs
  • Switch YAML parsing from serde_yaml to serde_yml and update related code

Build:

  • Add serde, serde_yml, and semver dependencies

Documentation:

  • Update design docs, README, and roadmap to reflect new AST structures, 'steps' terminology, and serde_yml usage

Tests:

  • Add unit tests for manifest AST deserialization
  • Add Cucumber behavioural tests and data for manifest parsing

Chores:

  • Mark AST definition roadmap entry as done

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 19, 2025

Summary by CodeRabbit

  • New Features
    • Introduced manifest parsing support with a robust Abstract Syntax Tree (AST) for Netsuke manifests, enabling semantic version validation and improved type safety.
    • Added manifest parsing tests, including feature scenarios and YAML fixtures, to ensure correct deserialisation and error handling.
  • Documentation
    • Updated design documentation to reflect schema changes, terminology updates, and clarified design decisions.
    • Improved roadmap and README formatting for clarity and consistency.
  • Tests
    • Expanded test coverage with unit and feature tests for manifest parsing and validation.
  • Chores
    • Updated dependencies to include new libraries for serialisation and semantic versioning.

Walkthrough

Expand the codebase to support parsing and deserialising Netsuke YAML manifests into a structured Abstract Syntax Tree (AST). Add new AST data structures, update documentation, extend tests with manifest parsing scenarios, and introduce supporting dependencies. Integrate the AST module into the library’s public API and test infrastructure.

Changes

File(s) Change Summary
Cargo.toml Add serde (with derive), serde_yml, and semver dependencies.
src/ast.rs Introduce AST structs/enums for manifest parsing: NetsukeManifest, Rule, Recipe, Target, StringOrList.
src/lib.rs Publicly expose the new ast module.
docs/netsuke-design.md Update manifest schema docs: version format, rename actions to steps, add E-R diagram, and design rationale.
docs/roadmap.md Mark AST data structure task as completed; update "actions" to "steps" in checklist.
tests/ast_tests.rs Add unit tests for manifest deserialisation, including error cases and variant handling.
tests/data/minimal.yml Add minimal YAML manifest fixture with version "1.0.0" and a single target "hello".
tests/features/manifest.feature Add Cucumber feature testing manifest parsing and target name validation.
tests/steps/manifest_steps.rs Add Cucumber step definitions for parsing manifest and asserting version and target name.
tests/steps/mod.rs Import new manifest_steps module.
tests/cucumber.rs Extend CliWorld struct with manifest and manifest_error fields.

Sequence Diagram(s)

sequenceDiagram
    participant TestRunner
    participant FileSystem
    participant SerdeYaml
    participant AST

    TestRunner->>FileSystem: Read manifest YAML file
    FileSystem-->>TestRunner: Return YAML string
    TestRunner->>SerdeYaml: Deserialize YAML into AST structs
    SerdeYaml-->>AST: Populate AST data structures
    SerdeYaml-->>TestRunner: Return AST or error
    TestRunner->>TestRunner: Store AST or error in test world
Loading

Possibly related PRs

Poem

In YAML’s embrace, the AST awakes,
With serde’s magic, structure it takes.
From docs to tests, the journey’s begun,
Parsing manifests, one by one.
Now targets and rules in Rust reside,
Let the build adventures coincide! 🚀


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c66df40 and ede8aa8.

📒 Files selected for processing (3)
  • docs/netsuke-design.md (13 hunks)
  • src/ast.rs (1 hunks)
  • tests/ast_tests.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs

Instructions used from:

Sources:
⚙️ CodeRabbit Configuration File

**/*.md

Instructions used from:

Sources:
⚙️ CodeRabbit Configuration File

🧠 Learnings (1)
src/ast.rs (1)
Learnt from: CR
PR: leynos/netsuke#0
File: docs/roadmap.md:0-0
Timestamp: 2025-07-18T19:04:19.414Z
Learning: Applies to docs/src/ast.rs : Define the core Abstract Syntax Tree (AST) data structures (NetsukeManifest, Rule, Target, StringOrList, Recipe) in src/ast.rs.
🪛 LanguageTool
docs/netsuke-design.md

[uncategorized] ~180-~180: Loose punctuation mark.
Context: ... Ninja build statement.[^3] - steps: A secondary list of build targets. Any ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~183-~183: Loose punctuation mark.
Context: ...ways: false }by default. -defaults`: An optional list of target names to be ...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (20)
tests/ast_tests.rs (6)

1-6: Excellent module documentation and imports.

The module documentation clearly explains the purpose, and the imports are appropriate and necessary for the comprehensive AST testing.


7-36: Well-structured comprehensive test.

The test properly validates manifest parsing, semantic versioning, and recipe field extraction. The use of let-else patterns and descriptive error messages enhances test reliability.


38-62: Thorough validation of required field enforcement.

The test comprehensively covers missing required fields scenarios and properly validates strict deserialization behaviour.


64-105: Excellent validation of serde configuration.

Both tests properly validate the deny_unknown_fields attribute and handle edge cases with empty collections appropriately.


107-158: Comprehensive StringOrList enum validation.

The test thoroughly covers all variants of the StringOrList enum, ensuring the flexible YAML syntax works correctly for user convenience.


160-215: Excellent optional field and enum validation.

Both tests provide thorough coverage - the optional fields test validates presence/absence scenarios with proper Option handling, and the enum test ensures invalid variants are rejected appropriately.

docs/netsuke-design.md (8)

159-162: Correct alignment with semantic versioning.

The documentation now properly reflects the use of semantic versioning with the semver crate, matching the AST implementation.


180-181: Correct terminology alignment.

The renaming from "actions" to "steps" properly aligns with the AST implementation and maintains consistency throughout the documentation.


187-232: Excellent diagram representation of AST structure.

The ER diagram accurately represents the AST data structures and their relationships, providing clear visual documentation of the manifest schema.


276-277: Consistent terminology update.

The reference correctly uses "steps" terminology, maintaining consistency with the AST field renaming.


376-395: Correct crate reference updates.

The documentation properly reflects the switch to serde_yml with appropriate rationale, aligning with the implementation choices.


414-490: Accurate AST implementation documentation.

The documentation correctly represents the actual AST structures with proper Rust syntax and design rationale, matching the implementation in src/ast.rs.


542-551: Valuable design rationale documentation.

The new design decisions section provides excellent context for implementation choices and helps readers understand the rationale behind AST structure decisions.


500-536: Clear two-pass parsing explanation.

The documentation maintains a clear explanation of the two-pass parsing requirement while correctly updating crate references.

src/ast.rs (6)

1-23: Excellent module documentation and imports.

The module documentation clearly explains the purpose and includes a helpful usage example. The imports are appropriate and necessary for the AST implementation.


24-71: Well-implemented manifest structure.

The NetsukeManifest struct properly implements the design specification with comprehensive documentation, correct semantic versioning, and appropriate serde configuration.


73-91: Consistent and well-documented Rule structure.

The Rule struct properly addresses field type consistency by using StringOrList for deps and includes clear documentation explaining its purpose.


93-109: Well-designed Recipe enum with proper tagging.

The Recipe enum correctly implements tagged deserialization with appropriate aliases for backward compatibility and clear variant documentation.


111-147: Comprehensive Target structure implementation.

The Target struct is excellently implemented with proper documentation, correct attribute ordering, and consistent field types that match the design specification.


149-173: Excellent flexible StringOrList implementation.

The StringOrList enum perfectly implements the user-friendly YAML syntax flexibility with clear documentation and proper untagged deserialization.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/define-ast-data-structures-in-src/ast.rs

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Jul 19, 2025

Reviewer's Guide

Introduces initial AST support by defining serde_yml-powered data structures, exposing the AST module publicly, updating docs and dependencies accordingly, marking the roadmap entry complete, and adding both unit and behavioural tests for manifest parsing.

Entity Relationship diagram for the new Netsuke manifest AST

erDiagram
    NETSUKE_MANIFEST {
        string netsuke_version
        map vars
        list rules
        list steps
        list targets
        list defaults
    }
    RULE {
        string name
        Recipe recipe
        string description
        StringOrList deps
    }
    TARGET {
        StringOrList name
        Recipe recipe
        StringOrList sources
        StringOrList deps
        StringOrList order_only_deps
        map vars
        bool phony
        bool always
    }
    RECIPE {
        enum kind
        string command
        string script
        StringOrList rule
    }
    STRING_OR_LIST {
        enum value
    }
    NETSUKE_MANIFEST ||--o{ RULE : contains
    NETSUKE_MANIFEST ||--o{ TARGET : has_steps
    NETSUKE_MANIFEST ||--o{ TARGET : has_targets
    RULE }o--|| RECIPE : uses
    TARGET }o--|| RECIPE : uses
    TARGET }o--|| STRING_OR_LIST : uses
    RECIPE }o--|| STRING_OR_LIST : uses
Loading

Class diagram for the new AST data structures

classDiagram
    class NetsukeManifest {
        +Version netsuke_version
        +HashMap<String, String> vars
        +Vec<Rule> rules
        +Vec<Target> steps
        +Vec<Target> targets
        +Vec<String> defaults
    }
    class Rule {
        +String name
        +Recipe recipe
        +Option<String> description
        +Option<StringOrList> deps
    }
    class Target {
        +StringOrList name
        +Recipe recipe
        +StringOrList sources
        +StringOrList deps
        +StringOrList order_only_deps
        +HashMap<String, String> vars
        +bool phony
        +bool always
    }
    class Recipe {
        <<enumeration>>
        Command(command: String)
        Script(script: String)
        Rule(rule: StringOrList)
    }
    class StringOrList {
        <<enumeration>>
        Empty
        String(String)
        List(Vec<String>)
    }
    NetsukeManifest "1" o-- "*" Rule
    NetsukeManifest "1" o-- "*" Target : steps
    NetsukeManifest "1" o-- "*" Target : targets
    Rule "1" o-- "1" Recipe
    Target "1" o-- "1" Recipe
    Target "1" o-- "1" StringOrList : name
    Target "1" o-- "1" StringOrList : sources
    Target "1" o-- "1" StringOrList : deps
    Target "1" o-- "1" StringOrList : order_only_deps
    Recipe "1" o-- "1" StringOrList : rule
Loading

File-Level Changes

Change Details Files
Implement initial AST data structures with serde_yml deserialization
  • Define NetsukeManifest, Rule, Target, Recipe, StringOrList in src/ast.rs
  • Use semver::Version for the version field and HashMap<String,String> for vars
  • Rename actions → steps and provide default fields with deny_unknown_fields
  • Implement untagged StringOrList with default Empty variant
  • Apply serde(tag) and alias annotations for Recipe enum
src/ast.rs
Expose the new AST module in the library API
  • Add pub mod ast; to src/lib.rs
src/lib.rs
Update project dependencies for AST parsing
  • Add serde with derive feature
  • Add serde_yml crate for YAML deserialization
  • Add semver with serde support
Cargo.toml
Refresh documentation to align with AST implementation
  • Replace ‘actions’ with ‘steps’ and update diagrams in docs/netsuke-design.md
  • Switch serde_yaml references to serde_yml in design docs
  • Update README.md formatting and terminology
  • Mark AST definition as done in docs/roadmap.md
docs/netsuke-design.md
README.md
docs/roadmap.md
Add comprehensive tests for manifest parsing
  • Create unit tests in tests/ast_tests.rs for deserialization scenarios
  • Extend cucumber world in tests/cucumber.rs with manifest fields
  • Add step definitions in tests/steps/manifest_steps.rs
  • Include minimal YAML sample and feature file for behavioural tests
tests/ast_tests.rs
tests/cucumber.rs
tests/steps/manifest_steps.rs
tests/data/minimal.yml
tests/features/manifest.feature

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and found some issues that need to be addressed.

  • Consider using StringOrList for the Rule.deps field so that it can accept both single and multiple dependencies and stay consistent with Target deps.
  • It might be worth parsing netsuke_version into a semantic version type (e.g. via the semver crate) instead of a raw String to catch invalid version formats early.
  • The manifest.vars map uses serde_yaml::Value while Target.vars is HashMap<String,String>—aligning these types could simplify variable handling and validation.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider using StringOrList for the Rule.deps field so that it can accept both single and multiple dependencies and stay consistent with Target deps.
- It might be worth parsing netsuke_version into a semantic version type (e.g. via the semver crate) instead of a raw String to catch invalid version formats early.
- The manifest.vars map uses serde_yaml::Value while Target.vars is HashMap<String,String>—aligning these types could simplify variable handling and validation.

## Individual Comments

### Comment 1
<location> `src/ast.rs:23` </location>
<code_context>
+    pub rules: Vec<Rule>,
+
+    #[serde(default)]
+    pub actions: Vec<Target>,
+
+    pub targets: Vec<Target>,
</code_context>

<issue_to_address>
The 'actions' field may overlap conceptually with 'targets', potentially causing confusion.

Consider clarifying the distinction between 'actions' and 'targets' or renaming them for better clarity.

Suggested implementation:

```rust
    #[serde(default)]
    pub steps: Vec<Target>,

    pub targets: Vec<Target>,

```

```rust
    #[serde(default)]
    pub steps: Vec<Target>,

    pub targets: Vec<Target>,

```

You will also need to update any code that references the `actions` field elsewhere in the codebase (e.g., deserialization, usage in logic, tests) to use the new `steps` name.
If the distinction between `steps` and `targets` is still unclear, consider adding documentation comments to each field explaining their purpose.
</issue_to_address>

### Comment 2
<location> `tests/ast_tests.rs:6` </location>
<code_context>
+use netsuke::ast::*;
+use rstest::rstest;
+
+#[rstest]
+fn parse_minimal_manifest() {
+    let yaml = r#"
+        netsuke_version: "1.0"
+        targets:
+          - name: hello
+            recipe:
+              kind: command
+              command: "echo hi"
+    "#;
+
+    let manifest: NetsukeManifest = serde_yaml::from_str(yaml).expect("parse");
+
+    assert_eq!(manifest.netsuke_version, "1.0");
+    let first = manifest.targets.first().expect("target");
+    if let StringOrList::String(name) = &first.name {
+        assert_eq!(name, "hello");
+    } else {
+        panic!("target name should be String");
+    }
+}
</code_context>

<issue_to_address>
Unit tests only cover minimal manifest; edge cases are missing.

Please add tests for edge cases, including missing required fields, unknown fields, empty lists/maps, all `StringOrList` variants, presence/absence of optional fields, and invalid enum variants. This will improve deserialization robustness and error handling coverage.

Suggested implementation:

```rust
#[rstest]
fn parse_minimal_manifest() {
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
    "#;

    let manifest: NetsukeManifest = serde_yaml::from_str(yaml).expect("parse");

    assert_eq!(manifest.netsuke_version, "1.0");
    let first = manifest.targets.first().expect("target");
    if let StringOrList::String(name) = &first.name {
        assert_eq!(name, "hello");
    } else {
        panic!("target name should be String");
    }
}

#[test]
fn missing_required_fields() {
    // Missing netsuke_version
    let yaml = r#"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());

    // Missing targets
    let yaml = r#"
        netsuke_version: "1.0"
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());

    // Missing name in target
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - recipe:
              kind: command
              command: "echo hi"
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());
}

#[test]
fn unknown_fields() {
    // Unknown field at root
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
        extra_field: 42
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());

    // Unknown field in target
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
            unexpected: true
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());
}

#[test]
fn empty_lists_and_maps() {
    // Empty targets list
    let yaml = r#"
        netsuke_version: "1.0"
        targets: []
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    assert!(manifest.targets.is_empty());

    // Empty recipe (should fail if recipe is required)
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe: {}
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());
}

#[test]
fn string_or_list_variants() {
    // String variant
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    if let StringOrList::String(name) = &manifest.targets[0].name {
        assert_eq!(name, "hello");
    } else {
        panic!("Expected String variant");
    }

    // List variant
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name:
              - hello
              - world
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    if let StringOrList::List(names) = &manifest.targets[0].name {
        assert_eq!(names, &vec!["hello".to_string(), "world".to_string()]);
    } else {
        panic!("Expected List variant");
    }

    // Empty list variant
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: []
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    if let StringOrList::List(names) = &manifest.targets[0].name {
        assert!(names.is_empty());
    } else {
        panic!("Expected List variant");
    }
}

#[test]
fn optional_fields() {
    // If there are optional fields in the manifest, test their presence/absence.
    // Example: assume 'description' is optional in target.
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            description: "A target"
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    // If description is Option<String>, check it
    if let Some(desc) = &manifest.targets[0].description {
        assert_eq!(desc, "A target");
    }

    // Absence of optional field
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    assert!(manifest.targets[0].description.is_none());
}

#[test]
fn invalid_enum_variants() {
    // Invalid kind in recipe
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: not_a_kind
              command: "echo hi"
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());
}

```

- If the `description` field or other optional fields do not exist in your schema, remove or adapt the relevant test code.
- If your `StringOrList` or enum types differ, adjust the test YAML and assertions accordingly.
- If your error handling is not strict (e.g., unknown fields are ignored), you may need to adjust the assertions for those tests.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/ast.rs Outdated
pub rules: Vec<Rule>,

#[serde(default)]
pub actions: Vec<Target>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The 'actions' field may overlap conceptually with 'targets', potentially causing confusion.

Consider clarifying the distinction between 'actions' and 'targets' or renaming them for better clarity.

Suggested implementation:

    #[serde(default)]
    pub steps: Vec<Target>,

    pub targets: Vec<Target>,
    #[serde(default)]
    pub steps: Vec<Target>,

    pub targets: Vec<Target>,

You will also need to update any code that references the actions field elsewhere in the codebase (e.g., deserialization, usage in logic, tests) to use the new steps name.
If the distinction between steps and targets is still unclear, consider adding documentation comments to each field explaining their purpose.

Comment thread tests/ast_tests.rs
Comment on lines +6 to +15
#[rstest]
fn parse_minimal_manifest() {
let yaml = r#"
netsuke_version: "1.0"
targets:
- name: hello
recipe:
kind: command
command: "echo hi"
"#;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Unit tests only cover minimal manifest; edge cases are missing.

Please add tests for edge cases, including missing required fields, unknown fields, empty lists/maps, all StringOrList variants, presence/absence of optional fields, and invalid enum variants. This will improve deserialization robustness and error handling coverage.

Suggested implementation:

#[rstest]
fn parse_minimal_manifest() {
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
    "#;

    let manifest: NetsukeManifest = serde_yaml::from_str(yaml).expect("parse");

    assert_eq!(manifest.netsuke_version, "1.0");
    let first = manifest.targets.first().expect("target");
    if let StringOrList::String(name) = &first.name {
        assert_eq!(name, "hello");
    } else {
        panic!("target name should be String");
    }
}

#[test]
fn missing_required_fields() {
    // Missing netsuke_version
    let yaml = r#"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());

    // Missing targets
    let yaml = r#"
        netsuke_version: "1.0"
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());

    // Missing name in target
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - recipe:
              kind: command
              command: "echo hi"
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());
}

#[test]
fn unknown_fields() {
    // Unknown field at root
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
        extra_field: 42
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());

    // Unknown field in target
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
            unexpected: true
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());
}

#[test]
fn empty_lists_and_maps() {
    // Empty targets list
    let yaml = r#"
        netsuke_version: "1.0"
        targets: []
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    assert!(manifest.targets.is_empty());

    // Empty recipe (should fail if recipe is required)
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe: {}
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());
}

#[test]
fn string_or_list_variants() {
    // String variant
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    if let StringOrList::String(name) = &manifest.targets[0].name {
        assert_eq!(name, "hello");
    } else {
        panic!("Expected String variant");
    }

    // List variant
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name:
              - hello
              - world
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    if let StringOrList::List(names) = &manifest.targets[0].name {
        assert_eq!(names, &vec!["hello".to_string(), "world".to_string()]);
    } else {
        panic!("Expected List variant");
    }

    // Empty list variant
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: []
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    if let StringOrList::List(names) = &manifest.targets[0].name {
        assert!(names.is_empty());
    } else {
        panic!("Expected List variant");
    }
}

#[test]
fn optional_fields() {
    // If there are optional fields in the manifest, test their presence/absence.
    // Example: assume 'description' is optional in target.
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            description: "A target"
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    // If description is Option<String>, check it
    if let Some(desc) = &manifest.targets[0].description {
        assert_eq!(desc, "A target");
    }

    // Absence of optional field
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: command
              command: "echo hi"
    "#;
    let manifest = serde_yaml::from_str::<NetsukeManifest>(yaml).expect("parse");
    assert!(manifest.targets[0].description.is_none());
}

#[test]
fn invalid_enum_variants() {
    // Invalid kind in recipe
    let yaml = r#"
        netsuke_version: "1.0"
        targets:
          - name: hello
            recipe:
              kind: not_a_kind
              command: "echo hi"
    "#;
    assert!(serde_yaml::from_str::<NetsukeManifest>(yaml).is_err());
}
  • If the description field or other optional fields do not exist in your schema, remove or adapt the relevant test code.
  • If your StringOrList or enum types differ, adjust the test YAML and assertions accordingly.
  • If your error handling is not strict (e.g., unknown fields are ignored), you may need to adjust the assertions for those tests.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🔭 Outside diff range comments (1)
src/lib.rs (1)

1-4: Update outdated module documentation.

The module documentation stating the library "only exposes the command line interface definitions" is now incorrect with the addition of the ast module.

-//! Currently this library only exposes the command line interface
-//! definitions used by the binary and tests.
+//! This library exposes the command line interface definitions and
+//! Abstract Syntax Tree (AST) structures for manifest parsing.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bca5106 and 6893c7f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml (1 hunks)
  • docs/netsuke-design.md (1 hunks)
  • docs/roadmap.md (1 hunks)
  • src/ast.rs (1 hunks)
  • src/lib.rs (1 hunks)
  • tests/ast_tests.rs (1 hunks)
  • tests/cucumber.rs (1 hunks)
  • tests/data/minimal.yml (1 hunks)
  • tests/features/manifest.feature (1 hunks)
  • tests/steps/manifest_steps.rs (1 hunks)
  • tests/steps/mod.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
Cargo.toml

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md
docs/**/*.md

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md
**/*.md

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md

⚙️ CodeRabbit Configuration File

**/*.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md

⚙️ CodeRabbit Configuration File

🧠 Learnings (1)
src/ast.rs (1)
Learnt from: CR
PR: leynos/netsuke#0
File: docs/roadmap.md:0-0
Timestamp: 2025-07-18T19:04:19.414Z
Learning: Applies to docs/src/ast.rs : Define the core Abstract Syntax Tree (AST) data structures (NetsukeManifest, Rule, Target, StringOrList, Recipe) in src/ast.rs.
🧬 Code Graph Analysis (1)
src/ast.rs (1)
tests/steps/manifest_steps.rs (1)
  • serde_yaml (11-11)
🪛 LanguageTool
docs/netsuke-design.md

[uncategorized] ~557-~557: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...Listprovides a defaultEmpty` variant so optional lists are trivial to represent...

(COMMA_COMPOUND_SENTENCE_2)

🔇 Additional comments (12)
Cargo.toml (1)

8-8: Well-chosen serde configuration.

The serde dependency with derive feature is appropriately configured for AST deserialization needs.

tests/steps/mod.rs (1)

2-2: Clean module organization.

The addition of the manifest_steps module follows the established pattern and maintains clear separation of test step concerns.

docs/roadmap.md (1)

19-21: Accurate roadmap progress tracking.

The completion status correctly reflects the implemented AST data structures, maintaining documentation accuracy with the actual codebase state.

tests/features/manifest.feature (1)

1-6: Well-structured behaviour test.

The feature test follows proper BDD structure with clear scenario definition and concrete assertions for manifest parsing verification.

src/lib.rs (1)

6-6: Proper module exposure for AST functionality.

The public ast module declaration correctly exposes the AST data structures for use in tests and CLI integration.

tests/data/minimal.yml (1)

1-6: LGTM! Clean minimal test fixture.

This test fixture correctly implements the basic manifest structure with the required netsuke_version field and a simple target definition. The YAML syntax is valid and the structure aligns with the design documentation.

tests/cucumber.rs (1)

7-8: LGTM! Proper extension of test state.

The addition of manifest and manifest_error fields correctly extends the shared test state to support manifest parsing scenarios. The Option wrapping enables proper error handling in Cucumber steps.

tests/ast_tests.rs (1)

1-2: LGTM! Proper module documentation.

The module-level documentation clearly states the purpose. Note the correct en-GB spelling of "deserialisation".

tests/steps/manifest_steps.rs (2)

1-2: LGTM! Clear module documentation.

The module purpose is clearly stated and follows the required documentation standards.


23-31: LGTM! Proper Clippy warning handling.

The #[expect] attribute is correctly scoped with a clear reason. The step implementation is straightforward and correct.

src/ast.rs (2)

1-5: Excellent module documentation.

The module documentation clearly explains the purpose and utility, properly references the design document, and follows the required //! format.


7-8: Clean and necessary imports.

The imports are minimal and all used appropriately in the code below.

Comment thread Cargo.toml Outdated
Comment thread docs/netsuke-design.md Outdated
Comment on lines +553 to +558
### 3.4 Design Decisions

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.
This keeps YAML manifests concise while ensuring forward compatibility.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Fix grammar and expand design rationale.

The design decisions section provides valuable context. However, address the grammatical issue and consider expanding the rationale.

Apply this fix for the comma usage:

-Unknown fields are rejected to surface user errors early. `StringOrList`
-provides a default `Empty` variant so optional lists are trivial to represent.
+Unknown fields are rejected to surface user errors early. `StringOrList`
+provides a default `Empty` variant, so optional lists are trivial to represent.

Consider expanding on why strict field validation is beneficial for user experience and how the design choices support forward compatibility.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### 3.4 Design Decisions
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.
This keeps YAML manifests concise while ensuring forward compatibility.
### 3.4 Design Decisions
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.
This keeps YAML manifests concise while ensuring forward compatibility.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~557-~557: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...Listprovides a defaultEmpty` variant so optional lists are trivial to represent...

(COMMA_COMPOUND_SENTENCE_2)

🤖 Prompt for AI Agents
In docs/netsuke-design.md around lines 553 to 558, fix the grammatical issue by
correcting comma usage in the design decisions paragraph. Expand the rationale
by explaining that strict field validation helps catch user errors early,
improving user experience, and clarify how using a default Empty variant in
StringOrList supports forward compatibility by allowing optional lists without
breaking existing manifests. Make the explanation clear and concise to enhance
reader understanding.

Comment thread tests/ast_tests.rs
Comment on lines +37 to +45
#[then(expr = "the first target name is {string}")]
fn first_target_name(world: &mut CliWorld, name: String) {
let manifest = world.manifest.as_ref().expect("manifest");
let first = manifest.targets.first().expect("targets");
match &first.name {
StringOrList::String(value) => assert_eq!(value, &name),
other => panic!("unexpected variant {other:?}"),
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Enhance error message consistency.

The step implementation works correctly but improve error message format for consistency.

Apply this improvement:

 #[then(expr = "the first target name is {string}")]
 fn first_target_name(world: &mut CliWorld, name: String) {
     let manifest = world.manifest.as_ref().expect("manifest");
     let first = manifest.targets.first().expect("targets");
     match &first.name {
         StringOrList::String(value) => assert_eq!(value, &name),
-        other => panic!("unexpected variant {other:?}"),
+        other => panic!("Expected target name to be StringOrList::String, got: {other:?}"),
     }
 }

This provides clearer error context when the assertion fails.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[then(expr = "the first target name is {string}")]
fn first_target_name(world: &mut CliWorld, name: String) {
let manifest = world.manifest.as_ref().expect("manifest");
let first = manifest.targets.first().expect("targets");
match &first.name {
StringOrList::String(value) => assert_eq!(value, &name),
other => panic!("unexpected variant {other:?}"),
}
}
#[then(expr = "the first target name is {string}")]
fn first_target_name(world: &mut CliWorld, name: String) {
let manifest = world.manifest.as_ref().expect("manifest");
let first = manifest.targets.first().expect("targets");
match &first.name {
StringOrList::String(value) => assert_eq!(value, &name),
other => panic!("Expected target name to be StringOrList::String, got: {other:?}"),
}
}
🤖 Prompt for AI Agents
In tests/steps/manifest_steps.rs around lines 37 to 45, improve the error
message consistency by updating the panic message in the match arm to a clearer
and more consistent format. Replace the current panic message with one that
explicitly states the unexpected variant encountered, ensuring it matches the
style used elsewhere for error messages in this file.

Comment thread tests/steps/manifest_steps.rs
Comment thread src/ast.rs Outdated
Comment on lines +79 to +87
/// A helper for fields that accept either a string or list of strings.
#[derive(Debug, Deserialize, Default)]
#[serde(untagged)]
pub enum StringOrList {
#[default]
Empty,
String(String),
List(Vec<String>),
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add comprehensive documentation with usage examples.

The StringOrList enum needs detailed /// documentation explaining its purpose and showing how it handles different YAML inputs.

Apply this diff to add proper documentation:

-/// A helper for fields that accept either a string or list of strings.
+/// A helper for fields that accept either a string or list of strings.
+///
+/// This enum allows manifest fields to accept flexible YAML syntax,
+/// automatically deserialising both single strings and string arrays
+/// into a unified type.
+///
+/// # Examples
+///
+/// ## Single string input
+/// ```
+/// use netsuke::ast::StringOrList;
+/// use serde_yaml;
+///
+/// let yaml = r#""single-item""#;
+/// let result: StringOrList = serde_yaml::from_str(yaml).unwrap();
+/// ```
+///
+/// ## List input
+/// ```
+/// use netsuke::ast::StringOrList;
+/// use serde_yaml;
+///
+/// let yaml = r#"["item1", "item2", "item3"]"#;
+/// let result: StringOrList = serde_yaml::from_str(yaml).unwrap();
+/// ```
+///
+/// ## Empty default
+/// ```
+/// use netsuke::ast::StringOrList;
+///
+/// let empty = StringOrList::default();
+/// assert!(matches!(empty, StringOrList::Empty));
+/// ```
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// A helper for fields that accept either a string or list of strings.
#[derive(Debug, Deserialize, Default)]
#[serde(untagged)]
pub enum StringOrList {
#[default]
Empty,
String(String),
List(Vec<String>),
}
/// A helper for fields that accept either a string or list of strings.
///
/// This enum allows manifest fields to accept flexible YAML syntax,
/// automatically deserialising both single strings and string arrays
/// into a unified type.
///
/// # Examples
///
/// ## Single string input
///
🤖 Prompt for AI Agents
In src/ast.rs around lines 79 to 87, the StringOrList enum lacks detailed
documentation and usage examples. Add comprehensive /// doc comments above the
enum definition explaining its purpose as a helper for fields accepting either a
string or list of strings. Include example code snippets demonstrating
deserialization from YAML for a single string, a list of strings, and the
default empty variant, using serde_yaml and showing how to instantiate and match
the enum variants.

Comment thread src/ast.rs Outdated
Comment thread src/ast.rs
Comment thread src/ast.rs
Comment on lines +41 to +51
/// Execution style for rules and targets.
#[derive(Debug, Deserialize)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum Recipe {
#[serde(alias = "command")]
Command { command: String },
#[serde(alias = "script")]
Script { script: String },
#[serde(alias = "rule")]
Rule { rule: StringOrList },
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add comprehensive documentation with examples for each variant.

The Recipe enum needs detailed /// documentation explaining each variant and their use cases, with examples as required for public APIs.

Apply this diff to add proper documentation:

-/// Execution style for rules and targets.
+/// Execution style for rules and targets.
+///
+/// Defines how a rule or target should be executed, supporting different
+/// execution models from simple commands to complex scripts or rule references.
+///
+/// # Examples
+///
+/// ## Command variant
+/// ```
+/// use netsuke::ast::Recipe;
+/// use serde_yaml;
+///
+/// let yaml = r#"
+/// kind: command
+/// command: "cargo build --release"
+/// "#;
+/// let recipe: Recipe = serde_yaml::from_str(yaml).unwrap();
+/// ```
+///
+/// ## Script variant
+/// ```
+/// use netsuke::ast::Recipe;
+/// use serde_yaml;
+///
+/// let yaml = r#"
+/// kind: script
+/// script: |
+///   echo "Building..."
+///   cargo build
+/// "#;
+/// let recipe: Recipe = serde_yaml::from_str(yaml).unwrap();
+/// ```
+///
+/// ## Rule reference variant
+/// ```
+/// use netsuke::ast::Recipe;
+/// use serde_yaml;
+///
+/// let yaml = r#"
+/// kind: rule
+/// rule: "compile"
+/// "#;
+/// let recipe: Recipe = serde_yaml::from_str(yaml).unwrap();
+/// ```
🤖 Prompt for AI Agents
In src/ast.rs around lines 41 to 51, the Recipe enum lacks detailed
documentation explaining each variant and their use cases. Add comprehensive ///
doc comments above the enum and each variant, including usage examples for
Command, Script, and Rule variants using serde_yaml deserialization snippets as
shown in the provided diff. This will improve clarity and usability for public
API consumers.

Comment thread src/ast.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (10)
Cargo.toml (1)

8-10: Replace deprecated serde_yaml dependency immediately.

The serde_yaml version "0.9" is deprecated and potentially insecure. This dependency must be updated to a maintained alternative.

Switch to the serde_yml crate which is the maintained successor:

-serde_yaml = "0.9"
+serde_yml = "0.11"

Update all imports in the codebase from serde_yaml to serde_yml.

tests/ast_tests.rs (1)

7-30: Enhance test robustness and error messages.

The test validates core functionality but needs improved error handling and additional assertions.

Apply these improvements:

 #[rstest]
 fn parse_minimal_manifest() {
     let yaml = r#"
-        netsuke_version: "1.0.0"
-        targets:
-          - name: hello
-            recipe:
-              kind: command
-              command: "echo hi"
+netsuke_version: "1.0.0"
+targets:
+  - name: hello
+    recipe:
+      kind: command
+      command: "echo hi"
     "#;

     let manifest: NetsukeManifest = serde_yaml::from_str(yaml).expect("parse");

     assert_eq!(
         manifest.netsuke_version,
         Version::parse("1.0.0").expect("ver")
     );
     let first = manifest.targets.first().expect("target");
-    if let StringOrList::String(name) = &first.name {
-        assert_eq!(name, "hello");
-    } else {
-        panic!("target name should be String");
-    }
+    let StringOrList::String(name) = &first.name else {
+        panic!("Expected target name to be StringOrList::String, got: {:?}", first.name);
+    };
+    assert_eq!(name, "hello");
+    
+    // Verify recipe fields
+    if let Recipe::Command { command } = &first.recipe {
+        assert_eq!(command, "echo hi");
+    } else {
+        panic!("Expected command recipe, got: {:?}", first.recipe);
+    }
tests/steps/manifest_steps.rs (2)

8-21: Improve error handling for file operations.

The step implementation uses expect() which panics on file read errors. Replace with proper error handling to distinguish between file I/O errors and parsing errors.


37-45: Enhance error message consistency.

The panic message could be clearer about expectations. Update to explicitly state what variant was expected.

docs/netsuke-design.md (1)

541-550: Fix grammar in design decisions section.

Add a comma before "so" to connect the independent clauses properly.

src/ast.rs (5)

11-31: Add comprehensive Rustdoc documentation for the public API.

The struct needs detailed /// documentation explaining each field and providing usage examples as required by the coding guidelines.


33-41: Add comprehensive documentation with examples.

The Rule struct requires detailed /// documentation explaining its purpose and providing usage examples for the public API.


43-53: Add comprehensive documentation with examples for each variant.

The Recipe enum needs detailed /// documentation explaining each variant and their use cases, with examples as required for public APIs.


55-79: Add comprehensive documentation with field explanations.

The Target struct needs detailed /// documentation explaining each field's purpose and providing examples as required for public APIs.


81-89: Add comprehensive documentation with usage examples.

The StringOrList enum needs detailed /// documentation explaining its purpose and showing how it handles different YAML inputs.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6893c7f and bd25656.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Cargo.toml (1 hunks)
  • docs/netsuke-design.md (6 hunks)
  • docs/roadmap.md (2 hunks)
  • src/ast.rs (1 hunks)
  • tests/ast_tests.rs (1 hunks)
  • tests/data/minimal.yml (1 hunks)
  • tests/features/manifest.feature (1 hunks)
  • tests/steps/manifest_steps.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
Cargo.toml

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md
docs/**/*.md

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md
**/*.md

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md

⚙️ CodeRabbit Configuration File

**/*.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md

⚙️ CodeRabbit Configuration File

🧠 Learnings (1)
src/ast.rs (1)
Learnt from: CR
PR: leynos/netsuke#0
File: docs/roadmap.md:0-0
Timestamp: 2025-07-18T19:04:19.414Z
Learning: Applies to docs/src/ast.rs : Define the core Abstract Syntax Tree (AST) data structures (NetsukeManifest, Rule, Target, StringOrList, Recipe) in src/ast.rs.
🪛 LanguageTool
docs/netsuke-design.md

[uncategorized] ~180-~180: Loose punctuation mark.
Context: ... Ninja build statement.[^3] - steps: A secondary list of build targets. Any ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~183-~183: Loose punctuation mark.
Context: ...ways: false }by default. -defaults`: An optional list of target names to be ...

(UNLIKELY_OPENING_PUNCTUATION)


[style] ~187-~187: Would you like to use the Oxford spelling “summarizes”? The spelling ‘summarises’ is also correct.
Context: ...t statement.[^3] The E-R diagram below summarises the structure of a Netsukefile and th...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~545-~545: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...Listprovides a defaultEmpty` variant so optional lists are trivial to represent...

(COMMA_COMPOUND_SENTENCE_2)

🔇 Additional comments (16)
tests/features/manifest.feature (1)

1-6: LGTM - Clean Cucumber feature definition.

The feature file correctly defines a focused end-to-end test for manifest parsing functionality using proper Gherkin syntax.

tests/data/minimal.yml (1)

1-6: Well-structured test fixture.

The minimal YAML manifest provides an appropriate baseline test case with correct semantic versioning and simple command recipe structure.

docs/roadmap.md (2)

19-21: Accurate roadmap progress tracking.

The completion marking and terminology update correctly reflect the implemented AST structures and maintain consistency across project documentation.


32-33: Terminology consistency maintained.

The change from "actions list" to "steps list" aligns with the updated manifest schema terminology.

tests/ast_tests.rs (7)

1-5: Comprehensive test module structure.

The module documentation and imports are well-organised, providing clear context for AST deserialisation testing.


32-56: Thorough validation of required field enforcement.

The test correctly verifies that serde fails when required fields are missing, ensuring robust deserialisation.


58-81: Proper unknown field rejection testing.

The test validates that deny_unknown_fields is working correctly at both root and target levels.


83-99: Good edge case coverage for empty structures.

The test appropriately handles empty lists and validates that empty recipe objects are rejected.


101-152: Comprehensive StringOrList variant testing.

The test thoroughly covers all variants of the StringOrList enum including edge cases like empty lists.


154-197: Effective optional field validation.

The test properly verifies both presence and absence of optional fields in the AST structures.


199-210: Appropriate enum validation testing.

The test ensures invalid enum variants are properly rejected during deserialisation.

tests/steps/manifest_steps.rs (1)

23-31: Well-structured test step with appropriate lint handling.

The lint suppression is properly justified, and the implementation correctly validates the manifest version.

docs/netsuke-design.md (4)

160-162: Version format correctly aligned with semantic versioning.

The change from "1.0" to "1.0.0" properly reflects the use of the semver crate for version validation.


180-183: Documentation correctly updated to reflect AST changes.

The rename from actions to steps is properly documented and aligns with the implementation.


191-232: E-R diagram accurately represents the AST structure.

The diagram correctly shows the relationships between manifest components and aligns with the implementation in src/ast.rs.


415-439: AST documentation correctly updated.

The field updates accurately reflect the implementation:

  • actionssteps rename
  • deps type changed to Option<StringOrList> for consistency

Comment thread docs/netsuke-design.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
src/ast.rs (4)

72-77: Add comprehensive documentation with examples.

The Rule struct needs more detailed documentation explaining its purpose and showing usage examples, as previously suggested.

Add this enhanced documentation:

 /// A reusable command template.
 ///
 /// A rule encapsulates a snippet of work that can be referenced by multiple
 /// targets. It may define a command line, a script block, or delegate to another
 /// named rule. Dependencies are expressed as a whitespace separated list.
+///
+/// # Examples
+///
+/// ```rust
+/// use netsuke::ast::{Rule, Recipe};
+/// use serde_yml;
+///
+/// let yaml = r#"
+/// name: "compile"
+/// recipe:
+///   kind: command
+///   command: "gcc -c $<"
+/// description: "Compile C source files"
+/// "#;
+///
+/// let rule: Rule = serde_yml::from_str(yaml).unwrap();
+/// assert_eq!(rule.name, "compile");
+/// ```

90-94: Add examples for each Recipe variant.

The Recipe enum needs examples demonstrating each execution style, as previously suggested.

Enhance the documentation:

 /// Execution style for rules and targets.
 ///
 /// The variant is selected using the `kind` field in the manifest. Each variant
 /// corresponds to a different way of specifying how a command should run.
+///
+/// # Examples
+///
+/// ## Command variant
+/// ```rust
+/// use netsuke::ast::Recipe;
+/// use serde_yml;
+///
+/// let yaml = r#"
+/// kind: command
+/// command: "cargo build --release"
+/// "#;
+/// let recipe: Recipe = serde_yml::from_str(yaml).unwrap();
+/// ```
+///
+/// ## Script variant  
+/// ```rust
+/// use netsuke::ast::Recipe;
+/// use serde_yml;
+///
+/// let yaml = r#"
+/// kind: script
+/// script: |
+///   echo "Building..."
+///   cargo build
+/// "#;
+/// let recipe: Recipe = serde_yml::from_str(yaml).unwrap();
+/// ```
+///
+/// ## Rule reference variant
+/// ```rust
+/// use netsuke::ast::Recipe;
+/// use serde_yml;
+///
+/// let yaml = r#"
+/// kind: rule
+/// rule: "compile"
+/// "#;
+/// let recipe: Recipe = serde_yml::from_str(yaml).unwrap();
+/// ```

108-113: Add usage examples to Target documentation.

The Target struct would benefit from examples showing typical usage patterns.

Add this example to the documentation:

 /// Targets describe the files produced by a rule and their dependencies.
 /// `phony` targets are always considered out of date, while `always` targets are
 /// regenerated even if their inputs are unchanged.
+///
+/// # Examples
+///
+/// ```rust
+/// use netsuke::ast::{Target, Recipe, StringOrList};
+/// use serde_yml;
+///
+/// let yaml = r#"
+/// name: "build"
+/// recipe:
+///   kind: command
+///   command: "cargo build"
+/// sources: ["src/**/*.rs"]
+/// phony: true
+/// "#;
+///
+/// let target: Target = serde_yml::from_str(yaml).unwrap();
+/// assert!(target.phony);
+/// ```

146-160: Add Rust usage examples for StringOrList.

The enum documentation should include Rust examples demonstrating deserialisation, as previously suggested.

Enhance the documentation:

 /// ```yaml
 /// # Scalar
 /// name: hello
 /// # Sequence
 /// name:
 ///   - hello
 ///   - world
 /// ```
+///
+/// # Examples
+///
+/// ## Single string input
+/// ```rust
+/// use netsuke::ast::StringOrList;
+/// use serde_yml;
+///
+/// let yaml = r#""single-item""#;
+/// let result: StringOrList = serde_yml::from_str(yaml).unwrap();
+/// assert!(matches!(result, StringOrList::String(_)));
+/// ```
+///
+/// ## List input
+/// ```rust
+/// use netsuke::ast::StringOrList;
+/// use serde_yml;
+///
+/// let yaml = r#"["item1", "item2", "item3"]"#;
+/// let result: StringOrList = serde_yml::from_str(yaml).unwrap();
+/// assert!(matches!(result, StringOrList::List(_)));
+/// ```
+///
+/// ## Empty default
+/// ```rust
+/// use netsuke::ast::StringOrList;
+///
+/// let empty = StringOrList::default();
+/// assert!(matches!(empty, StringOrList::Empty));
+/// ```
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd25656 and 62da840.

📒 Files selected for processing (6)
  • Cargo.toml (1 hunks)
  • docs/netsuke-design.md (13 hunks)
  • docs/roadmap.md (2 hunks)
  • src/ast.rs (1 hunks)
  • tests/ast_tests.rs (1 hunks)
  • tests/steps/manifest_steps.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
Cargo.toml

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md
docs/**/*.md

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md
**/*.md

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md

⚙️ CodeRabbit Configuration File

**/*.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • AGENTS.md

⚙️ CodeRabbit Configuration File

🧠 Learnings (2)
docs/roadmap.md (1)
Learnt from: CR
PR: leynos/netsuke#0
File: docs/roadmap.md:0-0
Timestamp: 2025-07-18T19:04:19.414Z
Learning: Applies to docs/src/ast.rs : Implement the two-pass parsing mechanism: the first pass renders the manifest as a Jinja template, and the second pass parses the resulting pure YAML string with serde_yaml.
src/ast.rs (1)
Learnt from: CR
PR: leynos/netsuke#0
File: docs/roadmap.md:0-0
Timestamp: 2025-07-18T19:04:19.414Z
Learning: Applies to docs/src/ast.rs : Define the core Abstract Syntax Tree (AST) data structures (NetsukeManifest, Rule, Target, StringOrList, Recipe) in src/ast.rs.
🪛 LanguageTool
docs/netsuke-design.md

[uncategorized] ~180-~180: Loose punctuation mark.
Context: ... Ninja build statement.[^3] - steps: A secondary list of build targets. Any ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~183-~183: Loose punctuation mark.
Context: ...ways: false }by default. -defaults`: An optional list of target names to be ...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (19)
Cargo.toml (1)

8-10: LGTM! Dependency choices align with design requirements.

The addition of serde with derive features, serde_yml for YAML parsing, and semver with serde support correctly implements the AST deserialization requirements. The switch from the deprecated serde_yaml to serde_yml addresses previous security concerns.

docs/roadmap.md (2)

19-21: Documentation accurately reflects implementation progress.

Marking the AST data structures task as completed is correct, and the terminology updates from "actions" to "steps" align with the actual implementation.


25-25: Consistent dependency naming updated.

The correction from "serde_yaml" to "serde_yml" maintains consistency with the new dependency choice in Cargo.toml.

tests/ast_tests.rs (5)

1-6: Excellent module documentation and imports.

The module-level documentation clearly states the purpose, and the imports are well-organised with proper use of semver::Version for version handling.


7-36: Robust minimal manifest test with proper assertions.

The test correctly validates all key aspects of manifest parsing: version validation using semver, target name extraction with proper error handling, and recipe field verification. The improved error messages in the expect calls provide clear context for debugging.


38-62: Comprehensive coverage of missing required fields.

The test thoroughly validates that all required fields (netsuke_version, targets, target names) are properly enforced during deserialization, ensuring robust error handling.


64-87: Proper validation of unknown field rejection.

The tests confirm that #[serde(deny_unknown_fields)] is working correctly at both root and nested levels, which helps surface user configuration errors early.


107-158: Thorough StringOrList variant testing.

All three variants (String, List, Empty list) are properly tested with clear assertions, ensuring the flexible field handling works correctly across different manifest formats.

tests/steps/manifest_steps.rs (4)

1-7: Clear module documentation and well-organised imports.

The module purpose is clearly stated, and imports are properly structured for the Cucumber testing framework integration.


8-28: Robust error handling for manifest parsing.

The step correctly handles both file I/O errors and YAML parsing errors separately, providing clear error messages that will aid in test debugging. The improved error handling addresses previous review feedback effectively.


30-38: Proper clippy lint suppression with clear justification.

The #[expect] attribute correctly suppresses the needless_pass_by_value lint with a clear reason explaining the Cucumber framework requirement.


44-52: Improved error message consistency.

The panic message provides clear context about the expected variant, following the improved error message format from previous review feedback.

docs/netsuke-design.md (6)

159-162: Semantic versioning adoption properly documented.

The change to "1.0.0" format and integration with the semver crate provides robust version validation, which is essential for schema evolution.


180-181: Terminology update maintains consistency.

The rename from "actions" to "steps" is consistently applied and aligns with the actual AST implementation.


191-232: Entity-relationship diagram improves clarity.

The new Mermaid ER diagram provides a clearer representation of the manifest structure and relationships compared to a class diagram, making the schema more accessible to users.


376-395: Correct dependency documentation updates.

The comprehensive updates from serde_yaml to serde_yml throughout the section maintain consistency with the actual dependency choices and provide accurate implementation guidance.


542-549: Valuable design decisions section added.

The new section effectively explains the rationale behind key design choices, particularly the strict field validation and StringOrList default variant, which aids understanding and future maintenance.


1305-1306: Implementation roadmap updated correctly.

The reference to serde_yml instead of serde_yaml maintains consistency with the actual dependency choices and provides accurate implementation guidance.

src/ast.rs (1)

52-69: Place doc comments before field attributes.

According to the coding guidelines, doc comments must come before attributes. The current placement violates this rule.

Apply this diff to fix the attribute ordering:

-    #[serde(default)]
     /// Global key/value pairs available to recipes.
+    #[serde(default)]
     pub vars: HashMap<String, String>,

-    #[serde(default)]
     /// Named rule templates that can be referenced by targets.
+    #[serde(default)]
     pub rules: Vec<Rule>,

     /// Optional top-level steps executed before normal targets.
     #[serde(default)]
     pub steps: Vec<Target>,

     /// Primary build targets.
     pub targets: Vec<Target>,

-    #[serde(default)]
     /// Names of targets built when no command line target is supplied.
+    #[serde(default)]
     pub defaults: Vec<String>,

Likely an incorrect or invalid review comment.

Comment thread src/ast.rs
Comment thread src/ast.rs Outdated
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 19, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 19, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 19, 2025

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and found some issues that need to be addressed.

  • I noticed references to serde_yaml were swapped to serde_yml—please double-check that the crate rename is intentional and update any lingering serde_yaml mentions in code, docs, and comments to avoid confusion.
  • The doc comment for Rule.deps still describes a whitespace-separated string, but the field is now a StringOrList; please update the documentation to reflect the new scalar-or-list semantics.
  • The ER diagram in the design doc has field names and relationships (e.g. steps vs. actions) that may not exactly match the AST structs—consider verifying and syncing the diagrams with the current code definitions.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- I noticed references to serde_yaml were swapped to serde_yml—please double-check that the crate rename is intentional and update any lingering serde_yaml mentions in code, docs, and comments to avoid confusion.
- The doc comment for Rule.deps still describes a whitespace-separated string, but the field is now a StringOrList; please update the documentation to reflect the new scalar-or-list semantics.
- The ER diagram in the design doc has field names and relationships (e.g. steps vs. actions) that may not exactly match the AST structs—consider verifying and syncing the diagrams with the current code definitions.

## Individual Comments

### Comment 1
<location> `src/ast.rs:87` </location>
<code_context>
     pub recipe: Recipe,
     pub description: Option<String>,
-    pub deps: Option<String>,
+    pub deps: Option<StringOrList>,
     // Additional fields like 'pool' or 'restat' can be added here
     // to map to more advanced Ninja features.
</code_context>

<issue_to_address>
Inconsistent use of Option<StringOrList> vs. defaulted StringOrList.

Standardize the type and defaulting of 'deps' across Rule and Target to avoid confusion and simplify downstream handling.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/ast.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/ast.rs (1)

9-17: Fix the example by importing StringOrList.

The example uses StringOrList::String without importing it, which would cause a compilation error.

Apply this diff to fix the example:

 //! ```rust
 //! use netsuke::ast::NetsukeManifest;
+//! use netsuke::ast::StringOrList;
 //!
 //! let yaml = r#"netsuke_version: \"1.0.0\"\ntargets:\n  - name: hello\n    recipe:\n      kind: command\n      command: \"echo hi\""#;
 //! let manifest: NetsukeManifest = serde_yml::from_str(yaml).expect("parse");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62da840 and c66df40.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml (1 hunks)
  • README.md (7 hunks)
  • src/ast.rs (1 hunks)
  • tests/steps/manifest_steps.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.md

Instructions used from:

Sources:
⚙️ CodeRabbit Configuration File

**/*.rs

Instructions used from:

Sources:
⚙️ CodeRabbit Configuration File

🧠 Learnings (1)
src/ast.rs (1)
Learnt from: CR
PR: leynos/netsuke#0
File: docs/roadmap.md:0-0
Timestamp: 2025-07-18T19:04:19.414Z
Learning: Applies to docs/src/ast.rs : Define the core Abstract Syntax Tree (AST) data structures (NetsukeManifest, Rule, Target, StringOrList, Recipe) in src/ast.rs.
🪛 LanguageTool
README.md

[uncategorized] ~85-~85: Loose punctuation mark.
Context: ... ``` Targets can also define: - deps: explicit dependencies - `order_only_dep...

(UNLIKELY_OPENING_PUNCTUATION)


[grammar] ~176-~176: Make sure that the adjective ‘small’ is correct. Possibly, it should be an adverb (typically ~ly) that modifies ‘carved’. Possibly, it should be the first word in a compound adjective (hyphenated adjective). Possibly, it is correct.
Context: ... ## Why “Netsuke”? A netsuke is a small carved object used to fasten things securely t...

(ADVERB_OR_HYPHENATED_ADJECTIVE)


[style] ~177-~177: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...It’s not the sword. It’s not the pouch. It’s the thing that connects them. That’s...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[locale-violation] ~182-~182: License must be spelled with a “c” when used as a noun in British English. Use “licence”.
Context: ...ent and the tool that gets it done. ## License [ISC](https://opensource.org/licenses/...

(LICENCE_LICENSE_NOUN_SINGULAR)

🔇 Additional comments (8)
Cargo.toml (1)

8-10: Dependency additions support AST implementation correctly.

The new dependencies appropriately support the AST module: serde with derive features for deserialization, serde_yml as a maintained YAML parser (addressing the deprecated serde_yaml concern), and semver with serde support for version handling.

README.md (1)

3-25: Formatting improvements enhance readability.

The text reflowing improves adherence to the 80-column wrapping guideline whilst maintaining content clarity.

tests/steps/manifest_steps.rs (1)

1-57: Step definitions implement manifest parsing correctly.

The Cucumber step implementations properly handle file reading errors, YAML parsing errors, and provide clear test assertions. The error handling improvements from previous reviews have been correctly implemented.

src/ast.rs (5)

46-70: AST structures correctly implement the manifest schema.

The NetsukeManifest struct properly defines all required fields with appropriate types, including the use of semver::Version for semantic versioning and StringOrList for flexible field parsing.


77-88: Rule structure supports reusable command templates effectively.

The Rule struct correctly encapsulates reusable command patterns with proper field types and serde attributes for strict deserialization.


94-106: Recipe enum provides comprehensive execution styles.

The tagged enum design with aliases correctly supports different execution models (command, script, rule reference) whilst maintaining clear YAML syntax.


113-144: Target structure comprehensively models build targets.

The Target struct correctly defines all necessary fields for build target specification, with proper defaults and flexible string/list handling through StringOrList.


160-170: StringOrList enum enables flexible YAML syntax.

The untagged enum design correctly handles both scalar and sequence YAML inputs, providing the flexibility needed for manifest field specification.

Comment thread README.md
That’s what this is: a tidy connector between your intent and the tool that
gets it done.

## License
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Use British English spelling for "licence".

Apply the British English spelling as required by the coding guidelines:

-## License
+## Licence
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## License
## Licence
🧰 Tools
🪛 LanguageTool

[locale-violation] ~182-~182: License must be spelled with a “c” when used as a noun in British English. Use “licence”.
Context: ...ent and the tool that gets it done. ## License [ISC](https://opensource.org/licenses/...

(LICENCE_LICENSE_NOUN_SINGULAR)

🤖 Prompt for AI Agents
In README.md at line 182, change the heading "License" to use British English
spelling by replacing it with "Licence" to comply with the coding guidelines.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 19, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 19, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link
Copy Markdown

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 19, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos leynos merged commit 5aeb422 into main Jul 19, 2025
2 checks passed
@leynos leynos deleted the codex/define-ast-data-structures-in-src/ast.rs branch July 19, 2025 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant