tstring-bindings 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/Cargo.lock +8 -8
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/Cargo.toml +1 -1
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/PKG-INFO +1 -1
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/json-tstring-rs/Cargo.toml +1 -1
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/json-tstring-rs/src/lib.rs +100 -2
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/json-tstring-rs/tests/parser.rs +31 -3
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/pyproject.toml +1 -1
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python/tstring_bindings/__init__.py +17 -13
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python/tstring_bindings/__init__.pyi +11 -7
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python/tstring_bindings/tstring_bindings.pyi +13 -10
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python-bindings/Cargo.toml +5 -5
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python-bindings/src/lib.rs +1 -1
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/toml-tstring-rs/Cargo.toml +1 -1
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/toml-tstring-rs/src/lib.rs +108 -4
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/toml-tstring-rs/tests/parser.rs +40 -3
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-core-rs/src/lib.rs +22 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-pyo3-bindings/Cargo.toml +4 -4
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-pyo3-bindings/benches/render_paths.rs +29 -5
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/yaml-tstring-rs/Cargo.toml +1 -1
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/yaml-tstring-rs/src/lib.rs +172 -3
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/yaml-tstring-rs/tests/parser.rs +62 -2
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/README.md +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/json-tstring-rs/README.md +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/json-tstring-rs/tests/conformance.rs +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python/tstring_bindings/_profiles.py +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python/tstring_bindings/_types.py +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python/tstring_bindings/py.typed +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python-bindings/README.md +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/toml-tstring-rs/README.md +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/toml-tstring-rs/tests/conformance.rs +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-core-rs/Cargo.toml +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-core-rs/README.md +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-pyo3-bindings/src/json.rs +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-pyo3-bindings/src/lib.rs +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-pyo3-bindings/src/toml.rs +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-pyo3-bindings/src/yaml.rs +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/yaml-tstring-rs/README.md +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/yaml-tstring-rs/test-support/renderer_layout.rs +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/yaml-tstring-rs/tests/conformance.rs +0 -0
- {tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/yaml-tstring-rs/tests/normalized.rs +0 -0
|
@@ -725,7 +725,7 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
|
|
725
725
|
|
|
726
726
|
[[package]]
|
|
727
727
|
name = "tstring-backend-e2e-tests"
|
|
728
|
-
version = "0.2.
|
|
728
|
+
version = "0.2.2"
|
|
729
729
|
dependencies = [
|
|
730
730
|
"tstring-json",
|
|
731
731
|
"tstring-syntax",
|
|
@@ -735,7 +735,7 @@ dependencies = [
|
|
|
735
735
|
|
|
736
736
|
[[package]]
|
|
737
737
|
name = "tstring-bindings"
|
|
738
|
-
version = "0.2.
|
|
738
|
+
version = "0.2.2"
|
|
739
739
|
dependencies = [
|
|
740
740
|
"pyo3",
|
|
741
741
|
"pythonize",
|
|
@@ -751,7 +751,7 @@ dependencies = [
|
|
|
751
751
|
|
|
752
752
|
[[package]]
|
|
753
753
|
name = "tstring-json"
|
|
754
|
-
version = "0.2.
|
|
754
|
+
version = "0.2.2"
|
|
755
755
|
dependencies = [
|
|
756
756
|
"serde_json",
|
|
757
757
|
"toml",
|
|
@@ -760,7 +760,7 @@ dependencies = [
|
|
|
760
760
|
|
|
761
761
|
[[package]]
|
|
762
762
|
name = "tstring-pyo3-bindings"
|
|
763
|
-
version = "0.2.
|
|
763
|
+
version = "0.2.2"
|
|
764
764
|
dependencies = [
|
|
765
765
|
"criterion",
|
|
766
766
|
"pyo3",
|
|
@@ -776,14 +776,14 @@ dependencies = [
|
|
|
776
776
|
|
|
777
777
|
[[package]]
|
|
778
778
|
name = "tstring-syntax"
|
|
779
|
-
version = "0.2.
|
|
779
|
+
version = "0.2.2"
|
|
780
780
|
dependencies = [
|
|
781
781
|
"num-bigint",
|
|
782
782
|
]
|
|
783
783
|
|
|
784
784
|
[[package]]
|
|
785
785
|
name = "tstring-toml"
|
|
786
|
-
version = "0.2.
|
|
786
|
+
version = "0.2.2"
|
|
787
787
|
dependencies = [
|
|
788
788
|
"serde_json",
|
|
789
789
|
"toml",
|
|
@@ -792,7 +792,7 @@ dependencies = [
|
|
|
792
792
|
|
|
793
793
|
[[package]]
|
|
794
794
|
name = "tstring-yaml"
|
|
795
|
-
version = "0.2.
|
|
795
|
+
version = "0.2.2"
|
|
796
796
|
dependencies = [
|
|
797
797
|
"saphyr",
|
|
798
798
|
"saphyr-parser",
|
|
@@ -803,7 +803,7 @@ dependencies = [
|
|
|
803
803
|
|
|
804
804
|
[[package]]
|
|
805
805
|
name = "tstring-yaml-pyo3-tests"
|
|
806
|
-
version = "0.2.
|
|
806
|
+
version = "0.2.2"
|
|
807
807
|
dependencies = [
|
|
808
808
|
"pyo3",
|
|
809
809
|
"saphyr",
|
|
@@ -9,7 +9,7 @@ homepage = "https://github.com/koxudaxi/tstring-structured-data"
|
|
|
9
9
|
license = "MIT"
|
|
10
10
|
repository = "https://github.com/koxudaxi/tstring-structured-data"
|
|
11
11
|
rust-version = "1.94.0"
|
|
12
|
-
version = "0.2.
|
|
12
|
+
version = "0.2.2"
|
|
13
13
|
|
|
14
14
|
[workspace.dependencies]
|
|
15
15
|
num-bigint = "0.4.6"
|
|
@@ -18,7 +18,7 @@ test = false
|
|
|
18
18
|
|
|
19
19
|
[dependencies]
|
|
20
20
|
serde_json = { workspace = true }
|
|
21
|
-
tstring-syntax = { version = "0.2.
|
|
21
|
+
tstring-syntax = { version = "0.2.2", path = "../tstring-core-rs" }
|
|
22
22
|
|
|
23
23
|
[dev-dependencies]
|
|
24
24
|
toml = { workspace = true }
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
use serde_json::Value;
|
|
2
2
|
use std::str::FromStr;
|
|
3
3
|
use tstring_syntax::{
|
|
4
|
-
BackendError, BackendResult, NormalizedDocument, NormalizedFloat,
|
|
5
|
-
NormalizedStream, NormalizedValue, SourcePosition, SourceSpan, StreamItem,
|
|
4
|
+
BackendError, BackendResult, InterpolationTypeRequirement, NormalizedDocument, NormalizedFloat,
|
|
5
|
+
NormalizedKey, NormalizedStream, NormalizedValue, SourcePosition, SourceSpan, StreamItem,
|
|
6
|
+
TemplateInput,
|
|
6
7
|
};
|
|
7
8
|
|
|
9
|
+
const JSON_VALUE_PYTHON_TYPE: &str =
|
|
10
|
+
"str | int | float | bool | None | dict[str, object] | list[object]";
|
|
11
|
+
const STRING_PYTHON_TYPE: &str = "str";
|
|
12
|
+
|
|
8
13
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
9
14
|
pub enum JsonProfile {
|
|
10
15
|
Rfc8259,
|
|
@@ -636,6 +641,99 @@ pub fn check_template(template: &TemplateInput) -> BackendResult<()> {
|
|
|
636
641
|
check_template_with_profile(template, JsonProfile::default())
|
|
637
642
|
}
|
|
638
643
|
|
|
644
|
+
pub fn interpolation_type_requirements_with_profile(
|
|
645
|
+
template: &TemplateInput,
|
|
646
|
+
profile: JsonProfile,
|
|
647
|
+
) -> BackendResult<Vec<InterpolationTypeRequirement>> {
|
|
648
|
+
let document = parse_validated_template_with_profile(template, profile)?;
|
|
649
|
+
let mut requirements = Vec::new();
|
|
650
|
+
collect_json_value_type_requirements(&document.value, &mut requirements);
|
|
651
|
+
requirements.sort_by_key(|requirement| requirement.interpolation_index);
|
|
652
|
+
Ok(requirements)
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
pub fn interpolation_type_requirements(
|
|
656
|
+
template: &TemplateInput,
|
|
657
|
+
) -> BackendResult<Vec<InterpolationTypeRequirement>> {
|
|
658
|
+
interpolation_type_requirements_with_profile(template, JsonProfile::default())
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
fn collect_json_value_type_requirements(
|
|
662
|
+
value: &JsonValueNode,
|
|
663
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
664
|
+
) {
|
|
665
|
+
match value {
|
|
666
|
+
JsonValueNode::String(node) => {
|
|
667
|
+
collect_json_string_type_requirements(node, requirements);
|
|
668
|
+
}
|
|
669
|
+
JsonValueNode::Literal(_) => {}
|
|
670
|
+
JsonValueNode::Interpolation(node) => {
|
|
671
|
+
requirements.push(json_type_requirement(node));
|
|
672
|
+
}
|
|
673
|
+
JsonValueNode::Object(node) => {
|
|
674
|
+
for member in &node.members {
|
|
675
|
+
collect_json_key_type_requirements(&member.key, requirements);
|
|
676
|
+
collect_json_value_type_requirements(&member.value, requirements);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
JsonValueNode::Array(node) => {
|
|
680
|
+
for item in &node.items {
|
|
681
|
+
collect_json_value_type_requirements(item, requirements);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
fn collect_json_key_type_requirements(
|
|
688
|
+
key: &JsonKeyNode,
|
|
689
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
690
|
+
) {
|
|
691
|
+
match &key.value {
|
|
692
|
+
JsonKeyValue::String(node) => {
|
|
693
|
+
collect_json_string_type_requirements(node, requirements);
|
|
694
|
+
}
|
|
695
|
+
JsonKeyValue::Interpolation(node) => {
|
|
696
|
+
requirements.push(json_type_requirement(node));
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
fn collect_json_string_type_requirements(
|
|
702
|
+
string: &JsonStringNode,
|
|
703
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
704
|
+
) {
|
|
705
|
+
for chunk in &string.chunks {
|
|
706
|
+
if let JsonStringPart::Interpolation(node) = chunk {
|
|
707
|
+
requirements.push(json_type_requirement(node));
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
fn json_type_requirement(node: &JsonInterpolationNode) -> InterpolationTypeRequirement {
|
|
713
|
+
match node.role.as_str() {
|
|
714
|
+
"value" => InterpolationTypeRequirement::new(
|
|
715
|
+
node.interpolation_index,
|
|
716
|
+
JSON_VALUE_PYTHON_TYPE,
|
|
717
|
+
"json value",
|
|
718
|
+
),
|
|
719
|
+
"key" => InterpolationTypeRequirement::new(
|
|
720
|
+
node.interpolation_index,
|
|
721
|
+
STRING_PYTHON_TYPE,
|
|
722
|
+
"json object key",
|
|
723
|
+
),
|
|
724
|
+
"string_fragment" => InterpolationTypeRequirement::new(
|
|
725
|
+
node.interpolation_index,
|
|
726
|
+
STRING_PYTHON_TYPE,
|
|
727
|
+
"json string fragment",
|
|
728
|
+
),
|
|
729
|
+
_ => InterpolationTypeRequirement::new(
|
|
730
|
+
node.interpolation_index,
|
|
731
|
+
JSON_VALUE_PYTHON_TYPE,
|
|
732
|
+
"json interpolation",
|
|
733
|
+
),
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
639
737
|
pub fn format_template_with_profile(
|
|
640
738
|
template: &TemplateInput,
|
|
641
739
|
profile: JsonProfile,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
use tstring_json::{
|
|
2
|
-
JsonKeyValue, JsonStringPart, JsonValueNode, check_template, format_template,
|
|
3
|
-
parse_validated_template, validate_template,
|
|
2
|
+
JsonKeyValue, JsonStringPart, JsonValueNode, check_template, format_template,
|
|
3
|
+
interpolation_type_requirements, parse_template, parse_validated_template, validate_template,
|
|
4
|
+
};
|
|
5
|
+
use tstring_syntax::{
|
|
6
|
+
InterpolationTypeRequirement, TemplateInput, TemplateInterpolation, TemplateSegment,
|
|
4
7
|
};
|
|
5
|
-
use tstring_syntax::{TemplateInput, TemplateInterpolation, TemplateSegment};
|
|
6
8
|
|
|
7
9
|
fn interpolation(index: usize, expression: &str) -> TemplateSegment {
|
|
8
10
|
TemplateSegment::Interpolation(TemplateInterpolation {
|
|
@@ -69,6 +71,32 @@ fn checks_valid_json_templates() {
|
|
|
69
71
|
check_template(&template).expect("expected check success");
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
#[test]
|
|
75
|
+
fn reports_contextual_interpolation_type_requirements() {
|
|
76
|
+
let template = TemplateInput::from_segments(vec![
|
|
77
|
+
TemplateSegment::StaticText("{".to_owned()),
|
|
78
|
+
interpolation(0, "key"),
|
|
79
|
+
TemplateSegment::StaticText(": ".to_owned()),
|
|
80
|
+
interpolation(1, "value"),
|
|
81
|
+
TemplateSegment::StaticText(", \"label\": \"".to_owned()),
|
|
82
|
+
interpolation(2, "label"),
|
|
83
|
+
TemplateSegment::StaticText("\"}".to_owned()),
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
assert_eq!(
|
|
87
|
+
interpolation_type_requirements(&template).expect("expected type requirements"),
|
|
88
|
+
vec![
|
|
89
|
+
InterpolationTypeRequirement::new(0, "str", "json object key"),
|
|
90
|
+
InterpolationTypeRequirement::new(
|
|
91
|
+
1,
|
|
92
|
+
"str | int | float | bool | None | dict[str, object] | list[object]",
|
|
93
|
+
"json value"
|
|
94
|
+
),
|
|
95
|
+
InterpolationTypeRequirement::new(2, "str", "json string fragment"),
|
|
96
|
+
]
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
72
100
|
#[test]
|
|
73
101
|
fn validates_json_templates_with_supported_interpolations() {
|
|
74
102
|
let template = TemplateInput::from_segments(vec![
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from string.templatelib import Template
|
|
4
|
-
from typing import Protocol, cast
|
|
4
|
+
from typing import Annotated, Protocol, cast
|
|
5
5
|
|
|
6
6
|
from . import tstring_bindings as _bindings
|
|
7
7
|
from ._profiles import (
|
|
@@ -14,29 +14,33 @@ from ._profiles import (
|
|
|
14
14
|
)
|
|
15
15
|
from ._types import JsonValue, TomlValue, YamlValue
|
|
16
16
|
|
|
17
|
+
type JsonTemplate = Annotated[Template, "json"]
|
|
18
|
+
type TomlTemplate = Annotated[Template, "toml"]
|
|
19
|
+
type YamlTemplate = Annotated[Template, "yaml"]
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
class _RenderJson(Protocol):
|
|
19
|
-
def __call__(self, template:
|
|
23
|
+
def __call__(self, template: JsonTemplate, profile: JsonProfile) -> JsonValue: ...
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
class _RenderJsonText(Protocol):
|
|
23
|
-
def __call__(self, template:
|
|
27
|
+
def __call__(self, template: JsonTemplate, profile: JsonProfile) -> str: ...
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
class _RenderToml(Protocol):
|
|
27
|
-
def __call__(self, template:
|
|
31
|
+
def __call__(self, template: TomlTemplate, profile: TomlProfile) -> TomlValue: ...
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
class _RenderTomlText(Protocol):
|
|
31
|
-
def __call__(self, template:
|
|
35
|
+
def __call__(self, template: TomlTemplate, profile: TomlProfile) -> str: ...
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
class _RenderYaml(Protocol):
|
|
35
|
-
def __call__(self, template:
|
|
39
|
+
def __call__(self, template: YamlTemplate, profile: YamlProfile) -> YamlValue: ...
|
|
36
40
|
|
|
37
41
|
|
|
38
42
|
class _RenderYamlText(Protocol):
|
|
39
|
-
def __call__(self, template:
|
|
43
|
+
def __call__(self, template: YamlTemplate, profile: YamlProfile) -> str: ...
|
|
40
44
|
|
|
41
45
|
|
|
42
46
|
class _BindingsContract(Protocol):
|
|
@@ -101,37 +105,37 @@ _render_yaml_text = _EXTENSION.render_yaml_text
|
|
|
101
105
|
|
|
102
106
|
|
|
103
107
|
def render_json(
|
|
104
|
-
template:
|
|
108
|
+
template: JsonTemplate, *, profile: JsonProfile | str | None = None
|
|
105
109
|
) -> JsonValue:
|
|
106
110
|
return _render_json(template, resolve_json_profile(profile))
|
|
107
111
|
|
|
108
112
|
|
|
109
113
|
def render_json_text(
|
|
110
|
-
template:
|
|
114
|
+
template: JsonTemplate, *, profile: JsonProfile | str | None = None
|
|
111
115
|
) -> str:
|
|
112
116
|
return _render_json_text(template, resolve_json_profile(profile))
|
|
113
117
|
|
|
114
118
|
|
|
115
119
|
def render_toml(
|
|
116
|
-
template:
|
|
120
|
+
template: TomlTemplate, *, profile: TomlProfile | str | None = None
|
|
117
121
|
) -> TomlValue:
|
|
118
122
|
return _render_toml(template, resolve_toml_profile(profile))
|
|
119
123
|
|
|
120
124
|
|
|
121
125
|
def render_toml_text(
|
|
122
|
-
template:
|
|
126
|
+
template: TomlTemplate, *, profile: TomlProfile | str | None = None
|
|
123
127
|
) -> str:
|
|
124
128
|
return _render_toml_text(template, resolve_toml_profile(profile))
|
|
125
129
|
|
|
126
130
|
|
|
127
131
|
def render_yaml(
|
|
128
|
-
template:
|
|
132
|
+
template: YamlTemplate, *, profile: YamlProfile | str | None = None
|
|
129
133
|
) -> YamlValue:
|
|
130
134
|
return _render_yaml(template, resolve_yaml_profile(profile))
|
|
131
135
|
|
|
132
136
|
|
|
133
137
|
def render_yaml_text(
|
|
134
|
-
template:
|
|
138
|
+
template: YamlTemplate, *, profile: YamlProfile | str | None = None
|
|
135
139
|
) -> str:
|
|
136
140
|
return _render_yaml_text(template, resolve_yaml_profile(profile))
|
|
137
141
|
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from string.templatelib import Template
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Annotated, Any
|
|
5
5
|
|
|
6
6
|
from ._profiles import JsonProfile, TomlProfile, YamlProfile
|
|
7
7
|
|
|
8
|
+
type JsonTemplate = Annotated[Template, "json"]
|
|
9
|
+
type TomlTemplate = Annotated[Template, "toml"]
|
|
10
|
+
type YamlTemplate = Annotated[Template, "yaml"]
|
|
11
|
+
|
|
8
12
|
type ExceptionSpan = tuple[tuple[int, int], tuple[int, int]]
|
|
9
13
|
type ExceptionDiagnostic = dict[str, object]
|
|
10
14
|
|
|
@@ -19,20 +23,20 @@ class TemplateSemanticError(TemplateError): ...
|
|
|
19
23
|
class UnrepresentableValueError(TemplateSemanticError): ...
|
|
20
24
|
|
|
21
25
|
def render_json(
|
|
22
|
-
template:
|
|
26
|
+
template: JsonTemplate, *, profile: JsonProfile | str | None = ...
|
|
23
27
|
) -> Any: ...
|
|
24
28
|
def render_json_text(
|
|
25
|
-
template:
|
|
29
|
+
template: JsonTemplate, *, profile: JsonProfile | str | None = ...
|
|
26
30
|
) -> str: ...
|
|
27
31
|
def render_toml(
|
|
28
|
-
template:
|
|
32
|
+
template: TomlTemplate, *, profile: TomlProfile | str | None = ...
|
|
29
33
|
) -> Any: ...
|
|
30
34
|
def render_toml_text(
|
|
31
|
-
template:
|
|
35
|
+
template: TomlTemplate, *, profile: TomlProfile | str | None = ...
|
|
32
36
|
) -> str: ...
|
|
33
37
|
def render_yaml(
|
|
34
|
-
template:
|
|
38
|
+
template: YamlTemplate, *, profile: YamlProfile | str | None = ...
|
|
35
39
|
) -> Any: ...
|
|
36
40
|
def render_yaml_text(
|
|
37
|
-
template:
|
|
41
|
+
template: YamlTemplate, *, profile: YamlProfile | str | None = ...
|
|
38
42
|
) -> str: ...
|
{tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/python/tstring_bindings/tstring_bindings.pyi
RENAMED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from string.templatelib import Template
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Annotated, Any
|
|
5
5
|
|
|
6
6
|
# Non-public extension module retained for internal imports and packaging
|
|
7
7
|
# compatibility. Public callers should use `tstring_bindings`.
|
|
8
8
|
|
|
9
|
+
type JsonTemplate = Annotated[Template, "json"]
|
|
10
|
+
type TomlTemplate = Annotated[Template, "toml"]
|
|
11
|
+
type YamlTemplate = Annotated[Template, "yaml"]
|
|
9
12
|
type ExceptionSpan = tuple[tuple[int, int], tuple[int, int]]
|
|
10
13
|
type ExceptionDiagnostic = dict[str, object]
|
|
11
14
|
|
|
@@ -22,18 +25,18 @@ class UnrepresentableValueError(TemplateSemanticError): ...
|
|
|
22
25
|
__contract_version__: int
|
|
23
26
|
__contract_symbols__: tuple[str, ...]
|
|
24
27
|
|
|
25
|
-
def render_json(template:
|
|
26
|
-
def render_json_text(template:
|
|
28
|
+
def render_json(template: JsonTemplate, profile: str = ...) -> Any: ...
|
|
29
|
+
def render_json_text(template: JsonTemplate, profile: str = ...) -> str: ...
|
|
27
30
|
def _render_json_result_payload(
|
|
28
|
-
template:
|
|
31
|
+
template: JsonTemplate, profile: str = ...
|
|
29
32
|
) -> tuple[str, Any]: ...
|
|
30
|
-
def render_toml(template:
|
|
31
|
-
def render_toml_text(template:
|
|
33
|
+
def render_toml(template: TomlTemplate, profile: str = ...) -> Any: ...
|
|
34
|
+
def render_toml_text(template: TomlTemplate, profile: str = ...) -> str: ...
|
|
32
35
|
def _render_toml_result_payload(
|
|
33
|
-
template:
|
|
36
|
+
template: TomlTemplate, profile: str = ...
|
|
34
37
|
) -> tuple[str, Any]: ...
|
|
35
|
-
def render_yaml(template:
|
|
36
|
-
def render_yaml_text(template:
|
|
38
|
+
def render_yaml(template: YamlTemplate, profile: str = ...) -> Any: ...
|
|
39
|
+
def render_yaml_text(template: YamlTemplate, profile: str = ...) -> str: ...
|
|
37
40
|
def _render_yaml_result_payload(
|
|
38
|
-
template:
|
|
41
|
+
template: YamlTemplate, profile: str = ...
|
|
39
42
|
) -> tuple[str, Any]: ...
|
|
@@ -24,12 +24,12 @@ pyo3 = { workspace = true, features = ["abi3-py314"] }
|
|
|
24
24
|
pythonize = { workspace = true }
|
|
25
25
|
saphyr = { workspace = true }
|
|
26
26
|
serde_json = { workspace = true }
|
|
27
|
-
tstring-json = { version = "0.2.
|
|
28
|
-
tstring-pyo3-bindings = { version = "0.2.
|
|
29
|
-
tstring-syntax = { version = "0.2.
|
|
27
|
+
tstring-json = { version = "0.2.2", path = "../json-tstring-rs" }
|
|
28
|
+
tstring-pyo3-bindings = { version = "0.2.2", path = "../tstring-pyo3-bindings" }
|
|
29
|
+
tstring-syntax = { version = "0.2.2", path = "../tstring-core-rs" }
|
|
30
30
|
toml = { workspace = true }
|
|
31
|
-
tstring-toml = { version = "0.2.
|
|
32
|
-
tstring-yaml = { version = "0.2.
|
|
31
|
+
tstring-toml = { version = "0.2.2", path = "../toml-tstring-rs" }
|
|
32
|
+
tstring-yaml = { version = "0.2.2", path = "../yaml-tstring-rs" }
|
|
33
33
|
|
|
34
34
|
[dev-dependencies]
|
|
35
35
|
pyo3 = { workspace = true, features = ["auto-initialize"] }
|
|
@@ -627,7 +627,7 @@ fn normalized_offset_to_python(py: Python<'_>, offset_minutes: i16) -> PyResult<
|
|
|
627
627
|
|
|
628
628
|
#[pymodule]
|
|
629
629
|
fn tstring_bindings(py: Python<'_>, module: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
630
|
-
module.add("__version__", "0.2.
|
|
630
|
+
module.add("__version__", "0.2.2")?;
|
|
631
631
|
module.add("__contract_version__", CONTRACT_VERSION)?;
|
|
632
632
|
module.add("__contract_symbols__", PyTuple::new(py, CONTRACT_SYMBOLS)?)?;
|
|
633
633
|
module.add("TemplateError", py.get_type::<TemplateError>())?;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
use tstring_syntax::{
|
|
2
|
-
BackendError, BackendResult, NormalizedDate, NormalizedDocument,
|
|
3
|
-
NormalizedFloat, NormalizedKey, NormalizedLocalDateTime,
|
|
4
|
-
NormalizedStream, NormalizedTemporal, NormalizedTime,
|
|
5
|
-
SourceSpan, StreamItem, TemplateInput,
|
|
2
|
+
BackendError, BackendResult, InterpolationTypeRequirement, NormalizedDate, NormalizedDocument,
|
|
3
|
+
NormalizedEntry, NormalizedFloat, NormalizedKey, NormalizedLocalDateTime,
|
|
4
|
+
NormalizedOffsetDateTime, NormalizedStream, NormalizedTemporal, NormalizedTime,
|
|
5
|
+
NormalizedValue, SourcePosition, SourceSpan, StreamItem, TemplateInput,
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
const TOML_VALUE_PYTHON_TYPE: &str = "str | int | float | bool | datetime.date | datetime.time | datetime.datetime | list[object] | dict[str, object]";
|
|
9
|
+
const STRING_PYTHON_TYPE: &str = "str";
|
|
10
|
+
|
|
8
11
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
9
12
|
pub enum TomlProfile {
|
|
10
13
|
V1_0,
|
|
@@ -1146,6 +1149,107 @@ pub fn check_template(template: &TemplateInput) -> BackendResult<()> {
|
|
|
1146
1149
|
check_template_with_profile(template, TomlProfile::default())
|
|
1147
1150
|
}
|
|
1148
1151
|
|
|
1152
|
+
pub fn interpolation_type_requirements_with_profile(
|
|
1153
|
+
template: &TemplateInput,
|
|
1154
|
+
profile: TomlProfile,
|
|
1155
|
+
) -> BackendResult<Vec<InterpolationTypeRequirement>> {
|
|
1156
|
+
let document = parse_validated_template_with_profile(template, profile)?;
|
|
1157
|
+
let mut requirements = Vec::new();
|
|
1158
|
+
collect_toml_document_type_requirements(&document, &mut requirements);
|
|
1159
|
+
requirements.sort_by_key(|requirement| requirement.interpolation_index);
|
|
1160
|
+
Ok(requirements)
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
pub fn interpolation_type_requirements(
|
|
1164
|
+
template: &TemplateInput,
|
|
1165
|
+
) -> BackendResult<Vec<InterpolationTypeRequirement>> {
|
|
1166
|
+
interpolation_type_requirements_with_profile(template, TomlProfile::default())
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
fn collect_toml_document_type_requirements(
|
|
1170
|
+
document: &TomlDocumentNode,
|
|
1171
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
1172
|
+
) {
|
|
1173
|
+
for statement in &document.statements {
|
|
1174
|
+
match statement {
|
|
1175
|
+
TomlStatementNode::Assignment(node) => {
|
|
1176
|
+
collect_toml_key_path_type_requirements(&node.key_path, requirements);
|
|
1177
|
+
collect_toml_value_type_requirements(&node.value, requirements);
|
|
1178
|
+
}
|
|
1179
|
+
TomlStatementNode::TableHeader(node) => {
|
|
1180
|
+
collect_toml_key_path_type_requirements(&node.key_path, requirements);
|
|
1181
|
+
}
|
|
1182
|
+
TomlStatementNode::ArrayTableHeader(node) => {
|
|
1183
|
+
collect_toml_key_path_type_requirements(&node.key_path, requirements);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
fn collect_toml_key_path_type_requirements(
|
|
1190
|
+
key_path: &TomlKeyPathNode,
|
|
1191
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
1192
|
+
) {
|
|
1193
|
+
for segment in &key_path.segments {
|
|
1194
|
+
match &segment.value {
|
|
1195
|
+
TomlKeySegmentValue::Bare(_) => {}
|
|
1196
|
+
TomlKeySegmentValue::String(node) => {
|
|
1197
|
+
collect_toml_string_type_requirements(node, requirements);
|
|
1198
|
+
}
|
|
1199
|
+
TomlKeySegmentValue::Interpolation(node) => {
|
|
1200
|
+
requirements.push(InterpolationTypeRequirement::new(
|
|
1201
|
+
node.interpolation_index,
|
|
1202
|
+
STRING_PYTHON_TYPE,
|
|
1203
|
+
"toml key",
|
|
1204
|
+
));
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
fn collect_toml_value_type_requirements(
|
|
1211
|
+
value: &TomlValueNode,
|
|
1212
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
1213
|
+
) {
|
|
1214
|
+
match value {
|
|
1215
|
+
TomlValueNode::String(node) => collect_toml_string_type_requirements(node, requirements),
|
|
1216
|
+
TomlValueNode::Literal(_) => {}
|
|
1217
|
+
TomlValueNode::Interpolation(node) => {
|
|
1218
|
+
requirements.push(InterpolationTypeRequirement::new(
|
|
1219
|
+
node.interpolation_index,
|
|
1220
|
+
TOML_VALUE_PYTHON_TYPE,
|
|
1221
|
+
"toml value",
|
|
1222
|
+
));
|
|
1223
|
+
}
|
|
1224
|
+
TomlValueNode::Array(node) => {
|
|
1225
|
+
for item in &node.items {
|
|
1226
|
+
collect_toml_value_type_requirements(item, requirements);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
TomlValueNode::InlineTable(node) => {
|
|
1230
|
+
for entry in &node.entries {
|
|
1231
|
+
collect_toml_key_path_type_requirements(&entry.key_path, requirements);
|
|
1232
|
+
collect_toml_value_type_requirements(&entry.value, requirements);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
fn collect_toml_string_type_requirements(
|
|
1239
|
+
string: &TomlStringNode,
|
|
1240
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
1241
|
+
) {
|
|
1242
|
+
for chunk in &string.chunks {
|
|
1243
|
+
if let TomlStringPart::Interpolation(node) = chunk {
|
|
1244
|
+
requirements.push(InterpolationTypeRequirement::new(
|
|
1245
|
+
node.interpolation_index,
|
|
1246
|
+
STRING_PYTHON_TYPE,
|
|
1247
|
+
"toml string fragment",
|
|
1248
|
+
));
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1149
1253
|
pub fn format_template_with_profile(
|
|
1150
1254
|
template: &TemplateInput,
|
|
1151
1255
|
profile: TomlProfile,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
use tstring_syntax::{
|
|
1
|
+
use tstring_syntax::{
|
|
2
|
+
InterpolationTypeRequirement, TemplateInput, TemplateInterpolation, TemplateSegment,
|
|
3
|
+
};
|
|
2
4
|
use tstring_toml::{
|
|
3
|
-
TomlStatementNode, TomlValueNode, check_template, format_template,
|
|
4
|
-
parse_validated_template, validate_template,
|
|
5
|
+
TomlStatementNode, TomlValueNode, check_template, format_template,
|
|
6
|
+
interpolation_type_requirements, parse_template, parse_validated_template, validate_template,
|
|
5
7
|
};
|
|
6
8
|
|
|
7
9
|
fn interpolation(index: usize, expression: &str) -> TemplateSegment {
|
|
@@ -83,6 +85,41 @@ fn validates_toml_templates_with_supported_interpolations() {
|
|
|
83
85
|
parse_validated_template(&template).expect("expected validated parse success");
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
#[test]
|
|
89
|
+
fn reports_contextual_interpolation_type_requirements() {
|
|
90
|
+
let template = TemplateInput::from_segments(vec![
|
|
91
|
+
interpolation(0, "table"),
|
|
92
|
+
TemplateSegment::StaticText(".title = ".to_owned()),
|
|
93
|
+
interpolation(1, "value"),
|
|
94
|
+
TemplateSegment::StaticText("\nmessage = \"Hello ".to_owned()),
|
|
95
|
+
interpolation(2, "fragment"),
|
|
96
|
+
TemplateSegment::StaticText("\"\nmeta = { ".to_owned()),
|
|
97
|
+
interpolation(3, "inline_key"),
|
|
98
|
+
TemplateSegment::StaticText(" = ".to_owned()),
|
|
99
|
+
interpolation(4, "inline_value"),
|
|
100
|
+
TemplateSegment::StaticText(" }\n".to_owned()),
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
assert_eq!(
|
|
104
|
+
interpolation_type_requirements(&template).expect("expected type requirements"),
|
|
105
|
+
vec![
|
|
106
|
+
InterpolationTypeRequirement::new(0, "str", "toml key"),
|
|
107
|
+
InterpolationTypeRequirement::new(
|
|
108
|
+
1,
|
|
109
|
+
"str | int | float | bool | datetime.date | datetime.time | datetime.datetime | list[object] | dict[str, object]",
|
|
110
|
+
"toml value"
|
|
111
|
+
),
|
|
112
|
+
InterpolationTypeRequirement::new(2, "str", "toml string fragment"),
|
|
113
|
+
InterpolationTypeRequirement::new(3, "str", "toml key"),
|
|
114
|
+
InterpolationTypeRequirement::new(
|
|
115
|
+
4,
|
|
116
|
+
"str | int | float | bool | datetime.date | datetime.time | datetime.datetime | list[object] | dict[str, object]",
|
|
117
|
+
"toml value"
|
|
118
|
+
),
|
|
119
|
+
]
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
86
123
|
#[test]
|
|
87
124
|
fn formats_toml_templates_with_raw_interpolations() {
|
|
88
125
|
let template = TemplateInput::from_segments(vec![
|
|
@@ -176,6 +176,28 @@ impl std::error::Error for BackendError {}
|
|
|
176
176
|
|
|
177
177
|
pub type BackendResult<T> = Result<T, BackendError>;
|
|
178
178
|
|
|
179
|
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
180
|
+
pub struct InterpolationTypeRequirement {
|
|
181
|
+
pub interpolation_index: usize,
|
|
182
|
+
pub expected_python_type: String,
|
|
183
|
+
pub expected_description: String,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
impl InterpolationTypeRequirement {
|
|
187
|
+
#[must_use]
|
|
188
|
+
pub fn new(
|
|
189
|
+
interpolation_index: usize,
|
|
190
|
+
expected_python_type: impl Into<String>,
|
|
191
|
+
expected_description: impl Into<String>,
|
|
192
|
+
) -> Self {
|
|
193
|
+
Self {
|
|
194
|
+
interpolation_index,
|
|
195
|
+
expected_python_type: expected_python_type.into(),
|
|
196
|
+
expected_description: expected_description.into(),
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
179
201
|
#[derive(Clone, Debug, PartialEq)]
|
|
180
202
|
pub struct NormalizedStream {
|
|
181
203
|
pub documents: Vec<NormalizedDocument>,
|
|
@@ -16,10 +16,10 @@ serde_json = { workspace = true }
|
|
|
16
16
|
saphyr = { workspace = true }
|
|
17
17
|
saphyr-parser = { workspace = true }
|
|
18
18
|
toml = { workspace = true }
|
|
19
|
-
tstring-json = { version = "0.2.
|
|
20
|
-
tstring-syntax = { version = "0.2.
|
|
21
|
-
tstring-toml = { version = "0.2.
|
|
22
|
-
tstring-yaml = { version = "0.2.
|
|
19
|
+
tstring-json = { version = "0.2.2", path = "../json-tstring-rs" }
|
|
20
|
+
tstring-syntax = { version = "0.2.2", path = "../tstring-core-rs" }
|
|
21
|
+
tstring-toml = { version = "0.2.2", path = "../toml-tstring-rs" }
|
|
22
|
+
tstring-yaml = { version = "0.2.2", path = "../yaml-tstring-rs" }
|
|
23
23
|
|
|
24
24
|
[dev-dependencies]
|
|
25
25
|
criterion = "0.8.2"
|
{tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/tstring-pyo3-bindings/benches/render_paths.rs
RENAMED
|
@@ -11,7 +11,7 @@ fn benchmark_json_metadata_render(criterion: &mut Criterion) {
|
|
|
11
11
|
let module = PyModule::from_code(
|
|
12
12
|
py,
|
|
13
13
|
pyo3::ffi::c_str!(
|
|
14
|
-
"value=3.14159\nlabel='service'\nitems=[1, 2, 3]\nmeta={'region': 'us-east-1', 'count': 3}\ntemplate=t'{\"format\": {value:.2f}, \"label\": \"{label!s}\", \"items\": {items}, \"meta\": {meta}, \"fragment\": \"pi={value:.2f}\"}'\n"
|
|
14
|
+
"value=3.14159\nlabel='service'\nitems=[1, 2, 3]\nmeta={'region': 'us-east-1', 'count': 3}\ntemplate=t'{{\"format\": {value:.2f}, \"label\": \"{label!s}\", \"items\": {items}, \"meta\": {meta}, \"fragment\": \"pi={value:.2f}\"}}'\n"
|
|
15
15
|
),
|
|
16
16
|
pyo3::ffi::c_str!("bench_json_metadata.py"),
|
|
17
17
|
pyo3::ffi::c_str!("bench_json_metadata"),
|
|
@@ -26,7 +26,13 @@ fn benchmark_json_metadata_render(criterion: &mut Criterion) {
|
|
|
26
26
|
criterion.bench_function("json_metadata_render", |bench| {
|
|
27
27
|
bench.iter(|| {
|
|
28
28
|
Python::with_gil(|py| {
|
|
29
|
-
json_backend::render_document(
|
|
29
|
+
json_backend::render_document(
|
|
30
|
+
py,
|
|
31
|
+
&template,
|
|
32
|
+
tstring_json::JsonProfile::default(),
|
|
33
|
+
node.as_ref(),
|
|
34
|
+
)
|
|
35
|
+
.unwrap();
|
|
30
36
|
});
|
|
31
37
|
});
|
|
32
38
|
});
|
|
@@ -52,7 +58,13 @@ fn benchmark_toml_metadata_render(criterion: &mut Criterion) {
|
|
|
52
58
|
criterion.bench_function("toml_metadata_render", |bench| {
|
|
53
59
|
bench.iter(|| {
|
|
54
60
|
Python::with_gil(|py| {
|
|
55
|
-
toml_backend::render_document(
|
|
61
|
+
toml_backend::render_document(
|
|
62
|
+
py,
|
|
63
|
+
&template,
|
|
64
|
+
tstring_toml::TomlProfile::default(),
|
|
65
|
+
node.as_ref(),
|
|
66
|
+
)
|
|
67
|
+
.unwrap();
|
|
56
68
|
});
|
|
57
69
|
});
|
|
58
70
|
});
|
|
@@ -78,7 +90,13 @@ fn benchmark_toml_dynamic_header_render(criterion: &mut Criterion) {
|
|
|
78
90
|
criterion.bench_function("toml_dynamic_header_render", |bench| {
|
|
79
91
|
bench.iter(|| {
|
|
80
92
|
Python::with_gil(|py| {
|
|
81
|
-
toml_backend::render_document(
|
|
93
|
+
toml_backend::render_document(
|
|
94
|
+
py,
|
|
95
|
+
&template,
|
|
96
|
+
tstring_toml::TomlProfile::default(),
|
|
97
|
+
node.as_ref(),
|
|
98
|
+
)
|
|
99
|
+
.unwrap();
|
|
82
100
|
});
|
|
83
101
|
});
|
|
84
102
|
});
|
|
@@ -104,7 +122,13 @@ fn benchmark_yaml_metadata_render(criterion: &mut Criterion) {
|
|
|
104
122
|
criterion.bench_function("yaml_metadata_render", |bench| {
|
|
105
123
|
bench.iter(|| {
|
|
106
124
|
Python::with_gil(|py| {
|
|
107
|
-
yaml_backend::render_document(
|
|
125
|
+
yaml_backend::render_document(
|
|
126
|
+
py,
|
|
127
|
+
&template,
|
|
128
|
+
tstring_yaml::YamlProfile::default(),
|
|
129
|
+
node.as_ref(),
|
|
130
|
+
)
|
|
131
|
+
.unwrap();
|
|
108
132
|
});
|
|
109
133
|
});
|
|
110
134
|
});
|
|
@@ -20,7 +20,7 @@ test = false
|
|
|
20
20
|
saphyr = { workspace = true }
|
|
21
21
|
saphyr-parser = { workspace = true }
|
|
22
22
|
serde_json = { workspace = true }
|
|
23
|
-
tstring-syntax = { version = "0.2.
|
|
23
|
+
tstring-syntax = { version = "0.2.2", path = "../tstring-core-rs" }
|
|
24
24
|
|
|
25
25
|
[dev-dependencies]
|
|
26
26
|
toml = { workspace = true }
|
|
@@ -3,11 +3,14 @@ use saphyr_parser::{ScalarStyle, Tag};
|
|
|
3
3
|
use std::borrow::Cow;
|
|
4
4
|
use std::str::FromStr;
|
|
5
5
|
use tstring_syntax::{
|
|
6
|
-
BackendError, BackendResult, NormalizedDocument, NormalizedEntry,
|
|
7
|
-
NormalizedKey, NormalizedKeyEntry, NormalizedStream, NormalizedValue,
|
|
8
|
-
SourceSpan, StreamItem, TemplateInput,
|
|
6
|
+
BackendError, BackendResult, InterpolationTypeRequirement, NormalizedDocument, NormalizedEntry,
|
|
7
|
+
NormalizedFloat, NormalizedKey, NormalizedKeyEntry, NormalizedStream, NormalizedValue,
|
|
8
|
+
SourcePosition, SourceSpan, StreamItem, TemplateInput,
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
const YAML_VALUE_PYTHON_TYPE: &str = "str | int | float | bool | None | datetime.date | datetime.time | datetime.datetime | list[object] | dict[object, object]";
|
|
12
|
+
const STRING_PYTHON_TYPE: &str = "str";
|
|
13
|
+
|
|
11
14
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
12
15
|
pub enum YamlProfile {
|
|
13
16
|
V1_2_2,
|
|
@@ -2084,6 +2087,172 @@ pub fn check_template(template: &TemplateInput) -> BackendResult<()> {
|
|
|
2084
2087
|
check_template_with_profile(template, YamlProfile::default())
|
|
2085
2088
|
}
|
|
2086
2089
|
|
|
2090
|
+
pub fn interpolation_type_requirements_with_profile(
|
|
2091
|
+
template: &TemplateInput,
|
|
2092
|
+
profile: YamlProfile,
|
|
2093
|
+
) -> BackendResult<Vec<InterpolationTypeRequirement>> {
|
|
2094
|
+
let stream = parse_validated_template_with_profile(template, profile)?;
|
|
2095
|
+
let mut requirements = Vec::new();
|
|
2096
|
+
collect_yaml_stream_type_requirements(&stream, &mut requirements);
|
|
2097
|
+
requirements.sort_by_key(|requirement| requirement.interpolation_index);
|
|
2098
|
+
Ok(requirements)
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
pub fn interpolation_type_requirements(
|
|
2102
|
+
template: &TemplateInput,
|
|
2103
|
+
) -> BackendResult<Vec<InterpolationTypeRequirement>> {
|
|
2104
|
+
interpolation_type_requirements_with_profile(template, YamlProfile::default())
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
2108
|
+
enum YamlInterpolationContext {
|
|
2109
|
+
Value,
|
|
2110
|
+
MappingKey,
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
fn collect_yaml_stream_type_requirements(
|
|
2114
|
+
stream: &YamlStreamNode,
|
|
2115
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
2116
|
+
) {
|
|
2117
|
+
for document in &stream.documents {
|
|
2118
|
+
collect_yaml_value_type_requirements(
|
|
2119
|
+
&document.value,
|
|
2120
|
+
YamlInterpolationContext::Value,
|
|
2121
|
+
requirements,
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
fn collect_yaml_value_type_requirements(
|
|
2127
|
+
value: &YamlValueNode,
|
|
2128
|
+
context: YamlInterpolationContext,
|
|
2129
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
2130
|
+
) {
|
|
2131
|
+
match value {
|
|
2132
|
+
YamlValueNode::Scalar(node) => {
|
|
2133
|
+
collect_yaml_scalar_type_requirements(node, "yaml scalar fragment", requirements);
|
|
2134
|
+
}
|
|
2135
|
+
YamlValueNode::Interpolation(node) => {
|
|
2136
|
+
requirements.push(yaml_value_type_requirement(node, context));
|
|
2137
|
+
}
|
|
2138
|
+
YamlValueNode::Mapping(node) => {
|
|
2139
|
+
for entry in &node.entries {
|
|
2140
|
+
collect_yaml_key_type_requirements(&entry.key, requirements);
|
|
2141
|
+
collect_yaml_value_type_requirements(
|
|
2142
|
+
&entry.value,
|
|
2143
|
+
YamlInterpolationContext::Value,
|
|
2144
|
+
requirements,
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
YamlValueNode::Sequence(node) => {
|
|
2149
|
+
for item in &node.items {
|
|
2150
|
+
collect_yaml_value_type_requirements(item, context, requirements);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
YamlValueNode::Decorated(node) => {
|
|
2154
|
+
if let Some(tag) = &node.tag {
|
|
2155
|
+
collect_yaml_chunk_type_requirements(
|
|
2156
|
+
&tag.chunks,
|
|
2157
|
+
"yaml metadata fragment",
|
|
2158
|
+
requirements,
|
|
2159
|
+
);
|
|
2160
|
+
}
|
|
2161
|
+
if let Some(anchor) = &node.anchor {
|
|
2162
|
+
collect_yaml_chunk_type_requirements(
|
|
2163
|
+
&anchor.chunks,
|
|
2164
|
+
"yaml metadata fragment",
|
|
2165
|
+
requirements,
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2168
|
+
collect_yaml_value_type_requirements(&node.value, context, requirements);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
fn collect_yaml_key_type_requirements(
|
|
2174
|
+
key: &YamlKeyNode,
|
|
2175
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
2176
|
+
) {
|
|
2177
|
+
match &key.value {
|
|
2178
|
+
YamlKeyValue::Scalar(node) => {
|
|
2179
|
+
collect_yaml_scalar_type_requirements(node, "yaml scalar fragment", requirements);
|
|
2180
|
+
}
|
|
2181
|
+
YamlKeyValue::Interpolation(node) => {
|
|
2182
|
+
requirements.push(yaml_value_type_requirement(
|
|
2183
|
+
node,
|
|
2184
|
+
YamlInterpolationContext::MappingKey,
|
|
2185
|
+
));
|
|
2186
|
+
}
|
|
2187
|
+
YamlKeyValue::Complex(node) => {
|
|
2188
|
+
collect_yaml_value_type_requirements(
|
|
2189
|
+
node,
|
|
2190
|
+
YamlInterpolationContext::MappingKey,
|
|
2191
|
+
requirements,
|
|
2192
|
+
);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
fn collect_yaml_scalar_type_requirements(
|
|
2198
|
+
scalar: &YamlScalarNode,
|
|
2199
|
+
description: &'static str,
|
|
2200
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
2201
|
+
) {
|
|
2202
|
+
match scalar {
|
|
2203
|
+
YamlScalarNode::Plain(node) => {
|
|
2204
|
+
collect_yaml_chunk_type_requirements(&node.chunks, description, requirements);
|
|
2205
|
+
}
|
|
2206
|
+
YamlScalarNode::DoubleQuoted(node) => {
|
|
2207
|
+
collect_yaml_chunk_type_requirements(&node.chunks, description, requirements);
|
|
2208
|
+
}
|
|
2209
|
+
YamlScalarNode::SingleQuoted(node) => {
|
|
2210
|
+
collect_yaml_chunk_type_requirements(&node.chunks, description, requirements);
|
|
2211
|
+
}
|
|
2212
|
+
YamlScalarNode::Block(node) => {
|
|
2213
|
+
collect_yaml_chunk_type_requirements(&node.chunks, description, requirements);
|
|
2214
|
+
}
|
|
2215
|
+
YamlScalarNode::Alias(node) => {
|
|
2216
|
+
collect_yaml_chunk_type_requirements(
|
|
2217
|
+
&node.chunks,
|
|
2218
|
+
"yaml metadata fragment",
|
|
2219
|
+
requirements,
|
|
2220
|
+
);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
fn collect_yaml_chunk_type_requirements(
|
|
2226
|
+
chunks: &[YamlChunk],
|
|
2227
|
+
description: &'static str,
|
|
2228
|
+
requirements: &mut Vec<InterpolationTypeRequirement>,
|
|
2229
|
+
) {
|
|
2230
|
+
for chunk in chunks {
|
|
2231
|
+
if let YamlChunk::Interpolation(node) = chunk {
|
|
2232
|
+
requirements.push(InterpolationTypeRequirement::new(
|
|
2233
|
+
node.interpolation_index,
|
|
2234
|
+
STRING_PYTHON_TYPE,
|
|
2235
|
+
description,
|
|
2236
|
+
));
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
fn yaml_value_type_requirement(
|
|
2242
|
+
node: &YamlInterpolationNode,
|
|
2243
|
+
context: YamlInterpolationContext,
|
|
2244
|
+
) -> InterpolationTypeRequirement {
|
|
2245
|
+
let description = match context {
|
|
2246
|
+
YamlInterpolationContext::Value => "yaml value",
|
|
2247
|
+
YamlInterpolationContext::MappingKey => "yaml mapping key",
|
|
2248
|
+
};
|
|
2249
|
+
InterpolationTypeRequirement::new(
|
|
2250
|
+
node.interpolation_index,
|
|
2251
|
+
YAML_VALUE_PYTHON_TYPE,
|
|
2252
|
+
description,
|
|
2253
|
+
)
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2087
2256
|
pub fn format_template_with_profile(
|
|
2088
2257
|
template: &TemplateInput,
|
|
2089
2258
|
profile: YamlProfile,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
use tstring_syntax::{
|
|
1
|
+
use tstring_syntax::{
|
|
2
|
+
InterpolationTypeRequirement, TemplateInput, TemplateInterpolation, TemplateSegment,
|
|
3
|
+
};
|
|
2
4
|
use tstring_yaml::{
|
|
3
|
-
YamlValueNode, check_template, format_template,
|
|
5
|
+
YamlValueNode, check_template, format_template, interpolation_type_requirements,
|
|
6
|
+
parse_template, validate_template,
|
|
4
7
|
};
|
|
5
8
|
|
|
6
9
|
fn interpolation(index: usize, expression: &str) -> TemplateSegment {
|
|
@@ -129,6 +132,63 @@ fn validates_plain_scalars_with_multiple_interpolations_and_no_whitespace() {
|
|
|
129
132
|
validate_template(&template).expect("expected YAML validation success");
|
|
130
133
|
}
|
|
131
134
|
|
|
135
|
+
#[test]
|
|
136
|
+
fn reports_contextual_interpolation_type_requirements() {
|
|
137
|
+
let template = TemplateInput::from_segments(vec![
|
|
138
|
+
interpolation(0, "key"),
|
|
139
|
+
TemplateSegment::StaticText(": ".to_owned()),
|
|
140
|
+
interpolation(1, "value"),
|
|
141
|
+
TemplateSegment::StaticText("\nmessage: \"Hello ".to_owned()),
|
|
142
|
+
interpolation(2, "fragment"),
|
|
143
|
+
TemplateSegment::StaticText("\"\ntagged: !<tag:".to_owned()),
|
|
144
|
+
interpolation(3, "tag"),
|
|
145
|
+
TemplateSegment::StaticText("> ".to_owned()),
|
|
146
|
+
interpolation(4, "tagged_value"),
|
|
147
|
+
TemplateSegment::StaticText("\n".to_owned()),
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
assert_eq!(
|
|
151
|
+
interpolation_type_requirements(&template).expect("expected type requirements"),
|
|
152
|
+
vec![
|
|
153
|
+
InterpolationTypeRequirement::new(
|
|
154
|
+
0,
|
|
155
|
+
"str | int | float | bool | None | datetime.date | datetime.time | datetime.datetime | list[object] | dict[object, object]",
|
|
156
|
+
"yaml mapping key"
|
|
157
|
+
),
|
|
158
|
+
InterpolationTypeRequirement::new(
|
|
159
|
+
1,
|
|
160
|
+
"str | int | float | bool | None | datetime.date | datetime.time | datetime.datetime | list[object] | dict[object, object]",
|
|
161
|
+
"yaml value"
|
|
162
|
+
),
|
|
163
|
+
InterpolationTypeRequirement::new(2, "str", "yaml scalar fragment"),
|
|
164
|
+
InterpolationTypeRequirement::new(3, "str", "yaml metadata fragment"),
|
|
165
|
+
InterpolationTypeRequirement::new(
|
|
166
|
+
4,
|
|
167
|
+
"str | int | float | bool | None | datetime.date | datetime.time | datetime.datetime | list[object] | dict[object, object]",
|
|
168
|
+
"yaml value"
|
|
169
|
+
),
|
|
170
|
+
]
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#[test]
|
|
175
|
+
fn reports_complex_key_interpolation_requirements_in_key_context() {
|
|
176
|
+
let template = TemplateInput::from_segments(vec![
|
|
177
|
+
TemplateSegment::StaticText("? [".to_owned()),
|
|
178
|
+
interpolation(0, "item"),
|
|
179
|
+
TemplateSegment::StaticText("]\n: ok\n".to_owned()),
|
|
180
|
+
]);
|
|
181
|
+
|
|
182
|
+
assert_eq!(
|
|
183
|
+
interpolation_type_requirements(&template).expect("expected type requirements"),
|
|
184
|
+
vec![InterpolationTypeRequirement::new(
|
|
185
|
+
0,
|
|
186
|
+
"str | int | float | bool | None | datetime.date | datetime.time | datetime.datetime | list[object] | dict[object, object]",
|
|
187
|
+
"yaml mapping key"
|
|
188
|
+
)]
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
132
192
|
#[test]
|
|
133
193
|
fn formats_yaml_templates_with_raw_interpolations() {
|
|
134
194
|
let template = TemplateInput::from_segments(vec![
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tstring_bindings-0.2.0 → tstring_bindings-0.2.2}/yaml-tstring-rs/test-support/renderer_layout.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|