diff --git a/Cargo.lock b/Cargo.lock index b58948ca..d18f0389 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -16,24 +16,24 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "annotate-snippets" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a433302f833baa830c0092100c481c7ea768c5981a3c36f549517a502f246dd" +checksum = "6d9b665789884a7e8fb06c84b295e923b03ca51edbb7d08f91a6a50322ecbfe6" dependencies = [ "anstyle", "unicode-width", @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -89,15 +89,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "base64" @@ -122,15 +122,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -143,18 +137,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c764d619ca78fccbf3069b37bd7af92577f044bb15236036662d79b6559f25b7" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" @@ -164,9 +155,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.1" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -174,9 +165,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -186,23 +177,23 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c" +checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.60", ] [[package]] @@ -283,7 +274,7 @@ checksum = "b569f4e3085ae957ecc37588e6b2227791b72745434eae966db29e122ba27f0d" dependencies = [ "anyhow", "bumpalo", - "indexmap 2.2.3", + "indexmap 2.2.6", "rustc-hash", "serde", "unicode-width", @@ -297,9 +288,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encode_unicode" @@ -325,9 +316,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fnv" @@ -347,9 +338,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -374,9 +365,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hi-doc" @@ -403,9 +394,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -414,21 +405,20 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "insta" -version = "1.35.1" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c985c1bef99cf13c58fade470483d81a2bfe846ebde60ed28cc2dddec2df9e2" +checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc" dependencies = [ "console", "lazy_static", "linked-hash-map", "similar", - "yaml-rust", ] [[package]] @@ -442,9 +432,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jrsonnet" @@ -550,7 +540,7 @@ version = "0.5.0-pre96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.60", ] [[package]] @@ -655,9 +645,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -684,7 +674,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax", - "syn 2.0.50", + "syn 2.0.60", ] [[package]] @@ -698,9 +688,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ "hashbrown 0.14.3", ] @@ -713,15 +703,15 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -783,9 +773,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -793,15 +783,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -812,9 +802,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "peg" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "400bcab7d219c38abf8bd7cc2054eb9bbbd4312d66f6a5557d572a203f646f61" +checksum = "8a625d12ad770914cbf7eff6f9314c3ef803bfe364a1b20bc36ddf56673e71e5" dependencies = [ "peg-macros", "peg-runtime", @@ -822,9 +812,9 @@ dependencies = [ [[package]] name = "peg-macros" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e61cce859b76d19090f62da50a9fe92bab7c2a5f09e183763559a2ac392c90" +checksum = "f241d42067ed3ab6a4fece1db720838e1418f36d868585a27931f95d6bc03582" dependencies = [ "peg-runtime", "proc-macro2", @@ -833,9 +823,9 @@ dependencies = [ [[package]] name = "peg-runtime" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36bae92c60fa2398ce4678b98b2c4b5a7c61099961ca1fa305aec04a9ad28922" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" [[package]] name = "ppv-lite86" @@ -845,18 +835,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -911,18 +901,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -932,9 +922,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -943,9 +933,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rowan" @@ -968,11 +958,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -993,29 +983,29 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -1068,9 +1058,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "smallvec" @@ -1086,9 +1076,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "structdump" @@ -1125,9 +1115,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -1136,9 +1126,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", @@ -1164,22 +1154,22 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.60", ] [[package]] @@ -1202,9 +1192,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "utf8parse" @@ -1230,144 +1220,94 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "xshell" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad" +checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437" dependencies = [ "xshell-macros", ] [[package]] name = "xshell-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e" +checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852" [[package]] name = "xtask" version = "0.1.0" dependencies = [ "anyhow", - "indexmap 2.2.3", + "indexmap 2.2.6", "itertools", "proc-macro2", "quote", @@ -1401,5 +1341,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.60", ] diff --git a/crates/jrsonnet-cli/src/manifest.rs b/crates/jrsonnet-cli/src/manifest.rs index 0324f8b5..ecce4812 100644 --- a/crates/jrsonnet-cli/src/manifest.rs +++ b/crates/jrsonnet-cli/src/manifest.rs @@ -4,7 +4,7 @@ use clap::{Parser, ValueEnum}; use jrsonnet_evaluator::manifest::{ JsonFormat, ManifestFormat, StringFormat, ToStringFormat, YamlStreamFormat, }; -use jrsonnet_stdlib::{TomlFormat, YamlFormat}; +use jrsonnet_stdlib::{TomlFormat, XmlJsonmlFormat, YamlFormat}; #[derive(Clone, Copy, ValueEnum)] pub enum ManifestFormatName { @@ -13,6 +13,7 @@ pub enum ManifestFormatName { Json, Yaml, Toml, + XmlJsonml, } #[derive(Parser)] @@ -70,10 +71,11 @@ impl ManifestOpts { #[cfg(feature = "exp-preserve-order")] preserve_order, )), + ManifestFormatName::XmlJsonml => Box::new(XmlJsonmlFormat::cli()), } }; if self.yaml_stream { - Box::new(YamlStreamFormat(format)) + Box::new(YamlStreamFormat::cli(format)) } else { format } diff --git a/crates/jrsonnet-evaluator/src/arr/mod.rs b/crates/jrsonnet-evaluator/src/arr/mod.rs index b83f85c8..dc1fe794 100644 --- a/crates/jrsonnet-evaluator/src/arr/mod.rs +++ b/crates/jrsonnet-evaluator/src/arr/mod.rs @@ -1,4 +1,7 @@ -use std::any::Any; +use std::{ + any::Any, + num::{NonZeroU32, NonZeroUsize}, +}; use jrsonnet_gcmodule::{Cc, Trace}; use jrsonnet_interner::IBytes; @@ -100,28 +103,29 @@ impl ArrValue { Self::new(RangeArray::new_inclusive(a, b)) } + /// # Panics + /// If step == 0 #[must_use] - pub fn slice( - self, - from: Option, - to: Option, - step: Option, - ) -> Option { - let len = self.len(); - let from = from.unwrap_or(0); - let to = to.unwrap_or(len).min(len); - let step = step.unwrap_or(1); - - if from >= to || step == 0 { - return None; + pub fn slice(self, index: Option, end: Option, step: Option) -> Self { + let get_idx = |pos: Option, len: usize, default| match pos { + Some(v) if v < 0 => len.saturating_sub((-v) as usize), + Some(v) => (v as usize).min(len), + None => default, + }; + let index = get_idx(index, self.len(), 0); + let end = get_idx(end, self.len(), self.len()); + let step = step.unwrap_or_else(|| NonZeroU32::new(1).expect("1 != 0")); + + if index >= end { + return Self::empty(); } - Some(Self::new(SliceArray { + Self::new(SliceArray { inner: self, - from: from as u32, - to: to as u32, - step: step as u32, - })) + from: index as u32, + to: end as u32, + step: step.get(), + }) } /// Array length. diff --git a/crates/jrsonnet-evaluator/src/manifest.rs b/crates/jrsonnet-evaluator/src/manifest.rs index be525ac2..489cfb04 100644 --- a/crates/jrsonnet-evaluator/src/manifest.rs +++ b/crates/jrsonnet-evaluator/src/manifest.rs @@ -340,7 +340,28 @@ impl ManifestFormat for StringFormat { } } -pub struct YamlStreamFormat(pub I); +pub struct YamlStreamFormat { + inner: I, + c_document_end: bool, + end_newline: bool, +} +impl YamlStreamFormat { + pub fn std_yaml_stream(inner: I, c_document_end: bool) -> Self { + Self { + inner, + c_document_end, + // Stdlib format always inserts newline at the end + end_newline: true, + } + } + pub fn cli(inner: I) -> Self { + Self { + inner, + c_document_end: true, + end_newline: false, + } + } +} impl ManifestFormat for YamlStreamFormat { fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> { let Val::Arr(arr) = val else { @@ -353,11 +374,16 @@ impl ManifestFormat for YamlStreamFormat { for v in arr.iter() { let v = v?; out.push_str("---\n"); - self.0.manifest_buf(v, out)?; + self.inner.manifest_buf(v, out)?; out.push('\n'); } + } + if self.c_document_end { out.push_str("..."); } + if self.end_newline { + out.push('\n'); + } Ok(()) } } diff --git a/crates/jrsonnet-evaluator/src/val.rs b/crates/jrsonnet-evaluator/src/val.rs index daea1c4a..d0a42cc1 100644 --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -2,6 +2,7 @@ use std::{ cell::RefCell, fmt::{self, Debug, Display}, mem::replace, + num::{NonZeroU32, NonZeroUsize}, rc::Rc, }; @@ -277,26 +278,11 @@ impl IndexableVal { .into(), )) } - Self::Arr(arr) => { - let get_idx = |pos: Option, len: usize, default| match pos { - Some(v) if v < 0 => len.saturating_sub((-v) as usize), - Some(v) => (v as usize).min(len), - None => default, - }; - let index = get_idx(index, arr.len(), 0); - let end = get_idx(end, arr.len(), arr.len()); - let step = step.as_deref().copied().unwrap_or(1); - - if index >= end { - return Ok(Self::Arr(ArrValue::empty())); - } - - Ok(Self::Arr( - arr.clone() - .slice(Some(index), Some(end), Some(step)) - .expect("arguments checked"), - )) - } + Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice( + index, + end, + step.map(|v| NonZeroU32::new(v.value() as u32).expect("bounded != 0")), + ))), } } } diff --git a/crates/jrsonnet-macros/src/lib.rs b/crates/jrsonnet-macros/src/lib.rs index 0880bf6f..6fa9a349 100644 --- a/crates/jrsonnet-macros/src/lib.rs +++ b/crates/jrsonnet-macros/src/lib.rs @@ -34,6 +34,12 @@ where Ok(Some(attr)) } +fn remove_attr(attrs: &mut Vec, ident: I) +where + Ident: PartialEq, +{ + attrs.retain(|a| !a.path().is_ident(&ident)); +} fn path_is(path: &Path, needed: &str) -> bool { path.leading_colon.is_none() @@ -121,10 +127,21 @@ impl Parse for BuiltinAttrs { } } +enum Optionality { + Required, + Optional, + Default(Expr), +} +impl Optionality { + fn is_optional(&self) -> bool { + !matches!(self, Self::Required) + } +} + enum ArgInfo { Normal { ty: Box, - is_option: bool, + optionality: Optionality, name: Option, cfg_attrs: Vec, }, @@ -138,7 +155,7 @@ enum ArgInfo { } impl ArgInfo { - fn parse(name: &str, arg: &FnArg) -> Result { + fn parse(name: &str, arg: &mut FnArg) -> Result { let FnArg::Typed(arg) = arg else { unreachable!() }; @@ -163,7 +180,10 @@ impl ArgInfo { _ => {} } - let (is_option, ty) = if let Some(ty) = extract_type_from_option(ty)? { + let (optionality, ty) = if let Some(default) = parse_attr::<_, _>(&arg.attrs, "default")? { + remove_attr(&mut arg.attrs, "default"); + (Optionality::Default(default), ty.clone()) + } else if let Some(ty) = extract_type_from_option(ty)? { if type_is_path(ty, "Thunk").is_some() { return Ok(Self::Lazy { is_option: true, @@ -171,9 +191,9 @@ impl ArgInfo { }); } - (true, Box::new(ty.clone())) + (Optionality::Optional, Box::new(ty.clone())) } else { - (false, ty.clone()) + (Optionality::Required, ty.clone()) }; let cfg_attrs = arg @@ -185,7 +205,7 @@ impl ArgInfo { Ok(Self::Normal { ty, - is_option, + optionality, name: ident.map(|v| v.to_string()), cfg_attrs, }) @@ -201,18 +221,14 @@ pub fn builtin( let item_fn = item.clone(); let item_fn: ItemFn = parse_macro_input!(item_fn); - match builtin_inner(attr, item_fn, item.into()) { + match builtin_inner(attr, item_fn) { Ok(v) => v.into(), Err(e) => e.into_compile_error().into(), } } #[allow(clippy::too_many_lines)] -fn builtin_inner( - attr: BuiltinAttrs, - fun: ItemFn, - item: proc_macro2::TokenStream, -) -> syn::Result { +fn builtin_inner(attr: BuiltinAttrs, mut fun: ItemFn) -> syn::Result { let ReturnType::Type(_, result) = &fun.sig.output else { return Err(Error::new( fun.sig.span(), @@ -224,13 +240,13 @@ fn builtin_inner( let args = fun .sig .inputs - .iter() + .iter_mut() .map(|arg| ArgInfo::parse(&name, arg)) .collect::>>()?; let params_desc = args.iter().filter_map(|a| match a { ArgInfo::Normal { - is_option, + optionality, name, cfg_attrs, .. @@ -238,9 +254,10 @@ fn builtin_inner( let name = name .as_ref() .map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)}); + let is_optional = optionality.is_optional(); Some(quote! { #(#cfg_attrs)* - BuiltinParam::new(#name, #is_option), + BuiltinParam::new(#name, #is_optional), }) } ArgInfo::Lazy { is_option, name } => { @@ -270,7 +287,7 @@ fn builtin_inner( .map(|(id, a)| match a { ArgInfo::Normal { ty, - is_option, + optionality, name, cfg_attrs, } => { @@ -279,17 +296,22 @@ fn builtin_inner( || format!("argument <{}> evaluation", #name), || <#ty>::from_untyped(value.evaluate()?), )?}; - let value = if *is_option { - quote! {if let Some(value) = &parsed[#id] { + let value = match optionality { + Optionality::Required => quote! {{ + let value = parsed[#id].as_ref().expect("args shape is checked"); + #eval + },}, + Optionality::Optional => quote! {if let Some(value) = &parsed[#id] { Some(#eval) } else { None - },} - } else { - quote! {{ - let value = parsed[#id].as_ref().expect("args shape is checked"); + },}, + Optionality::Default(expr) => quote! {if let Some(value) = &parsed[#id] { #eval - },} + } else { + let v: #ty = #expr; + v + },}, }; quote! { #(#cfg_attrs)* @@ -302,7 +324,7 @@ fn builtin_inner( Some(value.clone()) } else { None - }} + },} } else { quote! { parsed[#id].as_ref().expect("args shape is correct").clone(), @@ -343,7 +365,7 @@ fn builtin_inner( }; Ok(quote! { - #item + #fun #[doc(hidden)] #[allow(non_camel_case_types)] @@ -373,7 +395,7 @@ fn builtin_inner( fn params(&self) -> &[BuiltinParam] { PARAMS } - #[allow(unused_variable)] + #[allow(unused_variables)] fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result { let parsed = parse_builtin_call(ctx.clone(), &PARAMS, args, false)?; diff --git a/crates/jrsonnet-stdlib/src/arrays.rs b/crates/jrsonnet-stdlib/src/arrays.rs index 543fe1d3..08f0055d 100644 --- a/crates/jrsonnet-stdlib/src/arrays.rs +++ b/crates/jrsonnet-stdlib/src/arrays.rs @@ -265,21 +265,18 @@ pub fn builtin_avg(arr: Vec, onEmpty: Option>) -> Result { } #[builtin] -pub fn builtin_remove_at(arr: ArrValue, at: usize) -> Result { +pub fn builtin_remove_at(arr: ArrValue, at: i32) -> Result { let newArrLeft = arr.clone().slice(None, Some(at), None); let newArrRight = arr.slice(Some(at + 1), None, None); - Ok(ArrValue::extended( - newArrLeft.unwrap_or_else(ArrValue::empty), - newArrRight.unwrap_or_else(ArrValue::empty), - )) + Ok(ArrValue::extended(newArrLeft, newArrRight)) } #[builtin] pub fn builtin_remove(arr: ArrValue, elem: Val) -> Result { for (index, item) in arr.iter().enumerate() { if equals(&item?, &elem)? { - return builtin_remove_at(arr.clone(), index); + return builtin_remove_at(arr.clone(), index as i32); } } Ok(arr) @@ -325,7 +322,9 @@ pub fn builtin_flatten_deep_array(value: Val) -> Result> { #[builtin] pub fn builtin_prune( a: Val, - #[cfg(feature = "exp-preserve-order")] preserve_order: bool, + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> Result { fn is_content(val: &Val) -> bool { match val { diff --git a/crates/jrsonnet-stdlib/src/lib.rs b/crates/jrsonnet-stdlib/src/lib.rs index 70655833..00e55ad1 100644 --- a/crates/jrsonnet-stdlib/src/lib.rs +++ b/crates/jrsonnet-stdlib/src/lib.rs @@ -99,6 +99,7 @@ pub fn stdlib_uncached(settings: Rc>) -> ObjValue { ("sign", builtin_sign::INST), ("max", builtin_max::INST), ("min", builtin_min::INST), + ("clamp", builtin_clamp::INST), ("sum", builtin_sum::INST), ("modulo", builtin_modulo::INST), ("floor", builtin_floor::INST), @@ -160,11 +161,20 @@ pub fn stdlib_uncached(settings: Rc>) -> ObjValue { ("objectRemoveKey", builtin_object_remove_key::INST), // Manifest ("escapeStringJson", builtin_escape_string_json::INST), + ("escapeStringPython", builtin_escape_string_json::INST), + ("escapeStringXML", builtin_escape_string_xml::INST), ("manifestJsonEx", builtin_manifest_json_ex::INST), + ("manifestJson", builtin_manifest_json::INST), + ("manifestJsonMinified", builtin_manifest_json_minified::INST), ("manifestYamlDoc", builtin_manifest_yaml_doc::INST), + ("manifestYamlStream", builtin_manifest_yaml_stream::INST), ("manifestTomlEx", builtin_manifest_toml_ex::INST), + ("manifestToml", builtin_manifest_toml::INST), ("toString", builtin_to_string::INST), - // Parsing + ("manifestPython", builtin_manifest_python::INST), + ("manifestPythonVars", builtin_manifest_python_vars::INST), + ("manifestXmlJsonml", builtin_manifest_xml_jsonml::INST), + // Parse ("parseJson", builtin_parse_json::INST), ("parseYaml", builtin_parse_yaml::INST), // Strings @@ -172,9 +182,12 @@ pub fn stdlib_uncached(settings: Rc>) -> ObjValue { ("substr", builtin_substr::INST), ("char", builtin_char::INST), ("strReplace", builtin_str_replace::INST), + ("escapeStringBash", builtin_escape_string_bash::INST), + ("escapeStringDollars", builtin_escape_string_dollars::INST), ("isEmpty", builtin_is_empty::INST), ("equalsIgnoreCase", builtin_equals_ignore_case::INST), ("splitLimit", builtin_splitlimit::INST), + ("split", builtin_split::INST), ("asciiUpper", builtin_ascii_upper::INST), ("asciiLower", builtin_ascii_lower::INST), ("findSubstr", builtin_find_substr::INST), @@ -186,6 +199,7 @@ pub fn stdlib_uncached(settings: Rc>) -> ObjValue { ("stringChars", builtin_string_chars::INST), // Misc ("length", builtin_length::INST), + ("get", builtin_get::INST), ("startsWith", builtin_starts_with::INST), ("endsWith", builtin_ends_with::INST), // Sets diff --git a/crates/jrsonnet-stdlib/src/manifest/mod.rs b/crates/jrsonnet-stdlib/src/manifest/mod.rs index eb84ff20..7071dfa1 100644 --- a/crates/jrsonnet-stdlib/src/manifest/mod.rs +++ b/crates/jrsonnet-stdlib/src/manifest/mod.rs @@ -1,13 +1,17 @@ +mod python; mod toml; +mod xml; mod yaml; use jrsonnet_evaluator::{ function::builtin, - manifest::{escape_string_json, JsonFormat}, + manifest::{escape_string_json, JsonFormat, YamlStreamFormat}, IStr, ObjValue, Result, Val, }; +pub use python::{PythonFormat, PythonVarsFormat}; pub use toml::TomlFormat; pub use yaml::YamlFormat; +pub use xml::XmlJsonmlFormat; #[builtin] pub fn builtin_escape_string_json(str_: IStr) -> Result { @@ -17,51 +21,149 @@ pub fn builtin_escape_string_json(str_: IStr) -> Result { #[builtin] pub fn builtin_manifest_json_ex( value: Val, - indent: IStr, + indent: String, newline: Option, key_val_sep: Option, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> Result { let newline = newline.as_deref().unwrap_or("\n"); let key_val_sep = key_val_sep.as_deref().unwrap_or(": "); value.manifest(JsonFormat::std_to_json( - indent.to_string(), + indent, newline, key_val_sep, #[cfg(feature = "exp-preserve-order")] - preserve_order.unwrap_or(false), + preserve_order, + )) +} + +#[builtin] +pub fn builtin_manifest_json( + value: Val, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, +) -> Result { + builtin_manifest_json_ex( + value, + " ".to_owned(), + None, + None, + #[cfg(feature = "exp-preserve-order")] + preserve_order, + ) +} + +#[builtin] +pub fn builtin_manifest_json_minified( + value: Val, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, +) -> Result { + value.manifest(JsonFormat::minify( + #[cfg(feature = "exp-preserve-order")] + preserve_order, )) } #[builtin] pub fn builtin_manifest_yaml_doc( value: Val, - indent_array_in_object: Option, - quote_keys: Option, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + #[default(false)] indent_array_in_object: bool, + #[default(true)] quote_keys: bool, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> Result { value.manifest(YamlFormat::std_to_yaml( - indent_array_in_object.unwrap_or(false), - quote_keys.unwrap_or(true), + indent_array_in_object, + quote_keys, #[cfg(feature = "exp-preserve-order")] - preserve_order.unwrap_or(false), + preserve_order, + )) +} + +#[builtin] +pub fn builtin_manifest_yaml_stream( + value: Val, + #[default(false)] indent_array_in_object: bool, + #[default(true)] c_document_end: bool, + #[default(true)] quote_keys: bool, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, +) -> Result { + value.manifest(YamlStreamFormat::std_yaml_stream( + YamlFormat::std_to_yaml( + indent_array_in_object, + quote_keys, + #[cfg(feature = "exp-preserve-order")] + preserve_order, + ), + c_document_end, )) } #[builtin] pub fn builtin_manifest_toml_ex( value: ObjValue, - indent: IStr, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + indent: String, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> Result { Val::Obj(value).manifest(TomlFormat::std_to_toml( - indent.to_string(), + indent, #[cfg(feature = "exp-preserve-order")] - preserve_order.unwrap_or(false), + preserve_order, )) } +#[builtin] +pub fn builtin_manifest_toml( + value: ObjValue, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, +) -> Result { + builtin_manifest_toml_ex( + value, + " ".to_owned(), + #[cfg(feature = "exp-preserve-order")] + preserve_order, + ) +} + #[builtin] pub fn builtin_to_string(a: Val) -> Result { a.to_string() } + +#[builtin] +pub fn builtin_manifest_python(v: Val) -> Result { + v.manifest(PythonFormat {}) +} +#[builtin] +pub fn builtin_manifest_python_vars(v: Val) -> Result { + v.manifest(PythonVarsFormat {}) +} + +#[builtin] +pub fn builtin_escape_string_xml(str_: String) -> String { + xml::escape_string_xml(str_.as_str()) +} + +#[builtin] +pub fn builtin_manifest_xml_jsonml(value: Val) -> Result { + value.manifest(XmlJsonmlFormat::std_to_xml()) +} diff --git a/crates/jrsonnet-stdlib/src/manifest/python.rs b/crates/jrsonnet-stdlib/src/manifest/python.rs new file mode 100644 index 00000000..b2e9c9f5 --- /dev/null +++ b/crates/jrsonnet-stdlib/src/manifest/python.rs @@ -0,0 +1,87 @@ +use jrsonnet_evaluator::{ + bail, + manifest::{escape_string_json_buf, ManifestFormat, ToStringFormat}, + Result, Val, +}; + +pub struct PythonFormat { + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, +} + +impl ManifestFormat for PythonFormat { + fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> { + match val { + Val::Bool(true) => buf.push_str("True"), + Val::Bool(false) => buf.push_str("False"), + Val::Null => buf.push_str("None"), + Val::Str(s) => escape_string_json_buf(&s.to_string(), buf), + Val::Num(_) => ToStringFormat.manifest_buf(val, buf)?, + Val::Arr(arr) => { + buf.push('['); + for (i, el) in arr.iter().enumerate() { + let el = el?; + if i != 0 { + buf.push_str(", "); + } + self.manifest_buf(el, buf)?; + } + buf.push(']'); + } + Val::Obj(obj) => { + obj.run_assertions()?; + buf.push('{'); + let fields = obj.fields( + #[cfg(feature = "exp-preserve-order")] + self.preserve_order, + ); + for (i, field) in fields.into_iter().enumerate() { + if i != 0 { + buf.push_str(", "); + } + escape_string_json_buf(&field, buf); + buf.push_str(": "); + let value = obj.get(field)?.expect("field exists"); + self.manifest_buf(value, buf)?; + } + buf.push('}'); + } + Val::Func(_) => bail!("tried to manifest function"), + } + Ok(()) + } +} + +pub struct PythonVarsFormat { + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, +} + +impl PythonVarsFormat {} + +impl ManifestFormat for PythonVarsFormat { + fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> { + let inner = PythonFormat { + #[cfg(feature = "exp-preserve-order")] + preserve_order: self.preserve_order, + }; + let Val::Obj(obj) = val else { + bail!("python vars root should be object"); + }; + obj.run_assertions()?; + + let fields = obj.fields( + #[cfg(feature = "exp-preserve-order")] + self.preserve_order, + ); + + for field in fields { + // Yep, no escaping + buf.push_str(&field); + buf.push_str(" = "); + inner.manifest_buf(obj.get(field)?.expect("field exists"), buf)?; + buf.push('\n'); + } + Ok(()) + } +} diff --git a/crates/jrsonnet-stdlib/src/manifest/xml.rs b/crates/jrsonnet-stdlib/src/manifest/xml.rs new file mode 100644 index 00000000..5f52d57c --- /dev/null +++ b/crates/jrsonnet-stdlib/src/manifest/xml.rs @@ -0,0 +1,173 @@ +use jrsonnet_evaluator::{ + bail, + manifest::{ManifestFormat, ToStringFormat}, + typed::{ComplexValType, Either4, Typed, ValType}, + val::{ArrValue, IndexableVal}, + Either, ObjValue, Result, ResultExt, Val, +}; + +pub struct XmlJsonmlFormat { + force_closing: bool, +} +impl XmlJsonmlFormat { + pub fn std_to_xml() -> Self { + Self { + force_closing: true, + } + } + pub fn cli() -> Self { + Self { + force_closing: false, + } + } +} + +enum JSONMLValue { + Tag { + tag: String, + attrs: ObjValue, + children: Vec, + }, + String(String), +} +impl Typed for JSONMLValue { + const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr); + + fn into_untyped(_typed: Self) -> Result { + unreachable!("not used, reserved for parseXML?") + } + + fn from_untyped(untyped: Val) -> Result { + let Val::Arr(arr) = untyped else { + if let Val::Str(s) = untyped { + return Ok(Self::String(s.to_string())); + }; + bail!("expected JSONML value (an array or string)"); + }; + if arr.len() < 1 { + bail!("JSONML value should have tag"); + }; + let tag = String::from_untyped( + arr.get(0) + .with_description(|| "getting JSONML tag")? + .expect("length checked"), + )?; + let (has_attrs, attrs) = if arr.len() >= 2 { + let maybe_attrs = arr + .get(1) + .with_description(|| "getting JSONML attrs")? + .expect("length checked"); + if let Val::Obj(attrs) = maybe_attrs { + (true, attrs) + } else { + (false, ObjValue::new_empty()) + } + } else { + (false, ObjValue::new_empty()) + }; + Ok(Self::Tag { + tag, + attrs, + children: Typed::from_untyped(Val::Arr(arr.slice( + Some(if has_attrs { 2 } else { 1 }), + None, + None, + )))?, + }) + } +} + +impl ManifestFormat for XmlJsonmlFormat { + fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> { + let val = JSONMLValue::from_untyped(val).with_description(|| "parsing JSONML value")?; + manifest_jsonml(&val, buf, self) + } +} + +fn manifest_jsonml(v: &JSONMLValue, buf: &mut String, opts: &XmlJsonmlFormat) -> Result<()> { + match v { + JSONMLValue::Tag { + tag, + attrs, + children, + } => { + let has_children = !children.is_empty(); + buf.push('<'); + buf.push_str(&tag); + attrs.run_assertions()?; + for (key, value) in attrs.iter() { + buf.push(' '); + buf.push_str(&key); + buf.push('='); + buf.push('"'); + let value = value?; + let value = if let Val::Str(s) = value { + s.to_string() + } else { + ToStringFormat.manifest(value)? + }; + escape_string_xml_buf(&value, buf); + buf.push('"'); + } + if !has_children && !opts.force_closing { + buf.push('/'); + } + buf.push('>'); + for child in children { + manifest_jsonml(&child, buf, opts)?; + } + if has_children || opts.force_closing { + buf.push('<'); + buf.push('/'); + buf.push_str(&tag); + buf.push('>'); + } + Ok(()) + } + JSONMLValue::String(s) => { + escape_string_xml_buf(s, buf); + Ok(()) + } + } +} + +pub fn escape_string_xml(str: &str) -> String { + let mut out = String::new(); + escape_string_xml_buf(str, &mut out); + out +} + +fn escape_string_xml_buf(str: &str, out: &mut String) { + if str.is_empty() { + return; + } + let mut remaining = str; + + let mut found = false; + while let Some(position) = remaining + .bytes() + .position(|c| matches!(c, b'<' | b'>' | b'&' | b'"' | b'\'')) + { + found = true; + + let (plain, rem) = remaining.split_at(position); + out.push_str(plain); + + out.push_str(match rem.as_bytes()[0] { + b'<' => "<", + b'>' => ">", + b'&' => "&", + b'"' => """, + b'\'' => "'", + _ => unreachable!("position() searches for those matches"), + }); + + remaining = &rem[1..]; + } + if !found { + // No match - no escapes required + out.push_str(&str); + return; + } + out.push_str(&remaining); +} diff --git a/crates/jrsonnet-stdlib/src/math.rs b/crates/jrsonnet-stdlib/src/math.rs index ac8bffa9..cd0e1d6e 100644 --- a/crates/jrsonnet-stdlib/src/math.rs +++ b/crates/jrsonnet-stdlib/src/math.rs @@ -24,6 +24,12 @@ pub fn builtin_min(a: f64, b: f64) -> f64 { a.min(b) } +#[allow(non_snake_case)] +#[builtin] +pub fn builtin_clamp(x: f64, minVal: f64, maxVal: f64) -> f64 { + x.clamp(minVal, maxVal) +} + #[builtin] pub fn builtin_sum(arr: Vec) -> f64 { arr.iter().sum() diff --git a/crates/jrsonnet-stdlib/src/misc.rs b/crates/jrsonnet-stdlib/src/misc.rs index 2933258e..d1fbd3fd 100644 --- a/crates/jrsonnet-stdlib/src/misc.rs +++ b/crates/jrsonnet-stdlib/src/misc.rs @@ -23,6 +23,30 @@ pub fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> usize { } } +#[builtin] +pub fn builtin_get( + o: ObjValue, + f: IStr, + default: Option>, + #[default(true)] + inc_hidden: bool, +) -> Result { + let do_default = move || { + let Some(default) = default else { + return Ok(Val::Null); + }; + default.evaluate() + }; + // Happy path for invisible fields + if !inc_hidden && !o.has_field_ex(f.clone(), false) { + return do_default(); + } + let Some(v) = o.get(f)? else { + return do_default(); + }; + Ok(v) +} + #[builtin(fields( settings: Rc>, ))] diff --git a/crates/jrsonnet-stdlib/src/objects.rs b/crates/jrsonnet-stdlib/src/objects.rs index 1f36bf20..664e42c3 100644 --- a/crates/jrsonnet-stdlib/src/objects.rs +++ b/crates/jrsonnet-stdlib/src/objects.rs @@ -8,10 +8,11 @@ use jrsonnet_evaluator::{ pub fn builtin_object_fields_ex( obj: ObjValue, hidden: bool, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, -) -> Vec { + + #[default(false)] #[cfg(feature = "exp-preserve-order")] - let preserve_order = preserve_order.unwrap_or(false); + preserve_order: bool, +) -> Vec { let out = obj.fields_ex( hidden, #[cfg(feature = "exp-preserve-order")] @@ -23,7 +24,10 @@ pub fn builtin_object_fields_ex( #[builtin] pub fn builtin_object_fields( o: ObjValue, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> Vec { builtin_object_fields_ex( o, @@ -36,7 +40,10 @@ pub fn builtin_object_fields( #[builtin] pub fn builtin_object_fields_all( o: ObjValue, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> Vec { builtin_object_fields_ex( o, @@ -49,10 +56,9 @@ pub fn builtin_object_fields_all( pub fn builtin_object_values_ex( o: ObjValue, include_hidden: bool, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + + #[cfg(feature = "exp-preserve-order")] preserve_order: bool, ) -> ArrValue { - #[cfg(feature = "exp-preserve-order")] - let preserve_order = preserve_order.unwrap_or(false); o.values_ex( include_hidden, #[cfg(feature = "exp-preserve-order")] @@ -62,7 +68,10 @@ pub fn builtin_object_values_ex( #[builtin] pub fn builtin_object_values( o: ObjValue, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> ArrValue { builtin_object_values_ex( o, @@ -74,7 +83,10 @@ pub fn builtin_object_values( #[builtin] pub fn builtin_object_values_all( o: ObjValue, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> ArrValue { builtin_object_values_ex( o, @@ -87,10 +99,8 @@ pub fn builtin_object_values_all( pub fn builtin_object_keys_values_ex( o: ObjValue, include_hidden: bool, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + #[cfg(feature = "exp-preserve-order")] preserve_order: bool, ) -> ArrValue { - #[cfg(feature = "exp-preserve-order")] - let preserve_order = preserve_order.unwrap_or(false); o.key_values_ex( include_hidden, #[cfg(feature = "exp-preserve-order")] @@ -100,7 +110,10 @@ pub fn builtin_object_keys_values_ex( #[builtin] pub fn builtin_object_keys_values( o: ObjValue, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> ArrValue { builtin_object_keys_values_ex( o, @@ -112,7 +125,10 @@ pub fn builtin_object_keys_values( #[builtin] pub fn builtin_object_keys_values_all( o: ObjValue, - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, + + #[default(false)] + #[cfg(feature = "exp-preserve-order")] + preserve_order: bool, ) -> ArrValue { builtin_object_keys_values_ex( o, @@ -141,12 +157,13 @@ pub fn builtin_object_has_all(o: ObjValue, f: IStr) -> bool { pub fn builtin_object_remove_key( obj: ObjValue, key: IStr, + // Standard implementation uses std.objectFields without such argument, we can't // assume order preservation should always be enabled/disabled - #[cfg(feature = "exp-preserve-order")] preserve_order: Option, -) -> ObjValue { + #[default(false)] #[cfg(feature = "exp-preserve-order")] - let preserve_order = preserve_order.unwrap_or(false); + preserve_order: bool, +) -> ObjValue { let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1); for (k, v) in obj.iter( #[cfg(feature = "exp-preserve-order")] diff --git a/crates/jrsonnet-stdlib/src/sort.rs b/crates/jrsonnet-stdlib/src/sort.rs index 18de08ad..503b7e07 100644 --- a/crates/jrsonnet-stdlib/src/sort.rs +++ b/crates/jrsonnet-stdlib/src/sort.rs @@ -139,8 +139,12 @@ pub fn sort(values: ArrValue, key_getter: FuncVal) -> Result { } #[builtin] -pub fn builtin_sort(arr: ArrValue, keyF: Option) -> Result { - super::sort::sort(arr, keyF.unwrap_or_else(FuncVal::identity)) +pub fn builtin_sort( + arr: ArrValue, + + #[default(FuncVal::identity())] keyF: FuncVal, +) -> Result { + super::sort::sort(arr, keyF) } fn uniq_identity(arr: Vec) -> Result> { @@ -174,11 +178,14 @@ fn uniq_keyf(arr: ArrValue, keyf: FuncVal) -> Result>> { #[builtin] #[allow(non_snake_case)] -pub fn builtin_uniq(arr: ArrValue, keyF: Option) -> Result { +pub fn builtin_uniq( + arr: ArrValue, + + #[default(FuncVal::identity())] keyF: FuncVal, +) -> Result { if arr.len() <= 1 { return Ok(arr); } - let keyF = keyF.unwrap_or(FuncVal::identity()); if keyF.is_identity() { Ok(ArrValue::eager(uniq_identity( arr.iter().collect::>>()?, @@ -190,11 +197,14 @@ pub fn builtin_uniq(arr: ArrValue, keyF: Option) -> Result { #[builtin] #[allow(non_snake_case)] -pub fn builtin_set(arr: ArrValue, keyF: Option) -> Result { +pub fn builtin_set( + arr: ArrValue, + + #[default(FuncVal::identity())] keyF: FuncVal, +) -> Result { if arr.len() <= 1 { return Ok(arr); } - let keyF = keyF.unwrap_or(FuncVal::identity()); if keyF.is_identity() { let arr = arr.iter().collect::>>()?; let arr = sort_identity(arr)?; diff --git a/crates/jrsonnet-stdlib/src/std.jsonnet b/crates/jrsonnet-stdlib/src/std.jsonnet index 0a4dd684..2dcec3ac 100644 --- a/crates/jrsonnet-stdlib/src/std.jsonnet +++ b/crates/jrsonnet-stdlib/src/std.jsonnet @@ -1,6 +1,5 @@ { local std = self, - local id = std.id, thisFile:: error 'std.thisFile is deprecated, to enable its support in jrsonnet - recompile it with "legacy-this-file" support.\nThis will slow down stdlib caching a bit, though', @@ -27,8 +26,6 @@ local revStr(str) = std.join('', std.reverse(std.stringChars(str))); std.map(function(e) revStr(e), std.reverse(std.splitLimit(revStr(str), revStr(c), maxsplits))), - split(str, c):: std.splitLimit(str, c, -1), - mapWithIndex(func, arr):: if !std.isFunction(func) then error ('std.mapWithIndex first param must be function, got ' + std.type(func)) @@ -62,11 +59,6 @@ else error 'Assertion failed. ' + a + ' != ' + b, - clamp(x, minVal, maxVal):: - if x < minVal then minVal - else if x > maxVal then maxVal - else x, - manifestIni(ini):: local body_lines(body) = std.join([], [ @@ -87,97 +79,6 @@ ]; std.join('\n', main_body + std.flattenArrays(all_sections) + ['']), - manifestToml(value):: std.manifestTomlEx(value, ' '), - - escapeStringPython(str):: - std.escapeStringJson(str), - - escapeStringBash(str_):: - local str = std.toString(str_); - local trans(ch) = - if ch == "'" then - "'\"'\"'" - else - ch; - "'%s'" % std.join('', [trans(ch) for ch in std.stringChars(str)]), - - escapeStringDollars(str_):: - local str = std.toString(str_); - local trans(ch) = - if ch == '$' then - '$$' - else - ch; - std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''), - - local xml_escapes = { - '<': '<', - '>': '>', - '&': '&', - '"': '"', - "'": ''', - }, - - escapeStringXML(str_):: - local str = std.toString(str_); - std.join('', [std.get(xml_escapes, ch, ch) for ch in std.stringChars(str)]), - - manifestJson(value):: std.manifestJsonEx(value, ' ') tailstrict, - - manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'), - - manifestYamlStream(value, indent_array_in_object=false, c_document_end=true, quote_keys=true):: - if !std.isArray(value) then - error 'manifestYamlStream only takes arrays, got ' + std.type(value) - else - '---\n' + std.join( - '\n---\n', [std.manifestYamlDoc(e, indent_array_in_object, quote_keys) for e in value] - ) + if c_document_end then '\n...\n' else '\n', - - manifestPython(v):: - if std.isObject(v) then - local fields = [ - '%s: %s' % [std.escapeStringPython(k), std.manifestPython(v[k])] - for k in std.objectFields(v) - ]; - '{%s}' % [std.join(', ', fields)] - else if std.isArray(v) then - '[%s]' % [std.join(', ', [std.manifestPython(v2) for v2 in v])] - else if std.isString(v) then - '%s' % [std.escapeStringPython(v)] - else if std.isFunction(v) then - error 'cannot manifest function' - else if std.isNumber(v) then - std.toString(v) - else if v == true then - 'True' - else if v == false then - 'False' - else if v == null then - 'None', - - manifestPythonVars(conf):: - local vars = ['%s = %s' % [k, std.manifestPython(conf[k])] for k in std.objectFields(conf)]; - std.join('\n', vars + ['']), - - manifestXmlJsonml(value):: - if !std.isArray(value) then - error 'Expected a JSONML value (an array), got %s' % std.type(value) - else - local aux(v) = - if std.isString(v) then - v - else - local tag = v[0]; - local has_attrs = std.length(v) > 1 && std.isObject(v[1]); - local attrs = if has_attrs then v[1] else {}; - local children = if has_attrs then v[2:] else v[1:]; - local attrs_str = - std.join('', [' %s="%s"' % [k, attrs[k]] for k in std.objectFields(attrs)]); - std.deepJoin(['<', tag, attrs_str, '>', [aux(x) for x in children], '']); - - aux(value), - mergePatch(target, patch):: if std.isObject(patch) then local target_object = @@ -202,9 +103,6 @@ else patch, - get(o, f, default=null, inc_hidden=true):: - if std.objectHasEx(o, f, inc_hidden) then o[f] else default, - resolvePath(f, r):: local arr = std.split(f, '/'); std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]), diff --git a/crates/jrsonnet-stdlib/src/strings.rs b/crates/jrsonnet-stdlib/src/strings.rs index 8c8654d5..8803ad9b 100644 --- a/crates/jrsonnet-stdlib/src/strings.rs +++ b/crates/jrsonnet-stdlib/src/strings.rs @@ -27,6 +27,20 @@ pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> String { str.replace(&from as &str, &to as &str) } +#[builtin] +pub fn builtin_escape_string_bash(str: String) -> String { + const QUOTE: char = '\''; + let mut out = str.replace(QUOTE, "'\"'\"'"); + out.insert(0, QUOTE); + out.push(QUOTE); + out +} + +#[builtin] +pub fn builtin_escape_string_dollars(str: String) -> String { + str.replace('$', "$$") +} + #[builtin] pub fn builtin_is_empty(str: String) -> bool { str.is_empty() @@ -46,6 +60,12 @@ pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> } } +#[builtin] +pub fn builtin_split(str: IStr, c: IStr) -> ArrValue { + use Either2::*; + builtin_splitlimit(str, c, B(M1)) +} + #[builtin] pub fn builtin_ascii_upper(str: IStr) -> String { str.to_ascii_uppercase()