Skip to content

Add YAML template validation API for plain scalar interpolations#25

Merged
koxudaxi merged 3 commits into
mainfrom
add-yaml-template-validation-api
Mar 18, 2026
Merged

Add YAML template validation API for plain scalar interpolations#25
koxudaxi merged 3 commits into
mainfrom
add-yaml-template-validation-api

Conversation

@koxudaxi

@koxudaxi koxudaxi commented Mar 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a backend validation pass for YAML plain scalars that mix whitespace and interpolations
  • add matching parse_validated_template* and validate_template* APIs for JSON and TOML so the backend surface stays aligned
  • route JSON/TOML/YAML Python bindings through validated parse paths
  • cover the new validation paths in Rust parser tests and Python runtime tests

Testing

  • cargo test --manifest-path rust/Cargo.toml -p tstring-yaml --test parser rejects_plain_scalars_that_mix_whitespace_and_interpolation
  • cargo test --manifest-path rust/Cargo.toml -p tstring-yaml --test parser validates_token_like_plain_scalars_with_interpolation
  • cargo test --manifest-path rust/Cargo.toml -p tstring-json --test parser validates_json_templates_with_supported_interpolations
  • cargo test --manifest-path rust/Cargo.toml -p tstring-toml --test parser validates_toml_templates_with_supported_interpolations
  • cargo test --manifest-path rust/Cargo.toml -p tstring-json --test parser checks_valid_json_templates
  • cargo test --manifest-path rust/Cargo.toml -p tstring-toml --test parser checks_valid_toml_templates
  • uv run pytest yaml-tstring/tests/test_yaml_tstring.py -k errors_cover_parse_render_and_metadata_paths
  • scratch cargo smoke test for tstring_yaml::validate_template

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced validation layer for JSON, TOML, and YAML template parsing with new validation APIs.
    • Templates are now validated at parse time for enhanced reliability.
  • Bug Fixes

    • Improved error handling for YAML plain scalars mixing whitespace and interpolations.
  • Tests

    • Added comprehensive test coverage for template validation across all formats.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026

Copy link
Copy Markdown

Walkthrough

Adds a validated parsing layer across YAML/JSON/TOML template parsers, exposing parse_validated_* and validate_* public functions, updating format/check entry points to use validated parsing, and updating Python bindings and tests to exercise the new validation behavior. Validation enforces constraints like rejecting plain scalars that mix whitespace with interpolations.

Changes

Cohort / File(s) Summary
YAML Backend
rust/yaml-tstring-rs/src/lib.rs, rust/yaml-tstring-rs/tests/parser.rs
Added public validated APIs (parse_validated_template_with_profile, parse_validated_template, validate_template_with_profile, validate_template), implemented internal validation pipeline (validate_template_stream, validate_value_node, validate_key_node, validate_plain_scalar_node), updated format_template_with_profile and check_template_with_profile to use validated paths, and added tests for plain-scalar validation.
JSON Backend
rust/json-tstring-rs/src/lib.rs, rust/json-tstring-rs/tests/parser.rs
Added validated API wrappers and validate_template* / parse_validated_template* entry points; updated format_template_with_profile and check_template_with_profile to call validated variants; tests updated to exercise validation-enabled flow.
TOML Backend
rust/toml-tstring-rs/src/lib.rs, rust/toml-tstring-rs/tests/parser.rs
Added validated API wrappers and validate_template* / parse_validated_template* entry points; changed format_template_with_profile and check_template_with_profile to use validated parsing; tests updated to import and use new functions.
Python Bindings
rust/python-bindings/src/lib.rs
Switched YAML template parsing call sites to use parse_validated_template_with_profile (validated parser) instead of the non-validated parser.
Python Tests
yaml-tstring/tests/test_yaml_tstring.py
Added test asserting TemplateParseError message when rendering a YAML plain scalar that mixes whitespace and an interpolation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant ValidatedParser as parse_validated_template_with_profile
    participant RawParser as parse_template_with_profile
    participant Validator as validate_template_stream
    participant NodeValidator as validate_value_node

    Client->>ValidatedParser: submit(template, profile)
    ValidatedParser->>RawParser: parse(template, profile)
    RawParser-->>ValidatedParser: stream (Yaml/Json/Toml nodes)
    ValidatedParser->>Validator: validate(stream)
    Validator->>Validator: iterate documents
    Validator->>NodeValidator: validate nodes recursively
    NodeValidator->>NodeValidator: validate keys/scalars/interpolations
    NodeValidator-->>Validator: ok / error
    Validator-->>ValidatedParser: ok / error
    ValidatedParser-->>Client: Parsed stream or validation error
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly Related PRs

Poem

🐰 I hopped through streams of YAML, bright and spry,
I sniffed each scalar, watched interpolations fly,
Mixed whitespace? I gently said, "Quote me, friend!"
Now templates hum tidy from end to end,
A rabbit's nod — validation's done with a sigh.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: adding YAML template validation API focused on plain scalar interpolations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-yaml-template-validation-api
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
rust/yaml-tstring-rs/src/lib.rs (1)

2051-2070: Reduce duplicated parse+validate flow in public APIs.

parse_validated_template_with_profile and validate_template_with_profile run the same pipeline. Reusing one from the other keeps behavior unified if validation wiring changes later.

♻️ Proposed refactor
 pub fn validate_template_with_profile(
     template: &TemplateInput,
     profile: YamlProfile,
 ) -> BackendResult<()> {
-    let stream = parse_template_with_profile(template, profile)?;
-    validate_template_stream(&stream)
+    parse_validated_template_with_profile(template, profile).map(|_| ())
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/yaml-tstring-rs/src/lib.rs` around lines 2051 - 2070, Duplicate
parse+validate logic exists between parse_validated_template_with_profile and
validate_template_with_profile; update validate_template_with_profile to call
parse_validated_template_with_profile(template, profile) and map or convert its
BackendResult<YamlStreamNode> to BackendResult<()> (e.g., by discarding the
returned stream or using .map(|_| ())) so all parsing+validation is centralized
in parse_validated_template_with_profile and behavior stays unified across APIs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@rust/yaml-tstring-rs/src/lib.rs`:
- Around line 2051-2070: Duplicate parse+validate logic exists between
parse_validated_template_with_profile and validate_template_with_profile; update
validate_template_with_profile to call
parse_validated_template_with_profile(template, profile) and map or convert its
BackendResult<YamlStreamNode> to BackendResult<()> (e.g., by discarding the
returned stream or using .map(|_| ())) so all parsing+validation is centralized
in parse_validated_template_with_profile and behavior stays unified across APIs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5c373fdc-ebe5-4b70-81e0-41e695026f84

📥 Commits

Reviewing files that changed from the base of the PR and between 6750012 and d1d027d.

📒 Files selected for processing (4)
  • rust/python-bindings/src/lib.rs
  • rust/yaml-tstring-rs/src/lib.rs
  • rust/yaml-tstring-rs/tests/parser.rs
  • yaml-tstring/tests/test_yaml_tstring.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
rust/toml-tstring-rs/src/lib.rs (1)

1926-1931: Tighten negative-infinity assertions in special-float tests.

These checks currently assert only negative sign. Adding is_infinite() would better lock the expected -inf shape and prevent false positives from other negative float values.

Optional test-strengthening diff
-            assert!(
-                special_floats[1]
-                    .as_float()
-                    .expect("float")
-                    .is_sign_negative()
-            );
+            let neg = special_floats[1].as_float().expect("float");
+            assert!(neg.is_infinite() && neg.is_sign_negative());

-            assert!(
-                special_float_deeper_arrays[1][0][0]
-                    .as_float()
-                    .expect("float")
-                    .is_sign_negative()
-            );
+            let neg = special_float_deeper_arrays[1][0][0].as_float().expect("float");
+            assert!(neg.is_infinite() && neg.is_sign_negative());

-            assert!(
-                rendered.data["special_float_inline_table"]["neg"]
-                    .as_float()
-                    .expect("neg float")
-                    .is_sign_negative()
-            );
+            let neg = rendered.data["special_float_inline_table"]["neg"]
+                .as_float()
+                .expect("neg float");
+            assert!(neg.is_infinite() && neg.is_sign_negative());

-            assert!(
-                rendered.data["special_float_mixed_nested"][0][1]
-                    .as_float()
-                    .expect("nested neg")
-                    .is_sign_negative()
-            );
+            let neg = rendered.data["special_float_mixed_nested"][0][1]
+                .as_float()
+                .expect("nested neg");
+            assert!(neg.is_infinite() && neg.is_sign_negative());

Also applies to: 1949-1954, 2964-2969, 2982-2987

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/toml-tstring-rs/src/lib.rs` around lines 1926 - 1931, Tighten the
negative-infinity test assertions by requiring both a negative sign and
infinity: replace the lone is_sign_negative() check on the float returned by
special_floats[..].as_float().expect("float") with a combined assertion that the
value is_sign_negative() && is_infinite(), e.g. assert!(value.is_sign_negative()
&& value.is_infinite()); apply the same change in the other occurrences that use
special_floats[..].as_float().expect("float") (the blocks around the other
reported assertion sites).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@rust/toml-tstring-rs/src/lib.rs`:
- Around line 1926-1931: Tighten the negative-infinity test assertions by
requiring both a negative sign and infinity: replace the lone is_sign_negative()
check on the float returned by special_floats[..].as_float().expect("float")
with a combined assertion that the value is_sign_negative() && is_infinite(),
e.g. assert!(value.is_sign_negative() && value.is_infinite()); apply the same
change in the other occurrences that use
special_floats[..].as_float().expect("float") (the blocks around the other
reported assertion sites).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 792f4d24-b077-4f57-825c-a84b5c8d4645

📥 Commits

Reviewing files that changed from the base of the PR and between d1d027d and 7a033cb.

📒 Files selected for processing (5)
  • rust/json-tstring-rs/src/lib.rs
  • rust/json-tstring-rs/tests/parser.rs
  • rust/python-bindings/src/lib.rs
  • rust/toml-tstring-rs/src/lib.rs
  • rust/toml-tstring-rs/tests/parser.rs

@koxudaxi
koxudaxi merged commit 05fa824 into main Mar 18, 2026
12 checks passed
@koxudaxi
koxudaxi deleted the add-yaml-template-validation-api branch March 18, 2026 17:55
@github-actions github-actions Bot added breaking-change-analyzed PR has been analyzed for breaking changes breaking-change PR contains breaking changes labels Mar 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: Breaking changes detected

Reasoning: The PR routes all Python bindings (JSON, TOML, YAML) through new validated parsing paths. For YAML specifically, new validation rejects plain scalars that combine whitespace-containing text with interpolations. This is a breaking change because previously valid YAML templates like t"value: fdsa fff fds{1}" now raise TemplateParseError. JSON and TOML validated paths currently pass through without additional validation, so no breaking change for those formats.

Content for Release Notes

Template String Parsing Changes

  • YAML plain scalars with whitespace and interpolations now rejected - YAML templates containing plain scalars that mix whitespace-containing text with interpolations now fail validation at parse time with the error "Quote YAML plain scalars that mix whitespace and interpolations." This affects all YAML template parsing through Python bindings and Rust's format_template*/check_template* functions. To fix, wrap such scalar values in quotes. (Add YAML template validation API for plain scalar interpolations #25)

    # Previously accepted, now rejected:
    key: hello world {var}
    replicas: fdsa fff fds{count}
    
    # Still valid (no whitespace in text portion):
    key: item-{var}
    key: prefix{var}suffix
    
    # Fixed (use quotes):
    key: "hello world {var}"

This analysis was performed by Claude Code Action

@github-actions

Copy link
Copy Markdown
Contributor

Released in 0.2.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change PR contains breaking changes breaking-change-analyzed PR has been analyzed for breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant