tstring-bindings 0.1.1__tar.gz → 0.2.1__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.1.1 → tstring_bindings-0.2.1}/Cargo.lock +79 -34
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/Cargo.toml +1 -1
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/PKG-INFO +1 -1
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/json-tstring-rs/Cargo.toml +1 -1
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/json-tstring-rs/src/lib.rs +29 -4
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/json-tstring-rs/tests/parser.rs +13 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/pyproject.toml +1 -1
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python/tstring_bindings/__init__.py +17 -13
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python/tstring_bindings/__init__.pyi +11 -7
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python/tstring_bindings/tstring_bindings.pyi +13 -10
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python-bindings/Cargo.toml +5 -5
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python-bindings/src/lib.rs +4 -4
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/toml-tstring-rs/Cargo.toml +1 -1
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/toml-tstring-rs/src/lib.rs +104 -54
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/toml-tstring-rs/tests/parser.rs +20 -1
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-pyo3-bindings/Cargo.toml +5 -5
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/yaml-tstring-rs/Cargo.toml +1 -1
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/yaml-tstring-rs/src/lib.rs +95 -2
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/yaml-tstring-rs/tests/parser.rs +68 -1
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/README.md +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/json-tstring-rs/README.md +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/json-tstring-rs/tests/conformance.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python/tstring_bindings/_profiles.py +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python/tstring_bindings/_types.py +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python/tstring_bindings/py.typed +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/python-bindings/README.md +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/toml-tstring-rs/README.md +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/toml-tstring-rs/tests/conformance.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-core-rs/Cargo.toml +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-core-rs/README.md +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-core-rs/src/lib.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-pyo3-bindings/benches/render_paths.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-pyo3-bindings/src/json.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-pyo3-bindings/src/lib.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-pyo3-bindings/src/toml.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-pyo3-bindings/src/yaml.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/yaml-tstring-rs/README.md +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/yaml-tstring-rs/test-support/renderer_layout.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/yaml-tstring-rs/tests/conformance.rs +0 -0
- {tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/yaml-tstring-rs/tests/normalized.rs +0 -0
|
@@ -11,6 +11,15 @@ dependencies = [
|
|
|
11
11
|
"memchr",
|
|
12
12
|
]
|
|
13
13
|
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "alloca"
|
|
16
|
+
version = "0.4.0"
|
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
+
checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"cc",
|
|
21
|
+
]
|
|
22
|
+
|
|
14
23
|
[[package]]
|
|
15
24
|
name = "anes"
|
|
16
25
|
version = "0.1.6"
|
|
@@ -47,6 +56,16 @@ version = "0.3.0"
|
|
|
47
56
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
48
57
|
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|
49
58
|
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "cc"
|
|
61
|
+
version = "1.2.57"
|
|
62
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
+
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
|
|
64
|
+
dependencies = [
|
|
65
|
+
"find-msvc-tools",
|
|
66
|
+
"shlex",
|
|
67
|
+
]
|
|
68
|
+
|
|
50
69
|
[[package]]
|
|
51
70
|
name = "cfg-if"
|
|
52
71
|
version = "1.0.4"
|
|
@@ -107,25 +126,24 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
|
|
107
126
|
|
|
108
127
|
[[package]]
|
|
109
128
|
name = "criterion"
|
|
110
|
-
version = "0.
|
|
129
|
+
version = "0.8.2"
|
|
111
130
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
112
|
-
checksum = "
|
|
131
|
+
checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3"
|
|
113
132
|
dependencies = [
|
|
133
|
+
"alloca",
|
|
114
134
|
"anes",
|
|
115
135
|
"cast",
|
|
116
136
|
"ciborium",
|
|
117
137
|
"clap",
|
|
118
138
|
"criterion-plot",
|
|
119
|
-
"is-terminal",
|
|
120
139
|
"itertools",
|
|
121
140
|
"num-traits",
|
|
122
|
-
"once_cell",
|
|
123
141
|
"oorandom",
|
|
142
|
+
"page_size",
|
|
124
143
|
"plotters",
|
|
125
144
|
"rayon",
|
|
126
145
|
"regex",
|
|
127
146
|
"serde",
|
|
128
|
-
"serde_derive",
|
|
129
147
|
"serde_json",
|
|
130
148
|
"tinytemplate",
|
|
131
149
|
"walkdir",
|
|
@@ -133,9 +151,9 @@ dependencies = [
|
|
|
133
151
|
|
|
134
152
|
[[package]]
|
|
135
153
|
name = "criterion-plot"
|
|
136
|
-
version = "0.
|
|
154
|
+
version = "0.8.2"
|
|
137
155
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
138
|
-
checksum = "
|
|
156
|
+
checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea"
|
|
139
157
|
dependencies = [
|
|
140
158
|
"cast",
|
|
141
159
|
"itertools",
|
|
@@ -193,6 +211,12 @@ version = "1.0.2"
|
|
|
193
211
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
194
212
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|
195
213
|
|
|
214
|
+
[[package]]
|
|
215
|
+
name = "find-msvc-tools"
|
|
216
|
+
version = "0.1.9"
|
|
217
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
218
|
+
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
|
219
|
+
|
|
196
220
|
[[package]]
|
|
197
221
|
name = "foldhash"
|
|
198
222
|
version = "0.1.5"
|
|
@@ -240,12 +264,6 @@ version = "0.5.0"
|
|
|
240
264
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
241
265
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
242
266
|
|
|
243
|
-
[[package]]
|
|
244
|
-
name = "hermit-abi"
|
|
245
|
-
version = "0.5.2"
|
|
246
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
247
|
-
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
|
248
|
-
|
|
249
267
|
[[package]]
|
|
250
268
|
name = "indexmap"
|
|
251
269
|
version = "2.13.0"
|
|
@@ -265,22 +283,11 @@ dependencies = [
|
|
|
265
283
|
"rustversion",
|
|
266
284
|
]
|
|
267
285
|
|
|
268
|
-
[[package]]
|
|
269
|
-
name = "is-terminal"
|
|
270
|
-
version = "0.4.17"
|
|
271
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
272
|
-
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
|
273
|
-
dependencies = [
|
|
274
|
-
"hermit-abi",
|
|
275
|
-
"libc",
|
|
276
|
-
"windows-sys",
|
|
277
|
-
]
|
|
278
|
-
|
|
279
286
|
[[package]]
|
|
280
287
|
name = "itertools"
|
|
281
|
-
version = "0.
|
|
288
|
+
version = "0.13.0"
|
|
282
289
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
283
|
-
checksum = "
|
|
290
|
+
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
284
291
|
dependencies = [
|
|
285
292
|
"either",
|
|
286
293
|
]
|
|
@@ -371,6 +378,16 @@ dependencies = [
|
|
|
371
378
|
"num-traits",
|
|
372
379
|
]
|
|
373
380
|
|
|
381
|
+
[[package]]
|
|
382
|
+
name = "page_size"
|
|
383
|
+
version = "0.6.0"
|
|
384
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
385
|
+
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
|
386
|
+
dependencies = [
|
|
387
|
+
"libc",
|
|
388
|
+
"winapi",
|
|
389
|
+
]
|
|
390
|
+
|
|
374
391
|
[[package]]
|
|
375
392
|
name = "plotters"
|
|
376
393
|
version = "0.3.7"
|
|
@@ -634,6 +651,12 @@ dependencies = [
|
|
|
634
651
|
"serde_core",
|
|
635
652
|
]
|
|
636
653
|
|
|
654
|
+
[[package]]
|
|
655
|
+
name = "shlex"
|
|
656
|
+
version = "1.3.0"
|
|
657
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
658
|
+
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
659
|
+
|
|
637
660
|
[[package]]
|
|
638
661
|
name = "syn"
|
|
639
662
|
version = "2.0.117"
|
|
@@ -702,7 +725,7 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
|
|
702
725
|
|
|
703
726
|
[[package]]
|
|
704
727
|
name = "tstring-backend-e2e-tests"
|
|
705
|
-
version = "0.
|
|
728
|
+
version = "0.2.1"
|
|
706
729
|
dependencies = [
|
|
707
730
|
"tstring-json",
|
|
708
731
|
"tstring-syntax",
|
|
@@ -712,7 +735,7 @@ dependencies = [
|
|
|
712
735
|
|
|
713
736
|
[[package]]
|
|
714
737
|
name = "tstring-bindings"
|
|
715
|
-
version = "0.
|
|
738
|
+
version = "0.2.1"
|
|
716
739
|
dependencies = [
|
|
717
740
|
"pyo3",
|
|
718
741
|
"pythonize",
|
|
@@ -728,7 +751,7 @@ dependencies = [
|
|
|
728
751
|
|
|
729
752
|
[[package]]
|
|
730
753
|
name = "tstring-json"
|
|
731
|
-
version = "0.
|
|
754
|
+
version = "0.2.1"
|
|
732
755
|
dependencies = [
|
|
733
756
|
"serde_json",
|
|
734
757
|
"toml",
|
|
@@ -737,7 +760,7 @@ dependencies = [
|
|
|
737
760
|
|
|
738
761
|
[[package]]
|
|
739
762
|
name = "tstring-pyo3-bindings"
|
|
740
|
-
version = "0.
|
|
763
|
+
version = "0.2.1"
|
|
741
764
|
dependencies = [
|
|
742
765
|
"criterion",
|
|
743
766
|
"pyo3",
|
|
@@ -753,14 +776,14 @@ dependencies = [
|
|
|
753
776
|
|
|
754
777
|
[[package]]
|
|
755
778
|
name = "tstring-syntax"
|
|
756
|
-
version = "0.
|
|
779
|
+
version = "0.2.1"
|
|
757
780
|
dependencies = [
|
|
758
781
|
"num-bigint",
|
|
759
782
|
]
|
|
760
783
|
|
|
761
784
|
[[package]]
|
|
762
785
|
name = "tstring-toml"
|
|
763
|
-
version = "0.
|
|
786
|
+
version = "0.2.1"
|
|
764
787
|
dependencies = [
|
|
765
788
|
"serde_json",
|
|
766
789
|
"toml",
|
|
@@ -769,7 +792,7 @@ dependencies = [
|
|
|
769
792
|
|
|
770
793
|
[[package]]
|
|
771
794
|
name = "tstring-yaml"
|
|
772
|
-
version = "0.
|
|
795
|
+
version = "0.2.1"
|
|
773
796
|
dependencies = [
|
|
774
797
|
"saphyr",
|
|
775
798
|
"saphyr-parser",
|
|
@@ -780,7 +803,7 @@ dependencies = [
|
|
|
780
803
|
|
|
781
804
|
[[package]]
|
|
782
805
|
name = "tstring-yaml-pyo3-tests"
|
|
783
|
-
version = "0.
|
|
806
|
+
version = "0.2.1"
|
|
784
807
|
dependencies = [
|
|
785
808
|
"pyo3",
|
|
786
809
|
"saphyr",
|
|
@@ -865,6 +888,22 @@ dependencies = [
|
|
|
865
888
|
"wasm-bindgen",
|
|
866
889
|
]
|
|
867
890
|
|
|
891
|
+
[[package]]
|
|
892
|
+
name = "winapi"
|
|
893
|
+
version = "0.3.9"
|
|
894
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
895
|
+
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
896
|
+
dependencies = [
|
|
897
|
+
"winapi-i686-pc-windows-gnu",
|
|
898
|
+
"winapi-x86_64-pc-windows-gnu",
|
|
899
|
+
]
|
|
900
|
+
|
|
901
|
+
[[package]]
|
|
902
|
+
name = "winapi-i686-pc-windows-gnu"
|
|
903
|
+
version = "0.4.0"
|
|
904
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
905
|
+
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
906
|
+
|
|
868
907
|
[[package]]
|
|
869
908
|
name = "winapi-util"
|
|
870
909
|
version = "0.1.11"
|
|
@@ -874,6 +913,12 @@ dependencies = [
|
|
|
874
913
|
"windows-sys",
|
|
875
914
|
]
|
|
876
915
|
|
|
916
|
+
[[package]]
|
|
917
|
+
name = "winapi-x86_64-pc-windows-gnu"
|
|
918
|
+
version = "0.4.0"
|
|
919
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
920
|
+
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
921
|
+
|
|
877
922
|
[[package]]
|
|
878
923
|
name = "windows-link"
|
|
879
924
|
version = "0.2.1"
|
|
@@ -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.
|
|
12
|
+
version = "0.2.1"
|
|
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.
|
|
21
|
+
tstring-syntax = { version = "0.2.1", path = "../tstring-core-rs" }
|
|
22
22
|
|
|
23
23
|
[dev-dependencies]
|
|
24
24
|
toml = { workspace = true }
|
|
@@ -600,11 +600,36 @@ pub fn parse_template(template: &TemplateInput) -> BackendResult<JsonDocumentNod
|
|
|
600
600
|
parse_template_with_profile(template, JsonProfile::default())
|
|
601
601
|
}
|
|
602
602
|
|
|
603
|
+
pub fn parse_validated_template_with_profile(
|
|
604
|
+
template: &TemplateInput,
|
|
605
|
+
profile: JsonProfile,
|
|
606
|
+
) -> BackendResult<JsonDocumentNode> {
|
|
607
|
+
// JSON does not add format-specific post-parse validation yet. Keep the
|
|
608
|
+
// validated entry point aligned with the other backends so callers can rely
|
|
609
|
+
// on one API shape as backend-specific validation rules are introduced.
|
|
610
|
+
parse_template_with_profile(template, profile)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
pub fn parse_validated_template(template: &TemplateInput) -> BackendResult<JsonDocumentNode> {
|
|
614
|
+
parse_validated_template_with_profile(template, JsonProfile::default())
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
pub fn validate_template_with_profile(
|
|
618
|
+
template: &TemplateInput,
|
|
619
|
+
profile: JsonProfile,
|
|
620
|
+
) -> BackendResult<()> {
|
|
621
|
+
parse_validated_template_with_profile(template, profile).map(|_| ())
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
pub fn validate_template(template: &TemplateInput) -> BackendResult<()> {
|
|
625
|
+
validate_template_with_profile(template, JsonProfile::default())
|
|
626
|
+
}
|
|
627
|
+
|
|
603
628
|
pub fn check_template_with_profile(
|
|
604
629
|
template: &TemplateInput,
|
|
605
630
|
profile: JsonProfile,
|
|
606
631
|
) -> BackendResult<()> {
|
|
607
|
-
|
|
632
|
+
validate_template_with_profile(template, profile)
|
|
608
633
|
}
|
|
609
634
|
|
|
610
635
|
pub fn check_template(template: &TemplateInput) -> BackendResult<()> {
|
|
@@ -615,7 +640,7 @@ pub fn format_template_with_profile(
|
|
|
615
640
|
template: &TemplateInput,
|
|
616
641
|
profile: JsonProfile,
|
|
617
642
|
) -> BackendResult<String> {
|
|
618
|
-
let document =
|
|
643
|
+
let document = parse_validated_template_with_profile(template, profile)?;
|
|
619
644
|
format_json_value(template, &document.value)
|
|
620
645
|
}
|
|
621
646
|
|
|
@@ -774,9 +799,9 @@ fn normalize_number(number: &serde_json::Number) -> BackendResult<NormalizedValu
|
|
|
774
799
|
|
|
775
800
|
#[cfg(test)]
|
|
776
801
|
mod tests {
|
|
777
|
-
use super::{
|
|
802
|
+
use super::{JsonKeyValue, JsonStringPart, JsonValueNode, parse_template};
|
|
778
803
|
use pyo3::prelude::*;
|
|
779
|
-
use serde_json::{
|
|
804
|
+
use serde_json::{Map, Number, Value, json};
|
|
780
805
|
use tstring_pyo3_bindings::{extract_template, json::render_document};
|
|
781
806
|
use tstring_syntax::{BackendError, BackendResult, ErrorKind};
|
|
782
807
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
use tstring_json::{
|
|
2
2
|
JsonKeyValue, JsonStringPart, JsonValueNode, check_template, format_template, parse_template,
|
|
3
|
+
parse_validated_template, validate_template,
|
|
3
4
|
};
|
|
4
5
|
use tstring_syntax::{TemplateInput, TemplateInterpolation, TemplateSegment};
|
|
5
6
|
|
|
@@ -68,6 +69,18 @@ fn checks_valid_json_templates() {
|
|
|
68
69
|
check_template(&template).expect("expected check success");
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
#[test]
|
|
73
|
+
fn validates_json_templates_with_supported_interpolations() {
|
|
74
|
+
let template = TemplateInput::from_segments(vec![
|
|
75
|
+
TemplateSegment::StaticText("{\"name\": ".to_owned()),
|
|
76
|
+
interpolation(0, "name"),
|
|
77
|
+
TemplateSegment::StaticText(", \"active\": true}".to_owned()),
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
validate_template(&template).expect("expected validate success");
|
|
81
|
+
parse_validated_template(&template).expect("expected validated parse success");
|
|
82
|
+
}
|
|
83
|
+
|
|
71
84
|
#[test]
|
|
72
85
|
fn formats_json_templates_with_raw_interpolations() {
|
|
73
86
|
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.1.1 → tstring_bindings-0.2.1}/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.
|
|
28
|
-
tstring-pyo3-bindings = { version = "0.
|
|
29
|
-
tstring-syntax = { version = "0.
|
|
27
|
+
tstring-json = { version = "0.2.1", path = "../json-tstring-rs" }
|
|
28
|
+
tstring-pyo3-bindings = { version = "0.2.1", path = "../tstring-pyo3-bindings" }
|
|
29
|
+
tstring-syntax = { version = "0.2.1", path = "../tstring-core-rs" }
|
|
30
30
|
toml = { workspace = true }
|
|
31
|
-
tstring-toml = { version = "0.
|
|
32
|
-
tstring-yaml = { version = "0.
|
|
31
|
+
tstring-toml = { version = "0.2.1", path = "../toml-tstring-rs" }
|
|
32
|
+
tstring-yaml = { version = "0.2.1", path = "../yaml-tstring-rs" }
|
|
33
33
|
|
|
34
34
|
[dev-dependencies]
|
|
35
35
|
pyo3 = { workspace = true, features = ["auto-initialize"] }
|
|
@@ -160,7 +160,7 @@ fn parse_json_template(
|
|
|
160
160
|
) -> PyResult<Arc<JsonDocumentNode>> {
|
|
161
161
|
json_parse_cache()
|
|
162
162
|
.get_or_try_insert_with(&template_cache_key(template, profile.as_str()), || {
|
|
163
|
-
tstring_json::
|
|
163
|
+
tstring_json::parse_validated_template_with_profile(template.input(), profile)
|
|
164
164
|
})
|
|
165
165
|
.map_err(backend_error_to_py)
|
|
166
166
|
}
|
|
@@ -171,7 +171,7 @@ fn parse_toml_template(
|
|
|
171
171
|
) -> PyResult<Arc<TomlDocumentNode>> {
|
|
172
172
|
toml_parse_cache()
|
|
173
173
|
.get_or_try_insert_with(&template_cache_key(template, profile.as_str()), || {
|
|
174
|
-
tstring_toml::
|
|
174
|
+
tstring_toml::parse_validated_template_with_profile(template.input(), profile)
|
|
175
175
|
})
|
|
176
176
|
.map_err(backend_error_to_py)
|
|
177
177
|
}
|
|
@@ -182,7 +182,7 @@ fn parse_yaml_template(
|
|
|
182
182
|
) -> PyResult<Arc<YamlStreamNode>> {
|
|
183
183
|
yaml_parse_cache()
|
|
184
184
|
.get_or_try_insert_with(&template_cache_key(template, profile.as_str()), || {
|
|
185
|
-
tstring_yaml::
|
|
185
|
+
tstring_yaml::parse_validated_template_with_profile(template.input(), profile)
|
|
186
186
|
})
|
|
187
187
|
.map_err(backend_error_to_py)
|
|
188
188
|
}
|
|
@@ -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.
|
|
630
|
+
module.add("__version__", "0.2.1")?;
|
|
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>())?;
|
|
@@ -1110,11 +1110,36 @@ pub fn parse_template(template: &TemplateInput) -> BackendResult<TomlDocumentNod
|
|
|
1110
1110
|
parse_template_with_profile(template, TomlProfile::default())
|
|
1111
1111
|
}
|
|
1112
1112
|
|
|
1113
|
+
pub fn parse_validated_template_with_profile(
|
|
1114
|
+
template: &TemplateInput,
|
|
1115
|
+
profile: TomlProfile,
|
|
1116
|
+
) -> BackendResult<TomlDocumentNode> {
|
|
1117
|
+
// TOML does not add format-specific post-parse validation yet. Keep the
|
|
1118
|
+
// validated entry point aligned with the other backends so callers can rely
|
|
1119
|
+
// on one API shape as backend-specific validation rules are introduced.
|
|
1120
|
+
parse_template_with_profile(template, profile)
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
pub fn parse_validated_template(template: &TemplateInput) -> BackendResult<TomlDocumentNode> {
|
|
1124
|
+
parse_validated_template_with_profile(template, TomlProfile::default())
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
pub fn validate_template_with_profile(
|
|
1128
|
+
template: &TemplateInput,
|
|
1129
|
+
profile: TomlProfile,
|
|
1130
|
+
) -> BackendResult<()> {
|
|
1131
|
+
parse_validated_template_with_profile(template, profile).map(|_| ())
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
pub fn validate_template(template: &TemplateInput) -> BackendResult<()> {
|
|
1135
|
+
validate_template_with_profile(template, TomlProfile::default())
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1113
1138
|
pub fn check_template_with_profile(
|
|
1114
1139
|
template: &TemplateInput,
|
|
1115
1140
|
profile: TomlProfile,
|
|
1116
1141
|
) -> BackendResult<()> {
|
|
1117
|
-
|
|
1142
|
+
validate_template_with_profile(template, profile)
|
|
1118
1143
|
}
|
|
1119
1144
|
|
|
1120
1145
|
pub fn check_template(template: &TemplateInput) -> BackendResult<()> {
|
|
@@ -1125,7 +1150,7 @@ pub fn format_template_with_profile(
|
|
|
1125
1150
|
template: &TemplateInput,
|
|
1126
1151
|
profile: TomlProfile,
|
|
1127
1152
|
) -> BackendResult<String> {
|
|
1128
|
-
let document =
|
|
1153
|
+
let document = parse_validated_template_with_profile(template, profile)?;
|
|
1129
1154
|
format_toml_document(template, &document)
|
|
1130
1155
|
}
|
|
1131
1156
|
|
|
@@ -1390,7 +1415,7 @@ fn normalize_time(value: toml::value::Time) -> NormalizedTime {
|
|
|
1390
1415
|
|
|
1391
1416
|
#[cfg(test)]
|
|
1392
1417
|
mod tests {
|
|
1393
|
-
use super::{
|
|
1418
|
+
use super::{TomlKeySegmentValue, TomlStatementNode, TomlValueNode, parse_template};
|
|
1394
1419
|
use pyo3::prelude::*;
|
|
1395
1420
|
use tstring_pyo3_bindings::{extract_template, toml::render_document};
|
|
1396
1421
|
use tstring_syntax::{BackendError, BackendResult, ErrorKind};
|
|
@@ -1901,10 +1926,12 @@ mod tests {
|
|
|
1901
1926
|
);
|
|
1902
1927
|
let special_floats = table["special_float_array"].as_array().expect("array");
|
|
1903
1928
|
assert!(special_floats[0].as_float().expect("float").is_infinite());
|
|
1904
|
-
assert!(
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1929
|
+
assert!(
|
|
1930
|
+
special_floats[1]
|
|
1931
|
+
.as_float()
|
|
1932
|
+
.expect("float")
|
|
1933
|
+
.is_sign_negative()
|
|
1934
|
+
);
|
|
1908
1935
|
assert!(special_floats[2].as_float().expect("float").is_nan());
|
|
1909
1936
|
assert_eq!(
|
|
1910
1937
|
table["special_float_nested_arrays"]
|
|
@@ -1916,18 +1943,24 @@ mod tests {
|
|
|
1916
1943
|
let special_float_deeper_arrays = table["special_float_deeper_arrays"]
|
|
1917
1944
|
.as_array()
|
|
1918
1945
|
.expect("array");
|
|
1919
|
-
assert!(
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1946
|
+
assert!(
|
|
1947
|
+
special_float_deeper_arrays[0][0][0]
|
|
1948
|
+
.as_float()
|
|
1949
|
+
.expect("float")
|
|
1950
|
+
.is_infinite()
|
|
1951
|
+
);
|
|
1952
|
+
assert!(
|
|
1953
|
+
special_float_deeper_arrays[1][0][0]
|
|
1954
|
+
.as_float()
|
|
1955
|
+
.expect("float")
|
|
1956
|
+
.is_sign_negative()
|
|
1957
|
+
);
|
|
1958
|
+
assert!(
|
|
1959
|
+
special_float_deeper_arrays[2][0][0]
|
|
1960
|
+
.as_float()
|
|
1961
|
+
.expect("float")
|
|
1962
|
+
.is_nan()
|
|
1963
|
+
);
|
|
1931
1964
|
assert_eq!(
|
|
1932
1965
|
table["upper_exp_nested_mixed"]
|
|
1933
1966
|
.as_array()
|
|
@@ -1935,14 +1968,18 @@ mod tests {
|
|
|
1935
1968
|
.len(),
|
|
1936
1969
|
2
|
|
1937
1970
|
);
|
|
1938
|
-
assert!(
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1971
|
+
assert!(
|
|
1972
|
+
table["special_float_inline_table"]["pos"]
|
|
1973
|
+
.as_float()
|
|
1974
|
+
.expect("float")
|
|
1975
|
+
.is_infinite()
|
|
1976
|
+
);
|
|
1977
|
+
assert!(
|
|
1978
|
+
table["special_float_inline_table"]["nan"]
|
|
1979
|
+
.as_float()
|
|
1980
|
+
.expect("float")
|
|
1981
|
+
.is_nan()
|
|
1982
|
+
);
|
|
1946
1983
|
assert_eq!(
|
|
1947
1984
|
table["special_float_mixed_nested"]
|
|
1948
1985
|
.as_array()
|
|
@@ -2026,9 +2063,10 @@ mod tests {
|
|
|
2026
2063
|
Err(err) => err,
|
|
2027
2064
|
};
|
|
2028
2065
|
assert_eq!(err.kind, ErrorKind::Parse);
|
|
2029
|
-
assert!(
|
|
2030
|
-
.message
|
|
2031
|
-
|
|
2066
|
+
assert!(
|
|
2067
|
+
err.message
|
|
2068
|
+
.contains("single-line basic strings cannot contain newlines")
|
|
2069
|
+
);
|
|
2032
2070
|
});
|
|
2033
2071
|
}
|
|
2034
2072
|
|
|
@@ -2920,30 +2958,42 @@ mod tests {
|
|
|
2920
2958
|
rendered.text,
|
|
2921
2959
|
"special_float_inline_table = { pos = +inf, neg = -inf, nan = nan }\nspecial_float_mixed_nested = [[+inf, -inf], [nan]]"
|
|
2922
2960
|
);
|
|
2923
|
-
assert!(
|
|
2924
|
-
.
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
.
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
assert!(
|
|
2936
|
-
.
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
.
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2961
|
+
assert!(
|
|
2962
|
+
rendered.data["special_float_inline_table"]["pos"]
|
|
2963
|
+
.as_float()
|
|
2964
|
+
.expect("pos float")
|
|
2965
|
+
.is_infinite()
|
|
2966
|
+
);
|
|
2967
|
+
assert!(
|
|
2968
|
+
rendered.data["special_float_inline_table"]["neg"]
|
|
2969
|
+
.as_float()
|
|
2970
|
+
.expect("neg float")
|
|
2971
|
+
.is_sign_negative()
|
|
2972
|
+
);
|
|
2973
|
+
assert!(
|
|
2974
|
+
rendered.data["special_float_inline_table"]["nan"]
|
|
2975
|
+
.as_float()
|
|
2976
|
+
.expect("nan float")
|
|
2977
|
+
.is_nan()
|
|
2978
|
+
);
|
|
2979
|
+
assert!(
|
|
2980
|
+
rendered.data["special_float_mixed_nested"][0][0]
|
|
2981
|
+
.as_float()
|
|
2982
|
+
.expect("nested pos")
|
|
2983
|
+
.is_infinite()
|
|
2984
|
+
);
|
|
2985
|
+
assert!(
|
|
2986
|
+
rendered.data["special_float_mixed_nested"][0][1]
|
|
2987
|
+
.as_float()
|
|
2988
|
+
.expect("nested neg")
|
|
2989
|
+
.is_sign_negative()
|
|
2990
|
+
);
|
|
2991
|
+
assert!(
|
|
2992
|
+
rendered.data["special_float_mixed_nested"][1][0]
|
|
2993
|
+
.as_float()
|
|
2994
|
+
.expect("nested nan")
|
|
2995
|
+
.is_nan()
|
|
2996
|
+
);
|
|
2947
2997
|
});
|
|
2948
2998
|
}
|
|
2949
2999
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
use tstring_syntax::{TemplateInput, TemplateInterpolation, TemplateSegment};
|
|
2
2
|
use tstring_toml::{
|
|
3
|
-
|
|
3
|
+
TomlStatementNode, TomlValueNode, check_template, format_template, parse_template,
|
|
4
|
+
parse_validated_template, validate_template,
|
|
4
5
|
};
|
|
5
6
|
|
|
6
7
|
fn interpolation(index: usize, expression: &str) -> TemplateSegment {
|
|
@@ -64,6 +65,24 @@ fn checks_valid_toml_templates() {
|
|
|
64
65
|
check_template(&template).expect("expected check success");
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
#[test]
|
|
69
|
+
fn validates_toml_templates_with_supported_interpolations() {
|
|
70
|
+
let template = TemplateInput::from_segments(vec![
|
|
71
|
+
TemplateSegment::StaticText("title = ".to_owned()),
|
|
72
|
+
TemplateSegment::Interpolation(TemplateInterpolation {
|
|
73
|
+
expression: "title".to_owned(),
|
|
74
|
+
conversion: None,
|
|
75
|
+
format_spec: String::new(),
|
|
76
|
+
interpolation_index: 0,
|
|
77
|
+
raw_source: Some("{title}".to_owned()),
|
|
78
|
+
}),
|
|
79
|
+
TemplateSegment::StaticText("\n".to_owned()),
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
validate_template(&template).expect("expected validate success");
|
|
83
|
+
parse_validated_template(&template).expect("expected validated parse success");
|
|
84
|
+
}
|
|
85
|
+
|
|
67
86
|
#[test]
|
|
68
87
|
fn formats_toml_templates_with_raw_interpolations() {
|
|
69
88
|
let template = TemplateInput::from_segments(vec![
|
|
@@ -16,13 +16,13 @@ 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.
|
|
20
|
-
tstring-syntax = { version = "0.
|
|
21
|
-
tstring-toml = { version = "0.
|
|
22
|
-
tstring-yaml = { version = "0.
|
|
19
|
+
tstring-json = { version = "0.2.1", path = "../json-tstring-rs" }
|
|
20
|
+
tstring-syntax = { version = "0.2.1", path = "../tstring-core-rs" }
|
|
21
|
+
tstring-toml = { version = "0.2.1", path = "../toml-tstring-rs" }
|
|
22
|
+
tstring-yaml = { version = "0.2.1", path = "../yaml-tstring-rs" }
|
|
23
23
|
|
|
24
24
|
[dev-dependencies]
|
|
25
|
-
criterion = "0.
|
|
25
|
+
criterion = "0.8.2"
|
|
26
26
|
pyo3 = { workspace = true, features = ["auto-initialize"] }
|
|
27
27
|
|
|
28
28
|
[[bench]]
|
|
@@ -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.
|
|
23
|
+
tstring-syntax = { version = "0.2.1", path = "../tstring-core-rs" }
|
|
24
24
|
|
|
25
25
|
[dev-dependencies]
|
|
26
26
|
toml = { workspace = true }
|
|
@@ -2048,11 +2048,36 @@ pub fn parse_template(template: &TemplateInput) -> BackendResult<YamlStreamNode>
|
|
|
2048
2048
|
parse_template_with_profile(template, YamlProfile::default())
|
|
2049
2049
|
}
|
|
2050
2050
|
|
|
2051
|
+
pub fn parse_validated_template_with_profile(
|
|
2052
|
+
template: &TemplateInput,
|
|
2053
|
+
profile: YamlProfile,
|
|
2054
|
+
) -> BackendResult<YamlStreamNode> {
|
|
2055
|
+
let stream = parse_template_with_profile(template, profile)?;
|
|
2056
|
+
validate_template_stream(&stream)?;
|
|
2057
|
+
Ok(stream)
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
pub fn parse_validated_template(template: &TemplateInput) -> BackendResult<YamlStreamNode> {
|
|
2061
|
+
parse_validated_template_with_profile(template, YamlProfile::default())
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
pub fn validate_template_with_profile(
|
|
2065
|
+
template: &TemplateInput,
|
|
2066
|
+
profile: YamlProfile,
|
|
2067
|
+
) -> BackendResult<()> {
|
|
2068
|
+
let stream = parse_template_with_profile(template, profile)?;
|
|
2069
|
+
validate_template_stream(&stream)
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
pub fn validate_template(template: &TemplateInput) -> BackendResult<()> {
|
|
2073
|
+
validate_template_with_profile(template, YamlProfile::default())
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2051
2076
|
pub fn check_template_with_profile(
|
|
2052
2077
|
template: &TemplateInput,
|
|
2053
2078
|
profile: YamlProfile,
|
|
2054
2079
|
) -> BackendResult<()> {
|
|
2055
|
-
|
|
2080
|
+
validate_template_with_profile(template, profile)
|
|
2056
2081
|
}
|
|
2057
2082
|
|
|
2058
2083
|
pub fn check_template(template: &TemplateInput) -> BackendResult<()> {
|
|
@@ -2063,7 +2088,7 @@ pub fn format_template_with_profile(
|
|
|
2063
2088
|
template: &TemplateInput,
|
|
2064
2089
|
profile: YamlProfile,
|
|
2065
2090
|
) -> BackendResult<String> {
|
|
2066
|
-
let stream =
|
|
2091
|
+
let stream = parse_validated_template_with_profile(template, profile)?;
|
|
2067
2092
|
format_yaml_stream(template, &stream)
|
|
2068
2093
|
}
|
|
2069
2094
|
|
|
@@ -2071,6 +2096,74 @@ pub fn format_template(template: &TemplateInput) -> BackendResult<String> {
|
|
|
2071
2096
|
format_template_with_profile(template, YamlProfile::default())
|
|
2072
2097
|
}
|
|
2073
2098
|
|
|
2099
|
+
fn validate_template_stream(stream: &YamlStreamNode) -> BackendResult<()> {
|
|
2100
|
+
for document in &stream.documents {
|
|
2101
|
+
validate_value_node(&document.value)?;
|
|
2102
|
+
}
|
|
2103
|
+
Ok(())
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
fn validate_value_node(node: &YamlValueNode) -> BackendResult<()> {
|
|
2107
|
+
match node {
|
|
2108
|
+
YamlValueNode::Scalar(YamlScalarNode::Plain(node)) => validate_plain_scalar_node(node),
|
|
2109
|
+
YamlValueNode::Mapping(node) => {
|
|
2110
|
+
for entry in &node.entries {
|
|
2111
|
+
validate_key_node(&entry.key)?;
|
|
2112
|
+
validate_value_node(&entry.value)?;
|
|
2113
|
+
}
|
|
2114
|
+
Ok(())
|
|
2115
|
+
}
|
|
2116
|
+
YamlValueNode::Sequence(node) => {
|
|
2117
|
+
for item in &node.items {
|
|
2118
|
+
validate_value_node(item)?;
|
|
2119
|
+
}
|
|
2120
|
+
Ok(())
|
|
2121
|
+
}
|
|
2122
|
+
YamlValueNode::Decorated(node) => validate_value_node(&node.value),
|
|
2123
|
+
YamlValueNode::Interpolation(_)
|
|
2124
|
+
| YamlValueNode::Scalar(
|
|
2125
|
+
YamlScalarNode::DoubleQuoted(_)
|
|
2126
|
+
| YamlScalarNode::SingleQuoted(_)
|
|
2127
|
+
| YamlScalarNode::Block(_)
|
|
2128
|
+
| YamlScalarNode::Alias(_),
|
|
2129
|
+
) => Ok(()),
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
fn validate_key_node(node: &YamlKeyNode) -> BackendResult<()> {
|
|
2134
|
+
match &node.value {
|
|
2135
|
+
YamlKeyValue::Scalar(YamlScalarNode::Plain(node)) => validate_plain_scalar_node(node),
|
|
2136
|
+
YamlKeyValue::Complex(node) => validate_value_node(node),
|
|
2137
|
+
YamlKeyValue::Interpolation(_)
|
|
2138
|
+
| YamlKeyValue::Scalar(
|
|
2139
|
+
YamlScalarNode::DoubleQuoted(_)
|
|
2140
|
+
| YamlScalarNode::SingleQuoted(_)
|
|
2141
|
+
| YamlScalarNode::Block(_)
|
|
2142
|
+
| YamlScalarNode::Alias(_),
|
|
2143
|
+
) => Ok(()),
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
fn validate_plain_scalar_node(node: &YamlPlainScalarNode) -> BackendResult<()> {
|
|
2148
|
+
let has_interpolation = node
|
|
2149
|
+
.chunks
|
|
2150
|
+
.iter()
|
|
2151
|
+
.any(|chunk| matches!(chunk, YamlChunk::Interpolation(_)));
|
|
2152
|
+
let has_whitespace_text = node.chunks.iter().any(|chunk| {
|
|
2153
|
+
matches!(chunk, YamlChunk::Text(text) if text.value.chars().any(char::is_whitespace))
|
|
2154
|
+
});
|
|
2155
|
+
|
|
2156
|
+
if has_interpolation && has_whitespace_text {
|
|
2157
|
+
return Err(BackendError::parse_at(
|
|
2158
|
+
"yaml.parse",
|
|
2159
|
+
"Quote YAML plain scalars that mix whitespace and interpolations.",
|
|
2160
|
+
Some(node.span.clone()),
|
|
2161
|
+
));
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
Ok(())
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2074
2167
|
pub fn normalize_documents_with_profile(
|
|
2075
2168
|
documents: &[YamlOwned],
|
|
2076
2169
|
_profile: YamlProfile,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
use tstring_syntax::{TemplateInput, TemplateInterpolation, TemplateSegment};
|
|
2
|
-
use tstring_yaml::{
|
|
2
|
+
use tstring_yaml::{
|
|
3
|
+
YamlValueNode, check_template, format_template, parse_template, validate_template,
|
|
4
|
+
};
|
|
3
5
|
|
|
4
6
|
fn interpolation(index: usize, expression: &str) -> TemplateSegment {
|
|
5
7
|
TemplateSegment::Interpolation(TemplateInterpolation {
|
|
@@ -62,6 +64,71 @@ fn checks_valid_yaml_templates() {
|
|
|
62
64
|
check_template(&template).expect("expected check success");
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
#[test]
|
|
68
|
+
fn rejects_plain_scalars_that_mix_whitespace_and_interpolation() {
|
|
69
|
+
let template = TemplateInput::from_segments(vec![
|
|
70
|
+
TemplateSegment::StaticText("replicas: fdsa fff fds".to_owned()),
|
|
71
|
+
TemplateSegment::Interpolation(TemplateInterpolation {
|
|
72
|
+
expression: "replicas".to_owned(),
|
|
73
|
+
conversion: None,
|
|
74
|
+
format_spec: String::new(),
|
|
75
|
+
interpolation_index: 0,
|
|
76
|
+
raw_source: Some("{replicas}".to_owned()),
|
|
77
|
+
}),
|
|
78
|
+
TemplateSegment::StaticText("\n".to_owned()),
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
let error = check_template(&template).expect_err("expected YAML validation failure");
|
|
82
|
+
assert_eq!(error.diagnostics[0].code, "yaml.parse");
|
|
83
|
+
assert!(
|
|
84
|
+
error
|
|
85
|
+
.message
|
|
86
|
+
.contains("Quote YAML plain scalars that mix whitespace and interpolations")
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[test]
|
|
91
|
+
fn validates_token_like_plain_scalars_with_interpolation() {
|
|
92
|
+
let template = TemplateInput::from_segments(vec![
|
|
93
|
+
TemplateSegment::StaticText("plain: item-".to_owned()),
|
|
94
|
+
TemplateSegment::Interpolation(TemplateInterpolation {
|
|
95
|
+
expression: "user".to_owned(),
|
|
96
|
+
conversion: None,
|
|
97
|
+
format_spec: String::new(),
|
|
98
|
+
interpolation_index: 0,
|
|
99
|
+
raw_source: Some("{user}".to_owned()),
|
|
100
|
+
}),
|
|
101
|
+
TemplateSegment::StaticText("\n".to_owned()),
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
validate_template(&template).expect("expected YAML validation success");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#[test]
|
|
108
|
+
fn validates_plain_scalars_with_multiple_interpolations_and_no_whitespace() {
|
|
109
|
+
let template = TemplateInput::from_segments(vec![
|
|
110
|
+
TemplateSegment::StaticText("plain: ".to_owned()),
|
|
111
|
+
TemplateSegment::Interpolation(TemplateInterpolation {
|
|
112
|
+
expression: "foo".to_owned(),
|
|
113
|
+
conversion: None,
|
|
114
|
+
format_spec: String::new(),
|
|
115
|
+
interpolation_index: 0,
|
|
116
|
+
raw_source: Some("{foo}".to_owned()),
|
|
117
|
+
}),
|
|
118
|
+
TemplateSegment::StaticText("-".to_owned()),
|
|
119
|
+
TemplateSegment::Interpolation(TemplateInterpolation {
|
|
120
|
+
expression: "bar".to_owned(),
|
|
121
|
+
conversion: None,
|
|
122
|
+
format_spec: String::new(),
|
|
123
|
+
interpolation_index: 1,
|
|
124
|
+
raw_source: Some("{bar}".to_owned()),
|
|
125
|
+
}),
|
|
126
|
+
TemplateSegment::StaticText("\n".to_owned()),
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
validate_template(&template).expect("expected YAML validation success");
|
|
130
|
+
}
|
|
131
|
+
|
|
65
132
|
#[test]
|
|
66
133
|
fn formats_yaml_templates_with_raw_interpolations() {
|
|
67
134
|
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
|
{tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/tstring-pyo3-bindings/benches/render_paths.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tstring_bindings-0.1.1 → tstring_bindings-0.2.1}/yaml-tstring-rs/test-support/renderer_layout.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|