diff --git a/Cargo.lock b/Cargo.lock index 70358ec..a1b8287 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,19 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -40,6 +46,18 @@ version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "0.1.10" @@ -52,6 +70,33 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" version = "4.0.15" @@ -133,6 +178,85 @@ dependencies = [ "libc", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -153,6 +277,33 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -163,6 +314,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hashbrown" version = "0.12.3" @@ -184,6 +341,21 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "indexmap" version = "1.9.1" @@ -194,6 +366,38 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.4" @@ -217,9 +421,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.135" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libcorn" @@ -227,11 +431,13 @@ version = "0.7.0" dependencies = [ "cfg-if 1.0.0", "console_error_panic_hook", + "criterion", "paste", "pest", "pest_derive", "serde", "serde-wasm-bindgen", + "serde_bytes", "serde_json", "serde_yaml", "thiserror", @@ -241,6 +447,12 @@ dependencies = [ "wee_alloc", ] +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "log" version = "0.4.17" @@ -256,18 +468,52 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "memory_units" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + [[package]] name = "once_cell" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "os_str_bytes" version = "6.3.0" @@ -324,6 +570,34 @@ dependencies = [ "sha2", ] +[[package]] +name = "plotters" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-svg" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +dependencies = [ + "plotters-backend", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -366,18 +640,84 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.145" @@ -389,15 +729,24 @@ dependencies = [ [[package]] name = "serde-wasm-bindgen" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" dependencies = [ "js-sys", "serde", "wasm-bindgen", ] +[[package]] +name = "serde_bytes" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.145" @@ -510,6 +859,16 @@ dependencies = [ "syn 2.0.16", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "toml" version = "0.7.4" @@ -574,6 +933,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasm-bindgen" version = "0.2.83" @@ -717,6 +1086,72 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winnow" version = "0.4.6" diff --git a/assets/inputs/string.corn b/assets/inputs/string.corn index b675ce0..d704205 100644 --- a/assets/inputs/string.corn +++ b/assets/inputs/string.corn @@ -1,3 +1,6 @@ { foo = "bar" + bar = "\"\\\n\r\t" + baz = "\u0061" + qux = "" } \ No newline at end of file diff --git a/assets/inputs/string_interpolation.corn b/assets/inputs/string_interpolation.corn new file mode 100644 index 0000000..b45b87d --- /dev/null +++ b/assets/inputs/string_interpolation.corn @@ -0,0 +1,6 @@ +let { + $greeting = "hello" + $subject = "world" +} in { + foo = "$greeting, $subject" +} \ No newline at end of file diff --git a/assets/outputs/json/complex.json b/assets/outputs/json/complex.json index c8c6bd1..83da4fd 100644 --- a/assets/outputs/json/complex.json +++ b/assets/outputs/json/complex.json @@ -25,7 +25,7 @@ "gender": "M", "name": { "first": "John", - "full": "$firstName $lastName", + "full": "John Smith", "last": "Smith" }, "negative": { diff --git a/assets/outputs/json/string.json b/assets/outputs/json/string.json index c8c4105..2f2ff86 100644 --- a/assets/outputs/json/string.json +++ b/assets/outputs/json/string.json @@ -1,3 +1,6 @@ { - "foo": "bar" + "bar": "\"\\\n\r\t", + "baz": "a", + "foo": "bar", + "qux": "" } diff --git a/assets/outputs/json/string_interpolation.json b/assets/outputs/json/string_interpolation.json new file mode 100644 index 0000000..e92af90 --- /dev/null +++ b/assets/outputs/json/string_interpolation.json @@ -0,0 +1,3 @@ +{ + "foo": "hello, world" +} diff --git a/assets/outputs/toml/complex.toml b/assets/outputs/toml/complex.toml index c4d9a15..5386b84 100644 --- a/assets/outputs/toml/complex.toml +++ b/assets/outputs/toml/complex.toml @@ -20,7 +20,7 @@ sinceYear = 2019 [name] first = "John" -full = "$firstName $lastName" +full = "John Smith" last = "Smith" [negative] diff --git a/assets/outputs/toml/string.toml b/assets/outputs/toml/string.toml index ff8cd1f..4b05266 100644 --- a/assets/outputs/toml/string.toml +++ b/assets/outputs/toml/string.toml @@ -1,2 +1,7 @@ +bar = """ +\"\\ +\r\t""" +baz = "a" foo = "bar" +qux = "" diff --git a/assets/outputs/toml/string_interpolation.toml b/assets/outputs/toml/string_interpolation.toml new file mode 100644 index 0000000..dac015a --- /dev/null +++ b/assets/outputs/toml/string_interpolation.toml @@ -0,0 +1,2 @@ +foo = "hello, world" + diff --git a/assets/outputs/yaml/complex.yml b/assets/outputs/yaml/complex.yml index ee8e57c..41c05ca 100644 --- a/assets/outputs/yaml/complex.yml +++ b/assets/outputs/yaml/complex.yml @@ -19,7 +19,7 @@ favourites: gender: M name: first: John - full: $firstName $lastName + full: John Smith last: Smith negative: float: -34.34 diff --git a/assets/outputs/yaml/string.yml b/assets/outputs/yaml/string.yml index 8218b15..20dd01a 100644 --- a/assets/outputs/yaml/string.yml +++ b/assets/outputs/yaml/string.yml @@ -1,2 +1,5 @@ +bar: "\"\\\n\r\t" +baz: a foo: bar +qux: '' diff --git a/assets/outputs/yaml/string_interpolation.yml b/assets/outputs/yaml/string_interpolation.yml new file mode 100644 index 0000000..be478b0 --- /dev/null +++ b/assets/outputs/yaml/string_interpolation.yml @@ -0,0 +1,2 @@ +foo: hello, world + diff --git a/corn-cli/src/error.rs b/corn-cli/src/error.rs index 649a3ab..0fe5b22 100644 --- a/corn-cli/src/error.rs +++ b/corn-cli/src/error.rs @@ -25,6 +25,7 @@ impl ExitCode for CornError { CornError::InputResolveError(_) => 2, CornError::InvalidPathError(_) => 6, CornError::InvalidSpreadError(_) => 7, + CornError::InvalidInterpolationError(_) => 8, CornError::DeserializationError(_) => 5, } } diff --git a/libcorn/Cargo.toml b/libcorn/Cargo.toml index 0ef2d33..ab7cfec 100644 --- a/libcorn/Cargo.toml +++ b/libcorn/Cargo.toml @@ -20,7 +20,7 @@ cfg-if = "1.0.0" serde = { version = "1.0.133", features = ["derive"] } thiserror = "1.0.40" wasm-bindgen = { version = "0.2.83", optional = true } -serde-wasm-bindgen = { version = "0.4.5", optional = true } +serde-wasm-bindgen = { version = "0.5.0", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } wee_alloc = { version = "0.4.5", optional = true } @@ -30,4 +30,5 @@ wasm-bindgen-test = { version = "0.3.29" } # required for testing serde_json = "1.0.75" serde_yaml = "0.9.11" +serde_bytes = "0.11.9" toml = "0.7.4" diff --git a/libcorn/src/de.rs b/libcorn/src/de.rs index 1034b79..8f36606 100644 --- a/libcorn/src/de.rs +++ b/libcorn/src/de.rs @@ -1,7 +1,7 @@ +use std::borrow::Cow; use std::collections::VecDeque; -use serde::de::{DeserializeSeed, EnumAccess, IntoDeserializer, VariantAccess, Visitor}; -use serde::{de, Deserialize}; +use serde::de::{self, DeserializeSeed, EnumAccess, IntoDeserializer, VariantAccess, Visitor}; use crate::error::{Error, Result}; use crate::parse; @@ -29,9 +29,9 @@ impl<'de> Deserializer<'de> { /// # Errors /// /// Will return a `DeserializationError` if the config is invalid. -pub fn from_str<'de, T>(s: &'de str) -> Result +pub fn from_str(s: &str) -> Result where - T: Deserialize<'de>, + T: de::DeserializeOwned, { let mut deserializer = Deserializer::from_str(s)?; T::deserialize(&mut deserializer) @@ -42,9 +42,9 @@ where /// # Errors /// /// Will return a `DeserializationError` if the config is invalid. -pub fn from_slice<'de, T>(bytes: &'de [u8]) -> Result +pub fn from_slice(bytes: &[u8]) -> Result where - T: de::Deserialize<'de>, + T: de::DeserializeOwned, { match std::str::from_utf8(bytes) { Ok(s) => from_str(s), @@ -99,8 +99,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { let seq = Seq::new(value); visitor.visit_seq(seq) } - Value::String(val) => visitor.visit_borrowed_str(val), - Value::EnvString(val) => visitor.visit_string(val), + Value::String(val) => visitor.visit_str(&val), Value::Integer(val) => visitor.visit_i64(val), Value::Float(val) => visitor.visit_f64(val), Value::Boolean(val) => visitor.visit_bool(val), @@ -192,7 +191,6 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { let value = get_value!(self); let char = match value { Value::String(value) => value.chars().next(), - Value::EnvString(value) => value.chars().next(), _ => return err_expected!("char", value), }; @@ -207,8 +205,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { match_value!(self, "string", - Value::String(val) => visitor.visit_borrowed_str(val) - Value::EnvString(val) => visitor.visit_string(val) + Value::String(val) => visitor.visit_str(&val) ) } @@ -224,8 +221,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { match_value!(self, "bytes array", - Value::String(val) => visitor.visit_borrowed_bytes(val.as_bytes()) - Value::EnvString(val) => visitor.visit_bytes(val.as_bytes()) + Value::String(val) => visitor.visit_bytes(val.as_bytes()) ) } @@ -346,7 +342,6 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { match value { Value::Object(_) => visitor.visit_enum(Enum::new(value)), Value::String(val) => visitor.visit_enum(val.into_deserializer()), - Value::EnvString(val) => visitor.visit_enum(val.into_deserializer()), _ => err_expected!("object or string (enum variant)", value), } } @@ -376,7 +371,7 @@ impl<'de> Map<'de> { Value::Object(values) => Self { values: values .into_iter() - .flat_map(|(key, value)| vec![Value::String(key), value]) + .flat_map(|(key, value)| vec![Value::String(Cow::Borrowed(key)), value]) .collect(), }, _ => unreachable!(), @@ -473,15 +468,15 @@ impl<'de> EnumAccess<'de> for Enum<'de> { V: DeserializeSeed<'de>, { match self.value { - Value::String(_) | Value::EnvString(_) => { + Value::String(_) => { let value = seed.deserialize(&mut Deserializer::from_value(self.value))?; Ok((value, Variant::new(None))) } Value::Object(obj) => { let first_pair = obj.into_iter().next(); if let Some(first_pair) = first_pair { - let tag = seed - .deserialize(&mut Deserializer::from_value(Value::String(first_pair.0)))?; + let value = Value::String(Cow::Borrowed(first_pair.0)); + let tag = seed.deserialize(&mut Deserializer::from_value(value))?; Ok((tag, Variant::new(Some(first_pair.1)))) } else { Err(Error::DeserializationError( diff --git a/libcorn/src/error.rs b/libcorn/src/error.rs index 88736eb..641b9cb 100644 --- a/libcorn/src/error.rs +++ b/libcorn/src/error.rs @@ -22,6 +22,9 @@ pub enum Error { #[error("attempted to spread a type that differs from its containing type")] InvalidSpreadError(String), + #[error("attempted to interpolate a non-string type into a string")] + InvalidInterpolationError(String), + #[error("failed to deserialize input")] DeserializationError(String), } diff --git a/libcorn/src/grammar.pest b/libcorn/src/grammar.pest index 5ed3e73..7a40ba4 100644 --- a/libcorn/src/grammar.pest +++ b/libcorn/src/grammar.pest @@ -45,12 +45,11 @@ string = ${ "\"" ~ string_val ~ "\"" } -string_val = ${ char* } +string_val = ${ (input | char)* } char = { -// input !("\"" | "\\") ~ ANY - | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("\"" | "\\" | "n" | "r" | "t") | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) } diff --git a/libcorn/src/lib.rs b/libcorn/src/lib.rs index 428951e..3f41723 100644 --- a/libcorn/src/lib.rs +++ b/libcorn/src/lib.rs @@ -1,4 +1,5 @@ use serde::Serialize; +use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; use std::fmt::{Display, Formatter}; @@ -23,10 +24,8 @@ pub enum Value<'a> { Object(BTreeMap<&'a str, Value<'a>>), /// Array of values, can be mixed types. Array(Vec>), - /// Borrowed string, from string literal or input. - String(&'a str), - /// Owned string, originating from an environment variable. - EnvString(String), + /// UTF-8 string + String(Cow<'a, str>), /// 64-bit signed integer. Integer(i64), /// 64-bit (double precision) floating point number. @@ -46,7 +45,6 @@ impl<'a> Display for Value<'a> { Value::Object(_) => "object", Value::Array(_) => "array", Value::String(_) => "string", - Value::EnvString(_) => "string (from environment variable)", Value::Integer(_) => "integer", Value::Float(_) => "float", Value::Boolean(_) => "boolean", diff --git a/libcorn/src/parser.rs b/libcorn/src/parser.rs index 44f7228..334f7de 100644 --- a/libcorn/src/parser.rs +++ b/libcorn/src/parser.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; use std::env::var; use std::fmt::Formatter; @@ -46,7 +47,7 @@ impl<'a> CornParser<'a> { match pair.as_rule() { Rule::object => Ok(Value::Object(self.parse_object(pair)?)), Rule::array => Ok(Value::Array(self.parse_array(pair)?)), - Rule::string => Ok(Value::String(Self::parse_string(pair))), + Rule::string => Ok(Value::String(self.parse_string(pair)?)), Rule::integer => Ok(Value::Integer(Self::parse_integer(pair))), Rule::float => Ok(Value::Float(Self::parse_float(&pair))), Rule::boolean => Ok(Value::Boolean(Self::parse_bool(&pair))), @@ -96,12 +97,59 @@ impl<'a> CornParser<'a> { /// Collects each `char` in a `Rule::string` /// to form a single `String`. - fn parse_string(pair: Pair<'a, Rule>) -> &'a str { + fn parse_string(&self, pair: Pair<'a, Rule>) -> Result> { assert_eq!(pair.as_rule(), Rule::string); - pair.into_inner() + + let mut full_string = String::new(); + + let pairs = pair + .into_inner() .next() .expect("string rules should contain a valid string value") - .as_str() + .into_inner(); + + for pair in pairs { + match pair.as_rule() { + Rule::char => full_string.push(Self::parse_char(&pair)), + Rule::input => { + let input_name = pair.as_str(); + let value = self.get_input(input_name)?; + match value { + Value::String(value) => full_string.push_str(&value), + _ => return Err(Error::InvalidInterpolationError(input_name.to_string())), + } + } + _ => unreachable!(), + }; + } + + Ok(Cow::Owned(full_string)) + } + + fn parse_char(pair: &Pair<'a, Rule>) -> char { + let str = pair.as_str(); + let mut chars = str.chars(); + + let first_char = chars.next().expect("character to exist"); + if first_char != '\\' { + return first_char; + } + + let second_char = chars.next().expect("character to exist"); + if second_char != 'u' { + return match second_char { + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + '"' => '\"', + '\\' => '\\', + _ => unreachable!(), + }; + } + + let num = + u32::from_str_radix(&str[3..], 16).expect("valid hex characters to exist after \\u"); + char::from_u32(num).unwrap_or('\u{FFFD}') } /// Parses each rule in a `Rule::array` @@ -253,7 +301,7 @@ impl<'a> CornParser<'a> { let var = var(env_name); if let Ok(var) = var { - return Ok(Value::EnvString(var)); + return Ok(Value::String(Cow::Owned(var))); } } diff --git a/libcorn/tests/de_tests.rs b/libcorn/tests/de_tests.rs index 11b5c4f..e8156c3 100644 --- a/libcorn/tests/de_tests.rs +++ b/libcorn/tests/de_tests.rs @@ -69,8 +69,9 @@ struct Boolean { } #[derive(Deserialize, Debug, PartialEq)] -struct Bytes<'a> { - foo: &'a [u8], +struct Bytes { + #[serde(with = "serde_bytes")] + foo: Vec, } #[derive(Deserialize, Debug, PartialEq)] @@ -263,6 +264,7 @@ struct ComplexKeysFoo { #[derive(Deserialize, Debug, PartialEq)] struct Float { foo: f64, + bar: f64, } #[derive(Deserialize, Debug, PartialEq)] @@ -357,6 +359,14 @@ struct ReadmeExample { version: String, } +#[derive(Deserialize, Debug, PartialEq)] +struct String_ { + foo: String, + bar: String, + baz: String, + qux: String, +} + #[derive(Deserialize, Debug, PartialEq)] struct ReadmeExampleAuthor { email: String, @@ -397,11 +407,6 @@ struct ReadmeExampleScripts { run: String, } -#[derive(Deserialize, Debug, PartialEq)] -struct Str<'a> { - foo: &'a str, -} - #[derive(Deserialize, Debug, PartialEq)] struct ValueAfterTable { foo: Empty, @@ -430,7 +435,8 @@ generate_eq_tests!( (object, Object), (object_in_array, ObjectInArray), (readme_example, ReadmeExample), - (string, Basic), + (string, String_), + (string_interpolation, Basic), (value_after_table, ValueAfterTable), (very_compact, Compact) ); @@ -548,17 +554,3 @@ fn null_unit() { assert_eq!(config, json_config); } - -#[test] -fn str() { - let test_name = "string"; - - let input = fs::read_to_string(format!("../assets/inputs/{test_name}.corn")).unwrap(); - let config = from_str::(&input).unwrap(); - - let json_input = - fs::read_to_string(format!("../assets/outputs/json/{test_name}.json")).unwrap(); - let json_config = serde_json::from_str(&json_input).unwrap(); - - assert_eq!(config, json_config); -} diff --git a/libcorn/tests/parser_tests.rs b/libcorn/tests/parser_tests.rs index 929f93c..7556c3c 100644 --- a/libcorn/tests/parser_tests.rs +++ b/libcorn/tests/parser_tests.rs @@ -92,6 +92,7 @@ generate_eq_tests!( readme_example, spread, string, + string_interpolation, value_after_table, very_compact );