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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions rust/json-tstring-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,11 +600,36 @@ pub fn parse_template(template: &TemplateInput) -> BackendResult<JsonDocumentNod
parse_template_with_profile(template, JsonProfile::default())
}

pub fn parse_validated_template_with_profile(
template: &TemplateInput,
profile: JsonProfile,
) -> BackendResult<JsonDocumentNode> {
// JSON does not add format-specific post-parse validation yet. Keep the
// validated entry point aligned with the other backends so callers can rely
// on one API shape as backend-specific validation rules are introduced.
parse_template_with_profile(template, profile)
}

pub fn parse_validated_template(template: &TemplateInput) -> BackendResult<JsonDocumentNode> {
parse_validated_template_with_profile(template, JsonProfile::default())
}

pub fn validate_template_with_profile(
template: &TemplateInput,
profile: JsonProfile,
) -> BackendResult<()> {
parse_validated_template_with_profile(template, profile).map(|_| ())
}

pub fn validate_template(template: &TemplateInput) -> BackendResult<()> {
validate_template_with_profile(template, JsonProfile::default())
}

pub fn check_template_with_profile(
template: &TemplateInput,
profile: JsonProfile,
) -> BackendResult<()> {
parse_template_with_profile(template, profile).map(|_| ())
validate_template_with_profile(template, profile)
}

pub fn check_template(template: &TemplateInput) -> BackendResult<()> {
Expand All @@ -615,7 +640,7 @@ pub fn format_template_with_profile(
template: &TemplateInput,
profile: JsonProfile,
) -> BackendResult<String> {
let document = parse_template_with_profile(template, profile)?;
let document = parse_validated_template_with_profile(template, profile)?;
format_json_value(template, &document.value)
}

Expand Down Expand Up @@ -774,9 +799,9 @@ fn normalize_number(number: &serde_json::Number) -> BackendResult<NormalizedValu

#[cfg(test)]
mod tests {
use super::{parse_template, JsonKeyValue, JsonStringPart, JsonValueNode};
use super::{JsonKeyValue, JsonStringPart, JsonValueNode, parse_template};
use pyo3::prelude::*;
use serde_json::{json, Map, Number, Value};
use serde_json::{Map, Number, Value, json};
use tstring_pyo3_bindings::{extract_template, json::render_document};
use tstring_syntax::{BackendError, BackendResult, ErrorKind};

Expand Down
13 changes: 13 additions & 0 deletions rust/json-tstring-rs/tests/parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use tstring_json::{
JsonKeyValue, JsonStringPart, JsonValueNode, check_template, format_template, parse_template,
parse_validated_template, validate_template,
};
use tstring_syntax::{TemplateInput, TemplateInterpolation, TemplateSegment};

Expand Down Expand Up @@ -68,6 +69,18 @@ fn checks_valid_json_templates() {
check_template(&template).expect("expected check success");
}

#[test]
fn validates_json_templates_with_supported_interpolations() {
let template = TemplateInput::from_segments(vec![
TemplateSegment::StaticText("{\"name\": ".to_owned()),
interpolation(0, "name"),
TemplateSegment::StaticText(", \"active\": true}".to_owned()),
]);

validate_template(&template).expect("expected validate success");
parse_validated_template(&template).expect("expected validated parse success");
}

#[test]
fn formats_json_templates_with_raw_interpolations() {
let template = TemplateInput::from_segments(vec![
Expand Down
6 changes: 3 additions & 3 deletions rust/python-bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ fn parse_json_template(
) -> PyResult<Arc<JsonDocumentNode>> {
json_parse_cache()
.get_or_try_insert_with(&template_cache_key(template, profile.as_str()), || {
tstring_json::parse_template_with_profile(template.input(), profile)
tstring_json::parse_validated_template_with_profile(template.input(), profile)
})
.map_err(backend_error_to_py)
}
Expand All @@ -171,7 +171,7 @@ fn parse_toml_template(
) -> PyResult<Arc<TomlDocumentNode>> {
toml_parse_cache()
.get_or_try_insert_with(&template_cache_key(template, profile.as_str()), || {
tstring_toml::parse_template_with_profile(template.input(), profile)
tstring_toml::parse_validated_template_with_profile(template.input(), profile)
})
.map_err(backend_error_to_py)
}
Expand All @@ -182,7 +182,7 @@ fn parse_yaml_template(
) -> PyResult<Arc<YamlStreamNode>> {
yaml_parse_cache()
.get_or_try_insert_with(&template_cache_key(template, profile.as_str()), || {
tstring_yaml::parse_template_with_profile(template.input(), profile)
tstring_yaml::parse_validated_template_with_profile(template.input(), profile)
})
.map_err(backend_error_to_py)
}
Expand Down
158 changes: 104 additions & 54 deletions rust/toml-tstring-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1110,11 +1110,36 @@ pub fn parse_template(template: &TemplateInput) -> BackendResult<TomlDocumentNod
parse_template_with_profile(template, TomlProfile::default())
}

pub fn parse_validated_template_with_profile(
template: &TemplateInput,
profile: TomlProfile,
) -> BackendResult<TomlDocumentNode> {
// TOML does not add format-specific post-parse validation yet. Keep the
// validated entry point aligned with the other backends so callers can rely
// on one API shape as backend-specific validation rules are introduced.
parse_template_with_profile(template, profile)
}

pub fn parse_validated_template(template: &TemplateInput) -> BackendResult<TomlDocumentNode> {
parse_validated_template_with_profile(template, TomlProfile::default())
}

pub fn validate_template_with_profile(
template: &TemplateInput,
profile: TomlProfile,
) -> BackendResult<()> {
parse_validated_template_with_profile(template, profile).map(|_| ())
}

pub fn validate_template(template: &TemplateInput) -> BackendResult<()> {
validate_template_with_profile(template, TomlProfile::default())
}

pub fn check_template_with_profile(
template: &TemplateInput,
profile: TomlProfile,
) -> BackendResult<()> {
parse_template_with_profile(template, profile).map(|_| ())
validate_template_with_profile(template, profile)
}

pub fn check_template(template: &TemplateInput) -> BackendResult<()> {
Expand All @@ -1125,7 +1150,7 @@ pub fn format_template_with_profile(
template: &TemplateInput,
profile: TomlProfile,
) -> BackendResult<String> {
let document = parse_template_with_profile(template, profile)?;
let document = parse_validated_template_with_profile(template, profile)?;
format_toml_document(template, &document)
}

Expand Down Expand Up @@ -1390,7 +1415,7 @@ fn normalize_time(value: toml::value::Time) -> NormalizedTime {

#[cfg(test)]
mod tests {
use super::{parse_template, TomlKeySegmentValue, TomlStatementNode, TomlValueNode};
use super::{TomlKeySegmentValue, TomlStatementNode, TomlValueNode, parse_template};
use pyo3::prelude::*;
use tstring_pyo3_bindings::{extract_template, toml::render_document};
use tstring_syntax::{BackendError, BackendResult, ErrorKind};
Expand Down Expand Up @@ -1901,10 +1926,12 @@ mod tests {
);
let special_floats = table["special_float_array"].as_array().expect("array");
assert!(special_floats[0].as_float().expect("float").is_infinite());
assert!(special_floats[1]
.as_float()
.expect("float")
.is_sign_negative());
assert!(
special_floats[1]
.as_float()
.expect("float")
.is_sign_negative()
);
assert!(special_floats[2].as_float().expect("float").is_nan());
assert_eq!(
table["special_float_nested_arrays"]
Expand All @@ -1916,33 +1943,43 @@ mod tests {
let special_float_deeper_arrays = table["special_float_deeper_arrays"]
.as_array()
.expect("array");
assert!(special_float_deeper_arrays[0][0][0]
.as_float()
.expect("float")
.is_infinite());
assert!(special_float_deeper_arrays[1][0][0]
.as_float()
.expect("float")
.is_sign_negative());
assert!(special_float_deeper_arrays[2][0][0]
.as_float()
.expect("float")
.is_nan());
assert!(
special_float_deeper_arrays[0][0][0]
.as_float()
.expect("float")
.is_infinite()
);
assert!(
special_float_deeper_arrays[1][0][0]
.as_float()
.expect("float")
.is_sign_negative()
);
assert!(
special_float_deeper_arrays[2][0][0]
.as_float()
.expect("float")
.is_nan()
);
assert_eq!(
table["upper_exp_nested_mixed"]
.as_array()
.expect("array")
.len(),
2
);
assert!(table["special_float_inline_table"]["pos"]
.as_float()
.expect("float")
.is_infinite());
assert!(table["special_float_inline_table"]["nan"]
.as_float()
.expect("float")
.is_nan());
assert!(
table["special_float_inline_table"]["pos"]
.as_float()
.expect("float")
.is_infinite()
);
assert!(
table["special_float_inline_table"]["nan"]
.as_float()
.expect("float")
.is_nan()
);
assert_eq!(
table["special_float_mixed_nested"]
.as_array()
Expand Down Expand Up @@ -2026,9 +2063,10 @@ mod tests {
Err(err) => err,
};
assert_eq!(err.kind, ErrorKind::Parse);
assert!(err
.message
.contains("single-line basic strings cannot contain newlines"));
assert!(
err.message
.contains("single-line basic strings cannot contain newlines")
);
});
}

Expand Down Expand Up @@ -2920,30 +2958,42 @@ mod tests {
rendered.text,
"special_float_inline_table = { pos = +inf, neg = -inf, nan = nan }\nspecial_float_mixed_nested = [[+inf, -inf], [nan]]"
);
assert!(rendered.data["special_float_inline_table"]["pos"]
.as_float()
.expect("pos float")
.is_infinite());
assert!(rendered.data["special_float_inline_table"]["neg"]
.as_float()
.expect("neg float")
.is_sign_negative());
assert!(rendered.data["special_float_inline_table"]["nan"]
.as_float()
.expect("nan float")
.is_nan());
assert!(rendered.data["special_float_mixed_nested"][0][0]
.as_float()
.expect("nested pos")
.is_infinite());
assert!(rendered.data["special_float_mixed_nested"][0][1]
.as_float()
.expect("nested neg")
.is_sign_negative());
assert!(rendered.data["special_float_mixed_nested"][1][0]
.as_float()
.expect("nested nan")
.is_nan());
assert!(
rendered.data["special_float_inline_table"]["pos"]
.as_float()
.expect("pos float")
.is_infinite()
);
assert!(
rendered.data["special_float_inline_table"]["neg"]
.as_float()
.expect("neg float")
.is_sign_negative()
);
assert!(
rendered.data["special_float_inline_table"]["nan"]
.as_float()
.expect("nan float")
.is_nan()
);
assert!(
rendered.data["special_float_mixed_nested"][0][0]
.as_float()
.expect("nested pos")
.is_infinite()
);
assert!(
rendered.data["special_float_mixed_nested"][0][1]
.as_float()
.expect("nested neg")
.is_sign_negative()
);
assert!(
rendered.data["special_float_mixed_nested"][1][0]
.as_float()
.expect("nested nan")
.is_nan()
);
});
}

Expand Down
21 changes: 20 additions & 1 deletion rust/toml-tstring-rs/tests/parser.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use tstring_syntax::{TemplateInput, TemplateInterpolation, TemplateSegment};
use tstring_toml::{
check_template, format_template, parse_template, TomlStatementNode, TomlValueNode,
TomlStatementNode, TomlValueNode, check_template, format_template, parse_template,
parse_validated_template, validate_template,
};

fn interpolation(index: usize, expression: &str) -> TemplateSegment {
Expand Down Expand Up @@ -64,6 +65,24 @@ fn checks_valid_toml_templates() {
check_template(&template).expect("expected check success");
}

#[test]
fn validates_toml_templates_with_supported_interpolations() {
let template = TemplateInput::from_segments(vec![
TemplateSegment::StaticText("title = ".to_owned()),
TemplateSegment::Interpolation(TemplateInterpolation {
expression: "title".to_owned(),
conversion: None,
format_spec: String::new(),
interpolation_index: 0,
raw_source: Some("{title}".to_owned()),
}),
TemplateSegment::StaticText("\n".to_owned()),
]);

validate_template(&template).expect("expected validate success");
parse_validated_template(&template).expect("expected validated parse success");
}

#[test]
fn formats_toml_templates_with_raw_interpolations() {
let template = TemplateInput::from_segments(vec![
Expand Down
Loading
Loading