diff --git a/Cargo.lock b/Cargo.lock index 606c829..e701770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -43,29 +43,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "async-nats" @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -123,15 +123,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ "axum-core", "bytes", @@ -145,8 +145,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "sync_wrapper", "tower", "tower-layer", @@ -155,9 +154,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ "bytes", "futures-core", @@ -166,7 +165,6 @@ dependencies = [ "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", @@ -180,15 +178,15 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "block-buffer" @@ -210,18 +208,19 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.22" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" @@ -240,7 +239,7 @@ checksum = "e3e3a3aec6ffc35ac360113d8b96206c38961afef56b0c5632cb77cd2f4429b9" dependencies = [ "serde", "serde_json", - "tucana", + "tucana 0.0.39", ] [[package]] @@ -259,7 +258,7 @@ dependencies = [ "serde_json", "tonic", "tonic-health", - "tucana", + "tucana 0.0.39", ] [[package]] @@ -354,12 +353,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -419,9 +418,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -448,12 +447,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -468,6 +467,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + [[package]] name = "fixedbitset" version = "0.5.7" @@ -482,9 +487,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -551,9 +556,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -567,26 +572,26 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] [[package]] name = "h2" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -603,9 +608,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" @@ -661,13 +666,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", @@ -675,6 +681,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -695,19 +702,20 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", "libc", "pin-project-lite", - "socket2 0.5.9", + "socket2", "tokio", "tower-service", "tracing", @@ -715,9 +723,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -728,9 +736,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -741,11 +749,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -756,42 +763,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -801,9 +804,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -822,9 +825,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", @@ -832,9 +835,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -853,22 +856,22 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde", + "serde_core", ] [[package]] name = "jiff-static" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", @@ -877,21 +880,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "log" @@ -907,9 +910,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mime" @@ -919,20 +922,20 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] name = "multimap" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "nkeys" @@ -981,9 +984,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl-probe" @@ -1045,9 +1048,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" @@ -1103,9 +1106,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -1118,9 +1121,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -1142,9 +1145,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.32" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", @@ -1152,9 +1155,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -1226,27 +1229,27 @@ dependencies = [ [[package]] name = "pulldown-cmark-to-cmark" -version = "21.0.0" +version = "21.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b6a0769a491a08b31ea5c62494a8f144ee0987d86d670a8af4df1e1b7cde75" +checksum = "8246feae3db61428fd0bb94285c690b460e4517d83152377543ca802357785f1" dependencies = [ "pulldown-cmark", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -1304,14 +1307,14 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -1321,9 +1324,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -1332,9 +1335,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "ring" @@ -1361,27 +1364,27 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.3", + "rustls-webpki 0.103.8", "subtle", "zeroize", ] @@ -1410,9 +1413,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "zeroize", ] @@ -1429,21 +1432,15 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] -[[package]] -name = "rustversion" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" - [[package]] name = "ryu" version = "1.0.20" @@ -1452,11 +1449,11 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1474,9 +1471,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -1484,24 +1481,34 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1510,14 +1517,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -1581,37 +1589,24 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" - -[[package]] -name = "socket2" -version = "0.5.9" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1626,9 +1621,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "subtle" @@ -1638,9 +1633,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" dependencies = [ "proc-macro2", "quote", @@ -1679,20 +1674,20 @@ dependencies = [ "tokio", "tonic", "tonic-health", - "tucana", + "tucana 0.0.40", ] [[package]] name = "tempfile" -version = "3.20.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1717,9 +1712,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -1732,15 +1727,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -1748,9 +1743,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -1766,7 +1761,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "socket2 0.6.0", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -1784,9 +1779,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -1806,9 +1801,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -1857,7 +1852,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2 0.6.0", + "socket2", "sync_wrapper", "tokio", "tokio-stream", @@ -1869,9 +1864,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e323d8bba3be30833707e36d046deabf10a35ae8ad3cae576943ea8933e25d" +checksum = "4c40aaccc9f9eccf2cd82ebc111adc13030d23e887244bc9cfa5d1d636049de3" dependencies = [ "prettyplease", "proc-macro2", @@ -1894,9 +1889,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d" +checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ "bytes", "prost", @@ -1905,9 +1900,9 @@ dependencies = [ [[package]] name = "tonic-prost-build" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef298fcd01b15e135440c4b8c974460ceca4e6a5af7f1c933b08e4d2875efa1" +checksum = "b4a16cba4043dc3ff43fcb3f96b4c5c154c64cbd18ca8dce2ab2c6a451d058a2" dependencies = [ "prettyplease", "proc-macro2", @@ -1963,9 +1958,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -1974,9 +1969,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -2017,11 +2012,31 @@ dependencies = [ "tonic-prost-build", ] +[[package]] +name = "tucana" +version = "0.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "565d382969ce8de8484d22516808cfef658710a6f3342a566ad1e247ccb852cf" +dependencies = [ + "pbjson", + "pbjson-build", + "pbjson-types", + "prost", + "prost-build", + "prost-types", + "serde", + "serde_json", + "tonic", + "tonic-build", + "tonic-prost", + "tonic-prost-build", +] + [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicase" @@ -2031,9 +2046,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "untrusted" @@ -2043,13 +2058,14 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -2081,17 +2097,17 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -2100,14 +2116,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.2", + "webpki-roots 1.0.4", ] [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] @@ -2124,16 +2140,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets", + "windows-targets 0.53.5", ] [[package]] @@ -2151,14 +2167,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "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", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2167,42 +2200,84 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2210,27 +2285,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "windows_x86_64_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -2238,9 +2315,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", @@ -2250,18 +2327,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", @@ -2291,15 +2368,15 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -2308,9 +2385,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -2319,9 +2396,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 85bd2b1..7e154fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,14 @@ -[package] -name = "taurus" +[workspace] +members = ["taurus"] +resolver = "3" + +[workspace.package] version = "0.1.0" edition = "2024" -[dependencies] +[workspace.dependencies] code0-flow = { version = "0.0.18" } -tucana = { version = "0.0.39" } +tucana = { version = "0.0.40" } tokio = { version = "1.44.1", features = ["rt-multi-thread"] } log = "0.4.27" futures-lite = "2.6.0" diff --git a/src/context/context.rs b/src/context/context.rs deleted file mode 100644 index 10bdeb7..0000000 --- a/src/context/context.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct Context {} diff --git a/src/context/mod.rs b/src/context/mod.rs deleted file mode 100644 index 89d720c..0000000 --- a/src/context/mod.rs +++ /dev/null @@ -1,152 +0,0 @@ -pub mod context; -pub mod signal; - -use crate::error::RuntimeError; -use std::{ - collections::{HashMap, VecDeque}, - ops::Index, -}; -use tucana::shared::{ReferenceValue, Value}; - -type NodeResult = Result; - -pub enum ContextResult { - // Will return the value / error if present of an executed node - NodeExecutionResult(NodeResult), - - // Will return the parameter of the node (indexed by the context) - ParameterResult(Value), -} - -#[derive(Clone)] -pub struct ContextEntry { - result: Result, - parameter: Vec, -} - -impl ContextEntry { - pub fn new(result: NodeResult, parameter: Vec) -> Self { - ContextEntry { result, parameter } - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct ContextReference { - // Level referencing the context depth (if a node will execute nodes in itself, e.g. foreach, map...) - pub primary_level: i32, - - // Level of depth in the current context level (node after node starting_node -> next_node --> next_node --> ending_node) - pub secondary_level: i32, - - // Index of parameters as input parameter of a node - pub tertiary_level: Option, -} - -pub struct Context { - current_context_level: ContextReference, - /// A stack of environments: layer 0 is the outermost. - layers: HashMap, - /// Context Snapshot of Past Context - context_history: VecDeque<(i32, i32)>, -} - -impl Default for Context { - fn default() -> Self { - Self::new() - } -} - -impl Context { - /// Create a new, empty context. - pub fn new() -> Self { - Context { - current_context_level: ContextReference { - primary_level: 0, - secondary_level: 0, - tertiary_level: None, - }, - layers: HashMap::new(), - context_history: VecDeque::new(), - } - } - - pub fn write_to_context(&mut self, reference: ContextReference, entry: ContextEntry) { - self.layers.insert(reference, entry); - } - - pub fn write_to_current_context(&mut self, entry: ContextEntry) { - self.write_to_context(self.current_context_level.clone(), entry); - } - - pub fn set_current_context( - &mut self, - primary_level: i32, - seconday_level: i32, - tertiary_level: Option, - ) { - self.current_context_level.primary_level = primary_level; - self.current_context_level.secondary_level = seconday_level; - self.current_context_level.tertiary_level = tertiary_level; - } - - /// Will indent the context and save the past context - pub fn next_context(&mut self) { - let context_snapshot = ( - self.current_context_level.primary_level, - self.current_context_level.secondary_level, - ); - - self.context_history.push_back(context_snapshot); - - self.current_context_level.primary_level += 1; - self.current_context_level.secondary_level = 0; - } - - /// Will return to the parent context and increment the seconday level - pub fn leave_context(&mut self) { - let last_snapshot = match self.context_history.pop_back() { - Some(pair) => pair, - None => return, - }; - - self.current_context_level.primary_level = last_snapshot.0; - self.current_context_level.secondary_level = last_snapshot.1 + 1; - } - - pub fn next_node(&mut self) { - self.current_context_level.secondary_level += 1; - } - - // Looks up the current Context - pub fn get_current_context(&self) -> Option { - for (context, value) in self.layers.iter() { - if context.primary_level != self.current_context_level.primary_level { - continue; - } - - if context.secondary_level != self.current_context_level.secondary_level { - continue; - } - - if let Some(index) = self.current_context_level.tertiary_level { - let params = &value.parameter; - - let real_index = index as usize; - let value = params.index(real_index); - return Some(ContextResult::ParameterResult(value.clone())); - } - - return Some(ContextResult::NodeExecutionResult(value.result.clone())); - } - None - } - - /// Looks up the context of a reference - pub fn get(&self, reference: &ReferenceValue) -> Option { - unimplemented!("Implement latest reference pattern from Tucana 0.0.39") - } - - pub fn is_end(&self) -> bool { - self.current_context_level.primary_level == 0 - } -} diff --git a/src/executor/mod.rs b/src/executor/mod.rs deleted file mode 100644 index cf3f844..0000000 --- a/src/executor/mod.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::context::Context; -use crate::context::signal::Signal; -use crate::error::RuntimeError; -use crate::registry::FunctionStore; -use std::collections::HashMap; -use tucana::shared::{NodeFunction, NodeParameter}; - -pub struct Executor<'a> { - functions: &'a FunctionStore, - nodes: HashMap, - context: Context, -} - -type HandleNodeParameterFn = fn(&mut Executor, node_parameter: &NodeParameter) -> Signal; - -impl<'a> Executor<'a> { - pub fn new( - functions: &'a FunctionStore, - nodes: HashMap, - context: Context, - ) -> Self { - Executor { - functions, - nodes, - context, - } - } - - pub fn execute(&mut self, starting_node_id: i64) -> Signal { - let mut current_node_id = starting_node_id; - - loop { - let node = match self.nodes.get(¤t_node_id) { - None => { - return Signal::Failure(RuntimeError::simple_str( - "NodeNotFound", - "The node with the id was not found", - )); - } - Some(n) => n.clone(), - }; - - let mut parameters = Vec::new(); - - for node_parameter in &node.parameters { - match Executor::handle_node_parameter(self, node_parameter) { - Signal::Success(value) | Signal::Return(value) => { - parameters.push(value.clone()) - } - Signal::Failure(error) => return Signal::Failure(error), - Signal::Stop => return Signal::Stop, - Signal::Respond(value) => return Signal::Respond(value), - } - } - - let execution_result = match self.functions.get(node.runtime_function_id.as_str()) { - Some(handler) => handler(¶meters, &mut self.context), - None => { - return Signal::Failure(RuntimeError::simple_str( - "FunctionNotFound", - "The function was not found", - )); - } - }; - - match execution_result { - Signal::Success(value) => { - if let Some(next_node_id) = node.next_node_id { - current_node_id = next_node_id; - continue; - } else { - return Signal::Success(value); - } - } - Signal::Failure(runtime_error) => return Signal::Failure(runtime_error), - Signal::Return(value) => return Signal::Return(value), - Signal::Respond(value) => return Signal::Respond(value), - Signal::Stop => return Signal::Stop, - } - } - } - - fn handle_node_parameter(&mut self, node_parameter: &NodeParameter) -> Signal { - let node_value = match &node_parameter.value { - Some(v) => v, - None => { - return Signal::Failure(RuntimeError::simple_str( - "NodeValueNotFound", - "An error occurred while executing a flow!", - )); - } - }; - - let value = match &node_value.value { - Some(v) => v, - None => { - return Signal::Failure(RuntimeError::simple_str( - "NodeValueNotFound", - "An error occurred while executing a flow!", - )); - } - }; - - match value { - tucana::shared::node_value::Value::LiteralValue(value) => { - Signal::Success(value.clone()) - } - tucana::shared::node_value::Value::ReferenceValue(_reference_value) => { - todo!("implement reference values!") - } - tucana::shared::node_value::Value::NodeFunctionId(id) => Executor::execute(self, *id), - } - } -} diff --git a/src/implementation/array.rs b/src/implementation/array.rs deleted file mode 100644 index 58591a3..0000000 --- a/src/implementation/array.rs +++ /dev/null @@ -1,1676 +0,0 @@ -use std::cmp::Ordering; - -use tucana::shared::{Value, value::Kind}; - -use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; - -pub fn collect_array_functions() -> Vec<(&'static str, HandlerFn)> { - vec![ - ("std::array::at", at), - ("std::array::concat", concat), - ("std::array::filter", filter), - ("std::array::find", find), - ("std::array::find_last", find_last), - ("std::array::find_index", find_index), - ("std::array::first", first), - ("std::array::last", last), - ("std::array::for_each", for_each), - ("std::array::map", map), - ("std::array::push", push), - ("std::array::pop", pop), - ("std::array::remove", remove), - ("std::array::is_empty", is_empty), - ("std::array::size", size), - ("std::array::index_of", index_of), - ("std::array::to_unique", to_unique), - ("std::array::sort", sort), - ("std::array::sort_reverse", sort_reverse), - ("std::array::reverse", reverse), - ("std::array::flat", flat), - ("std::array::min", min), - ("std::array::max", max), - ("std::array::sum", sum), - ("std::array::join", join), - ] -} - -fn at(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::NumberValue(index)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected a list and a number as arguments but received {:?}", - values - ), - )); - }; - - let item = array.values[*index as usize].clone(); - - Signal::Success(item) -} - -fn concat(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(lhs)), - }, - Value { - kind: Some(Kind::ListValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); - }; - - let mut result = lhs.values.clone(); - result.extend(rhs.values.clone()); - - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: result, - })), - }) -} - -fn filter(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_predicate)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); - }; - - let mut predicate_collector = vec![]; - resolved_predicate.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::BoolValue(pred)), - } = v.clone() - { - predicate_collector.push(pred); - } - }); - - let mut index = 0; - let new_array = array - .values - .clone() - .into_iter() - .filter(|_| { - let predicate = predicate_collector[index]; - index += 1; - predicate - }) - .collect::>(); - - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: new_array, - })), - }) -} - -fn find(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_predicate)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); - }; - - let mut predicate_collector = vec![]; - resolved_predicate.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::BoolValue(pred)), - } = v.clone() - { - predicate_collector.push(pred); - } - }); - - let mut index = 0; - let item = array.values.clone().into_iter().find(|_| { - let predicate = predicate_collector[index]; - index += 1; - predicate - }); - - match item { - Some(item) => Signal::Success(item), - None => Signal::Failure(RuntimeError::simple( - "NotFoundError", - "No item found that satisfies the predicate".to_string(), - )), - } -} - -fn find_last(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_predicate)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); - }; - - let mut predicate_collector = vec![]; - resolved_predicate.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::BoolValue(pred)), - } = v.clone() - { - predicate_collector.push(pred); - } - }); - - let mut index = 0; - let mut new_array = array.values.clone(); - new_array.reverse(); - - let item = new_array.into_iter().find(|_| { - let predicate = predicate_collector[index]; - index += 1; - predicate - }); - - match item { - Some(item) => Signal::Success(item), - None => Signal::Failure(RuntimeError::simple( - "NotFoundError", - "No item found that satisfies the predicate".to_string(), - )), - } -} - -fn find_index(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_predicate)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); - }; - - let mut predicate_collector = vec![]; - resolved_predicate.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::BoolValue(pred)), - } = v.clone() - { - predicate_collector.push(pred); - } - }); - - let mut index = 0; - let new_array = array.values.clone(); - - let item = new_array.into_iter().find(|_| { - let predicate = predicate_collector[index]; - - if !predicate { - index += 1; - } - predicate - }); - - match item { - Some(_) => Signal::Success(Value { - kind: Some(Kind::NumberValue(index as f64)), - }), - None => Signal::Failure(RuntimeError::simple( - "NotFoundError", - "No item found that satisfies the predicate".to_string(), - )), - } -} - -fn first(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - match array.values.first() { - Some(item) => Signal::Success(item.clone()), - None => Signal::Failure(RuntimeError::simple_str( - "ArrayEmptyRuntimeError", - "This array is empty", - )), - } -} - -fn last(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - match array.values.last() { - Some(item) => Signal::Success(item.clone()), - None => Signal::Failure(RuntimeError::simple_str( - "ArrayEmptyRuntimeError", - "This array is empty", - )), - } -} - -/* - * for_each has no implementation - * - * Reason: - * The definition itself takes in an array and a node - * The node itself will be executed on the arrays elements - * If the node is (CONSUMER) resolved it goes in this function --> therefor all code is already executed - */ -fn for_each(_values: &[Value], _ctx: &mut Context) -> Signal { - Signal::Success(Value { - kind: Some(Kind::NullValue(0)), - }) -} - -/* - * Same as for_each - * - * Reason: - * The definition itself takes in an array and a node - * The node itself will be executed on the arrays elements - * If the node is (TRANSFORM) resolved it goes in this function --> therefor all code is already executed - * The TRANSFORM node is gives a new item as a result - */ -fn map(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(_array)), - }, - Value { - kind: Some(Kind::ListValue(transform_result)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::ListValue(transform_result.clone())), - }) -} - -fn push(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - item, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - let mut new_array = array.clone(); - new_array.values.push(item.clone()); - Signal::Success(Value { - kind: Some(Kind::ListValue(new_array)), - }) -} - -fn pop(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - let mut new_array = array.clone(); - new_array.values.pop(); - Signal::Success(Value { - kind: Some(Kind::ListValue(new_array)), - }) -} - -fn remove(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - item, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - let mut new_array = array.clone(); - let item_clone = item.clone(); - let index = match new_array.values.iter().position(|x| *x == item_clone) { - Some(index) => index, - None => { - return Signal::Failure(RuntimeError::simple( - "ValueNotFoundRuntimeError", - format!("Item {:?} not found in array", item_clone), - )); - } - }; - - new_array.values.remove(index); - - Signal::Success(Value { - kind: Some(Kind::ListValue(new_array)), - }) -} - -fn is_empty(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(array.values.is_empty())), - }) -} - -fn size(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(array.values.len() as f64)), - }) -} - -fn index_of(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - item, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - let new_array = array.clone(); - let item_clone = item.clone(); - let index = match new_array.values.iter().position(|x| *x == item_clone) { - Some(index) => index, - None => { - return Signal::Failure(RuntimeError::simple( - "ValueNotFoundRuntimeError", - format!("Item {:?} not found in array", item_clone), - )); - } - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(index as f64)), - }) -} - -fn to_unique(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - let mut unique_values = Vec::new(); - - for value in &array.values { - if !unique_values.contains(value) { - unique_values.push(value.clone()); - } - } - - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: unique_values, - })), - }) -} - -fn sort(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_comparator)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); - }; - - let mut comparate_collector = vec![]; - resolved_comparator.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::NumberValue(comp)), - } = v.clone() - { - comparate_collector.push(comp); - } - }); - - let mut index = 0; - let mut new_array = array.values.clone(); - new_array.sort_by(|_, _| { - let comp = comparate_collector[index]; - index += 1; - match comp { - -1.0 => Ordering::Less, - 0.0 => Ordering::Equal, - _ => Ordering::Greater, - } - }); - - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: new_array, - })), - }) -} - -fn sort_reverse(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_comparator)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); - }; - - let mut comparate_collector = vec![]; - resolved_comparator.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::NumberValue(comp)), - } = v.clone() - { - comparate_collector.push(comp); - } - }); - - let mut index = 0; - let mut new_array = array.values.clone(); - new_array.reverse(); - - new_array.sort_by(|_, _| { - let comp = comparate_collector[index]; - index += 1; - match comp { - -1.0 => Ordering::Less, - 0.0 => Ordering::Equal, - _ => Ordering::Greater, - } - }); - - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: new_array, - })), - }) -} - -fn reverse(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - let mut new_array = array.values.clone(); - new_array.reverse(); - - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: new_array, - })), - }) -} - -fn flat(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - - let mut flattable_array: Vec> = Vec::new(); - - for item in &array.values { - if let Value { - kind: Some(Kind::ListValue(sub_array)), - } = item - { - flattable_array.push(sub_array.values.clone()); - } else { - flattable_array.push(vec![item.clone()]); - } - } - - let flattend = flattable_array - .into_iter() - .flatten() - .collect::>(); - - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: flattend, - })), - }) -} - -fn min(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an array of numbers as an argument but received {:?}", - values - ), - )); - }; - - let mut new_array = vec![]; - array.values.clone().into_iter().for_each(|a| { - if let Value { - kind: Some(Kind::NumberValue(number)), - } = a - { - new_array.push(number); - } - }); - - let min = new_array.iter().min_by(|a, b| a.total_cmp(b)); - - match min { - Some(min) => Signal::Success(Value { - kind: Some(Kind::NumberValue(*min)), - }), - None => Signal::Failure(RuntimeError::simple( - "ArrayEmptyRuntimeError", - "Array is empty".to_string(), - )), - } -} - -fn max(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an array of numbers as an argument but received {:?}", - values - ), - )); - }; - - let mut new_array = vec![]; - array.values.clone().into_iter().for_each(|a| { - if let Value { - kind: Some(Kind::NumberValue(number)), - } = a - { - new_array.push(number); - } - }); - - let max = new_array.iter().max_by(|a, b| a.total_cmp(b)); - - match max { - Some(max) => Signal::Success(Value { - kind: Some(Kind::NumberValue(*max)), - }), - None => Signal::Failure(RuntimeError::simple( - "ArrayEmptyRuntimeError", - "Array is empty".to_string(), - )), - } -} - -fn sum(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an array of numbers as an argument but received {:?}", - values - ), - )); - }; - - let mut sum = 0.0; - array.values.clone().into_iter().for_each(|a| { - if let Value { - kind: Some(Kind::NumberValue(number)), - } = a - { - sum += number; - } - }); - - Signal::Success(Value { - kind: Some(Kind::NumberValue(sum)), - }) -} - -fn join(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::StringValue(separator)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected array of text and a text as arguments but received {:?}", - values - ), - )); - }; - - let mut collector = vec![]; - array.values.clone().into_iter().for_each(|a| { - if let Value { - kind: Some(Kind::StringValue(string)), - } = a - { - collector.push(string); - } - }); - - let joined = collector.join(separator); - - Signal::Success(Value { - kind: Some(Kind::StringValue(joined)), - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::context::Context; - use tucana::shared::{ListValue, Value, value::Kind}; - - // Helper function to create a number value - fn create_number_value(num: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(num)), - } - } - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), - } - } - - // Helper function to create a bool value - fn create_bool_value(b: bool) -> Value { - Value { - kind: Some(Kind::BoolValue(b)), - } - } - - // Helper function to create an array value - fn create_array_value(values: Vec) -> Value { - Value { - kind: Some(Kind::ListValue(ListValue { values })), - } - } - - #[test] - fn test_at_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(10.0), - create_number_value(20.0), - create_number_value(30.0), - ]); - - // Test getting first element - let values = vec![array.clone(), create_number_value(0.0)]; - let result = at(&values, &mut ctx); - match result { - Signal::Success(v) => match v.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 10.0), - _ => panic!("Expected NumberValue"), - }, - _ => panic!("Expected NumberValue"), - } - - // Test getting second element - let values = vec![array.clone(), create_number_value(1.0)]; - let signal = at(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 20.0), - _ => panic!("Expected NumberValue"), - } - - // Test getting third element - let values = vec![array, create_number_value(2.0)]; - let signal = at(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 30.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_at_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = at(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_number_value(0.0)]; - let result = at(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type for second parameter - let values = vec![array, create_string_value("not_number")]; - let result = at(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_concat_success() { - let mut ctx = Context::new(); - - let array1 = create_array_value(vec![create_number_value(1.0), create_number_value(2.0)]); - let array2 = create_array_value(vec![create_number_value(3.0), create_number_value(4.0)]); - - let values = vec![array1, array2]; - let signal = concat(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 4); - - // Check each element - match &list.values[0].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 1.0), - _ => panic!("Expected NumberValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 2.0), - _ => panic!("Expected NumberValue at index 1"), - } - match &list.values[2].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 3.0), - _ => panic!("Expected NumberValue at index 2"), - } - match &list.values[3].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 4.0), - _ => panic!("Expected NumberValue at index 3"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_concat_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = concat(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), array.clone()]; - let result = concat(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type for second parameter - let values = vec![array, create_number_value(42.0)]; - let result = concat(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_filter_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(1.0), - create_number_value(2.0), - create_number_value(3.0), - ]); - let predicate = create_array_value(vec![ - create_bool_value(true), - create_bool_value(false), - create_bool_value(true), - ]); - - let values = vec![array, predicate]; - let signal = filter(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 2); - - match &list.values[0].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 1.0), - _ => panic!("Expected NumberValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 3.0), - _ => panic!("Expected NumberValue at index 1"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_filter_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - let predicate = create_array_value(vec![create_bool_value(true)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = filter(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), predicate.clone()]; - let result = filter(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for second parameter - let values = vec![array, create_number_value(42.0)]; - let result = filter(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_first_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("first"), - create_string_value("second"), - create_string_value("third"), - ]); - - let values = vec![array]; - let signal = first(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "first"), - _ => panic!("Expected StringValue"), - } - } - - #[test] - fn test_first_error() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let result = first(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = first(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = first(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_last_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("first"), - create_string_value("second"), - create_string_value("last"), - ]); - - let values = vec![array]; - let signal = last(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "last"), - _ => panic!("Expected StringValue"), - } - } - - #[test] - fn test_last_error() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let result = last(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = last(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_push_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![create_number_value(1.0), create_number_value(2.0)]); - let new_element = create_number_value(3.0); - - let values = vec![array, new_element]; - let signal = push(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 3); - - match &list.values[2].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 3.0), - _ => panic!("Expected NumberValue at last index"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_push_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = push(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_number_value(42.0)]; - let result = push(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_pop_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(1.0), - create_number_value(2.0), - create_number_value(3.0), - ]); - - let values = vec![array]; - let signal = pop(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 2); - - // Check that the last element was removed - match &list.values[0].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 1.0), - _ => panic!("Expected NumberValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 2.0), - _ => panic!("Expected NumberValue at index 1"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_pop_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = pop(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = pop(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_empty_success() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let signal = is_empty(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test with non-empty array - let non_empty_array = create_array_value(vec![create_number_value(1.0)]); - let values = vec![non_empty_array]; - let signal = is_empty(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_is_empty_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = is_empty(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = is_empty(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_size_success() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let signal = size(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), - _ => panic!("Expected NumberValue"), - } - - // Test with array of 3 elements - let array = create_array_value(vec![ - create_number_value(1.0), - create_number_value(2.0), - create_number_value(3.0), - ]); - let values = vec![array]; - let signal = size(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_size_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_reverse_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(1.0), - create_number_value(2.0), - create_number_value(3.0), - ]); - - let values = vec![array]; - let signal = reverse(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 3); - - // Check that elements are reversed - match &list.values[0].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 3.0), - _ => panic!("Expected NumberValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 2.0), - _ => panic!("Expected NumberValue at index 1"), - } - match &list.values[2].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 1.0), - _ => panic!("Expected NumberValue at index 2"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_reverse_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = reverse(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = reverse(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_sum_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(1.5), - create_number_value(2.5), - create_number_value(3.0), - ]); - - let values = vec![array]; - let signal = sum(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 7.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_sum_empty_array() { - let mut ctx = Context::new(); - - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let signal = sum(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_sum_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = sum(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = sum(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_join_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("hello"), - create_string_value("world"), - create_string_value("test"), - ]); - let separator = create_string_value(", "); - - let values = vec![array, separator]; - let signal = join(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "hello, world, test"), - _ => panic!("Expected StringValue"), - } - } - - #[test] - fn test_join_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_string_value("hello")]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = join(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_string_value(",")]; - let result = join(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for second parameter - let values = vec![array, create_number_value(42.0)]; - let result = join(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_min_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(5.0), - create_number_value(1.0), - create_number_value(3.0), - create_number_value(2.0), - ]); - - let values = vec![array]; - let signal = min(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_min_error() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let result = min(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = min(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_max_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(5.0), - create_number_value(1.0), - create_number_value(8.0), - create_number_value(2.0), - ]); - - let values = vec![array]; - let signal = max(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 8.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_max_error() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let result = max(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = max(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_index_of_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(10.0), - create_number_value(42.0), - create_number_value(30.0), - create_number_value(42.0), // duplicate - ]); - let search_element = create_number_value(42.0); - - let values = vec![array, search_element]; - let signal = index_of(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), // Should return first occurrence - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_index_of_not_found() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("hello"), - create_string_value("world"), - ]); - let search_element = create_string_value("missing"); - - let values = vec![array, search_element]; - let result = index_of(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_index_of_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = index_of(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_number_value(42.0)]; - let result = index_of(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_remove_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("first"), - create_string_value("second"), - create_string_value("third"), - ]); - let item_to_remove = create_string_value("second"); // Remove middle element - - let values = vec![array, item_to_remove]; - let signal = remove(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 2); - - match &list.values[0].kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "first"), - _ => panic!("Expected StringValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "third"), - _ => panic!("Expected StringValue at index 1"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_remove_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = remove(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_number_value(0.0)]; - let result = remove(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with item not found - let values = vec![array, create_number_value(999.0)]; - let result = remove(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } -} diff --git a/src/implementation/boolean.rs b/src/implementation/boolean.rs deleted file mode 100644 index 4adf8c5..0000000 --- a/src/implementation/boolean.rs +++ /dev/null @@ -1,634 +0,0 @@ -use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; -use tucana::shared::{Value, value::Kind}; - -pub fn collect_boolean_functions() -> Vec<(&'static str, HandlerFn)> { - vec![ - ("std::boolean::as_number", as_number), - ("std::boolean::as_text", as_text), - ("std::boolean::from_number", from_number), - ("std::boolean::from_text", from_text), - ("std::boolean::is_equal", is_equal), - ("std::number::negate", negate), - ] -} - -fn as_number(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::BoolValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a boolean value but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(*value as i64 as f64)), - }) -} - -fn as_text(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::BoolValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a boolean value but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(value.to_string())), - }) -} - -fn from_number(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(number)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number value but received {:?}", values), - )); - }; - - let is_zero = number == &0.0; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(!is_zero)), - }) -} - -fn from_text(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(text)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a string value but received {:?}", values), - )); - }; - - let bool: bool = match text.to_lowercase().parse() { - Ok(value) => value, - Err(_) => { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Failed to parse boolean from string: {:?}", text), - )); - } - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(bool)), - }) -} - -fn is_equal(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::BoolValue(lhs)), - }, - Value { - kind: Some(Kind::BoolValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two boolean values but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(lhs == rhs)), - }) -} - -fn negate(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::BoolValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a boolean value but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(!value)), - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::context::Context; - use tucana::shared::{Value, value::Kind}; - - // Helper function to create a bool value - fn create_bool_value(b: bool) -> Value { - Value { - kind: Some(Kind::BoolValue(b)), - } - } - - // Helper function to create a number value - fn create_number_value(num: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(num)), - } - } - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), - } - } - - // Helper function to create an invalid value (no kind) - fn create_invalid_value() -> Value { - Value { kind: None } - } - - #[test] - fn test_as_number_success() { - let mut ctx = Context::new(); - - // Test true -> 1.0 - let values = vec![create_bool_value(true)]; - let signal = as_number(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), - _ => panic!("Expected NumberValue"), - } - - // Test false -> 0.0 - let values = vec![create_bool_value(false)]; - let signal = as_number(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_as_number_error() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters (none) - let values = vec![]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong number of parameters (too many) - let values = vec![create_bool_value(true), create_bool_value(false)]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (number) - let values = vec![create_number_value(5.0)]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (string) - let values = vec![create_string_value("hello")]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_as_text_success() { - let mut ctx = Context::new(); - - // Test true -> "true" - let values = vec![create_bool_value(true)]; - let signal = as_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "true"), - _ => panic!("Expected StringValue"), - } - - // Test false -> "false" - let values = vec![create_bool_value(false)]; - let signal = as_text(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "false"), - _ => panic!("Expected StringValue"), - } - } - - #[test] - fn test_as_text_error() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters (none) - let values = vec![]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong number of parameters (too many) - let values = vec![create_bool_value(true), create_bool_value(false)]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (number) - let values = vec![create_number_value(5.0)]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (string) - let values = vec![create_string_value("hello")]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_from_number_success() { - let mut ctx = Context::new(); - - // Test 0.0 -> false - let values = vec![create_number_value(0.0)]; - let signal = from_number(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - - // Test positive number -> true - let values = vec![create_number_value(5.0)]; - let signal = from_number(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test negative number -> true - let values = vec![create_number_value(-3.5)]; - let signal = from_number(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test -0.0 -> false - let values = vec![create_number_value(-0.0)]; - let signal = from_number(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_from_number_error() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters (none) - let values = vec![]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong number of parameters (too many) - let values = vec![create_number_value(5.0), create_number_value(3.0)]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (bool) - let values = vec![create_bool_value(true)]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (string) - let values = vec![create_string_value("hello")]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_from_text_success() { - let mut ctx = Context::new(); - - // Test "true" -> true - let values = vec![create_string_value("true")]; - let signal = from_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test "false" -> false - let values = vec![create_string_value("false")]; - let signal = from_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - - // Test "True" -> true (case insensitive) - let values = vec![create_string_value("True")]; - let signal = from_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test "FALSE" -> false (case insensitive) - let values = vec![create_string_value("FALSE")]; - let signal = from_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_from_text_error() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters (none) - let values = vec![]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong number of parameters (too many) - let values = vec![create_string_value("true"), create_string_value("false")]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (bool) - let values = vec![create_bool_value(true)]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (number) - let values = vec![create_number_value(5.0)]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with unparseable text - let values = vec![create_string_value("hello")]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with numeric string - let values = vec![create_string_value("123")]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with empty string - let values = vec![create_string_value("")]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_equal_success() { - let mut ctx = Context::new(); - - // Test true == true -> true - let values = vec![create_bool_value(true), create_bool_value(true)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test false == false -> true - let values = vec![create_bool_value(false), create_bool_value(false)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test true == false -> false - let values = vec![create_bool_value(true), create_bool_value(false)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - - // Test false == true -> false - let values = vec![create_bool_value(false), create_bool_value(true)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_is_equal_error() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters (none) - let values = vec![]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong number of parameters (one) - let values = vec![create_bool_value(true)]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong number of parameters (too many) - let values = vec![ - create_bool_value(true), - create_bool_value(false), - create_bool_value(true), - ]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (first parameter) - let values = vec![create_number_value(5.0), create_bool_value(true)]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (second parameter) - let values = vec![create_bool_value(true), create_string_value("hello")]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid values - let values = vec![create_invalid_value(), create_bool_value(true)]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - let values = vec![create_bool_value(true), create_invalid_value()]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_negate_success() { - let mut ctx = Context::new(); - - // Test !true -> false - let values = vec![create_bool_value(true)]; - let signal = negate(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - - // Test !false -> true - let values = vec![create_bool_value(false)]; - let signal = negate(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_negate_error() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters (none) - let values = vec![]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong number of parameters (too many) - let values = vec![create_bool_value(true), create_bool_value(false)]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (number) - let values = vec![create_number_value(5.0)]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (string) - let values = vec![create_string_value("hello")]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } -} diff --git a/src/implementation/control.rs b/src/implementation/control.rs deleted file mode 100644 index be49084..0000000 --- a/src/implementation/control.rs +++ /dev/null @@ -1,26 +0,0 @@ -use tucana::shared::Value; - -use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; - -pub fn collect_control_functions() -> Vec<(&'static str, HandlerFn)> { - vec![ - ("std::control::stop", stop), - ("std::control::return", r#return), - ] -} - -fn stop(_values: &[Value], _ctx: &mut Context) -> Signal { - Signal::Stop -} - -fn r#return(values: &[Value], _ctx: &mut Context) -> Signal { - let [Value { kind }] = values else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one generic value but received {:?}", values), - )); - }; - - Signal::Return(Value { kind: kind.clone() }) -} diff --git a/src/implementation/number.rs b/src/implementation/number.rs deleted file mode 100644 index a5dcd67..0000000 --- a/src/implementation/number.rs +++ /dev/null @@ -1,1951 +0,0 @@ -use std::f64; - -use tucana::shared::{Value, value::Kind}; - -use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; - -pub fn collect_number_functions() -> Vec<(&'static str, HandlerFn)> { - vec![ - ("std::number::add", add), - ("std::number::multiply", multiply), - ("std::number::substract", substract), - ("std::number::divide", divide), - ("std::number::modulo", modulo), - ("std::number::abs", abs), - ("std::number::is_positive", is_positive), - ("std::number::is_greater", is_greater), - ("std::number::is_less", is_less), - ("std::number::is_zero", is_zero), - ("std::number::square", square), - ("std::number::exponential", exponential), - ("std::number::pi", pi), - ("std::number::euler", euler), - ("std::number::infinity", infinity), - ("std::number::round_up", round_up), - ("std::number::round_down", round_down), - ("std::number::round", round), - ("std::number::square_root", square_root), - ("std::number::root", root), - ("std::number::log", log), - ("std::number::ln", ln), - ("std::number::from_text", from_text), - ("std::number::as_text", as_text), - ("std::number::min", min), - ("std::number::max", max), - ("std::number::negate", negate), - ("std::number::random", random), - ("std::number::sin", sin), - ("std::number::cos", cos), - ("std::number::tan", tan), - ("std::number::arcsin", arcsin), - ("std::number::arccos", arccos), - ("std::number::arctan", arctan), - ("std::number::sinh", sinh), - ("std::number::cosh", cosh), - ("std::number::clamp", clamp), - ("std::number::is_equal", is_equal), - ] -} - -fn add(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs + rhs)), - }) -} - -fn multiply(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs * rhs)), - }) -} - -fn substract(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs - rhs)), - }) -} - -fn divide(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - if rhs == &0.0 { - return Signal::Failure(RuntimeError::simple_str( - "DivisionByZero", - "You cannot divide by zero", - )); - } - - Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs / rhs)), - }) -} - -fn modulo(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - if rhs == &0.0 { - return Signal::Failure(RuntimeError::simple_str( - "DivisionByZero", - "You cannot divide by zero", - )); - } - - Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs % rhs)), - }) -} - -fn abs(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.abs())), - }) -} - -fn is_positive(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(!value.is_sign_negative())), - }) -} - -fn is_greater(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(lhs > rhs)), - }) -} - -fn is_less(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(lhs < rhs)), - }) -} - -fn is_zero(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(value == &0.0)), - }) -} - -fn square(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.powf(2.0))), - }) -} - -fn exponential(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(base)), - }, - Value { - kind: Some(Kind::NumberValue(exponent)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(base.powf(*exponent))), - }) -} - -fn pi(_values: &[Value], _ctx: &mut Context) -> Signal { - Signal::Success(Value { - kind: Some(Kind::NumberValue(f64::consts::PI)), - }) -} - -fn euler(_values: &[Value], _ctx: &mut Context) -> Signal { - Signal::Success(Value { - kind: Some(Kind::NumberValue(f64::consts::E)), - }) -} - -fn infinity(_values: &[Value], _ctx: &mut Context) -> Signal { - Signal::Success(Value { - kind: Some(Kind::NumberValue(f64::INFINITY)), - }) -} - -fn round_up(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(decimal_places)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - let factor = 10_f64.powi(*decimal_places as i32); - - Signal::Success(Value { - kind: Some(Kind::NumberValue((value * factor).ceil() / factor)), - }) -} - -fn round_down(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(decimal_places)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - let factor = 10_f64.powi(*decimal_places as i32); - - Signal::Success(Value { - kind: Some(Kind::NumberValue((value * factor).floor() / factor)), - }) -} - -fn round(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(decimal_places)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - let factor = 10_f64.powi(*decimal_places as i32); - - Signal::Success(Value { - kind: Some(Kind::NumberValue((value * factor).round() / factor)), - }) -} - -fn square_root(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.sqrt())), - }) -} - -fn root(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(root)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.powf(*root))), - }) -} - -fn log(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(log)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.log(*log))), - }) -} - -fn ln(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.ln())), - }) -} - -fn from_text(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(string_value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one string as argument but received {:?}", values), - )); - }; - - let value: f64 = match string_value.parse() { - Ok(result) => result, - Err(_) => { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Failed to parse string as number: {}", string_value), - )); - } - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value)), - }) -} - -fn as_text(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(value.to_string())), - }) -} - -fn min(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs.min(*rhs))), - }) -} - -fn max(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs.max(*rhs))), - }) -} - -fn negate(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(-value)), - }) -} - -fn random(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(min)), - }, - Value { - kind: Some(Kind::NumberValue(max)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(rand::random_range(*min..*max))), - }) -} - -fn sin(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.sin())), - }) -} - -fn cos(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.cos())), - }) -} - -fn tan(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.tan())), - }) -} - -fn arcsin(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.asin())), - }) -} - -fn arccos(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.acos())), - }) -} - -fn arctan(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.atan())), - }) -} -fn sinh(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.sinh())), - }) -} - -fn cosh(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.cosh())), - }) -} - -fn clamp(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(min)), - }, - Value { - kind: Some(Kind::NumberValue(max)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected three numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.clamp(*min, *max))), - }) -} - -fn is_equal(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(lhs == rhs)), - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::context::Context; - use tucana::shared::{Value, value::Kind}; - - // Helper function to create a number value - fn create_number_value(num: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(num)), - } - } - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), - } - } - - // Helper function to create a bool value - fn create_bool_value(b: bool) -> Value { - Value { - kind: Some(Kind::BoolValue(b)), - } - } - - // Helper function to create an invalid value (no kind) - fn create_invalid_value() -> Value { - Value { kind: None } - } - - #[test] - fn test_add_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(5.0), create_number_value(3.0)]; - let signal = add(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 8.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_add_runtime_exception() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters - let values = vec![create_number_value(5.0)]; - let result = add(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value types - let values = vec![create_string_value("hello"), create_number_value(3.0)]; - let result = add(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid values - let values = vec![create_invalid_value(), create_number_value(3.0)]; - let result = add(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_multiply_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(4.0), create_number_value(2.5)]; - let signal = multiply(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 10.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_multiply_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_bool_value(true), create_number_value(3.0)]; - let result = multiply(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_substract_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(10.0), create_number_value(4.0)]; - let signal = substract(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 6.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_substract_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(5.0)]; - let result = substract(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_divide_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(15.0), create_number_value(3.0)]; - let signal = divide(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_divide_by_zero_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(10.0), create_number_value(0.0)]; - let result = divide(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_divide_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("not_a_number"), - create_number_value(2.0), - ]; - let result = divide(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_modulo_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(10.0), create_number_value(3.0)]; - let signal = modulo(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_modulo_by_zero_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(10.0), create_number_value(0.0)]; - let result = modulo(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_modulo_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_invalid_value(), create_number_value(3.0)]; - let result = modulo(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_abs_success() { - let mut ctx = Context::new(); - - // Test positive number - let values = vec![create_number_value(5.0)]; - let signal = abs(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), - _ => panic!("Expected NumberValue"), - } - - // Test negative number - let values = vec![create_number_value(-7.5)]; - let signal = abs(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 7.5), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_abs_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("not_a_number")]; - let result = abs(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_positive_success() { - let mut ctx = Context::new(); - - // Test positive number - let values = vec![create_number_value(5.0)]; - let signal = is_positive(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test negative number - let values = vec![create_number_value(-5.0)]; - let signal = is_positive(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - - // Test zero - let values = vec![create_number_value(0.0)]; - let signal = is_positive(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_is_positive_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_bool_value(true)]; - let result = is_positive(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_greater_success() { - let mut ctx = Context::new(); - - // Test greater - let values = vec![create_number_value(10.0), create_number_value(5.0)]; - let signal = is_greater(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test not greater - let values = vec![create_number_value(3.0), create_number_value(7.0)]; - let signal = is_greater(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_is_greater_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![ - create_number_value(5.0), - create_string_value("not_a_number"), - ]; - let result = is_greater(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_less_success() { - let mut ctx = Context::new(); - - // Test less - let values = vec![create_number_value(3.0), create_number_value(7.0)]; - let signal = is_less(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test not less - let values = vec![create_number_value(10.0), create_number_value(5.0)]; - let signal = is_less(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_is_less_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_invalid_value(), create_number_value(5.0)]; - let result = is_less(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_zero_success() { - let mut ctx = Context::new(); - - // Test zero - let values = vec![create_number_value(0.0)]; - let signal = is_zero(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test non-zero - let values = vec![create_number_value(5.0)]; - let signal = is_zero(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_is_zero_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("zero")]; - let result = is_zero(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_square_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(4.0)]; - let signal = square(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 16.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_square_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_bool_value(false)]; - let result = square(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_exponential_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(2.0), create_number_value(3.0)]; - let signal = exponential(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 8.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_exponential_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(2.0)]; - let result = exponential(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_pi_success() { - let mut ctx = Context::new(); - let values = vec![]; - let signal = pi(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => { - assert!((val - std::f64::consts::PI).abs() < f64::EPSILON) - } - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_euler_success() { - let mut ctx = Context::new(); - let values = vec![]; - let signal = euler(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => { - assert!((val - std::f64::consts::E).abs() < f64::EPSILON) - } - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_infinity_success() { - let mut ctx = Context::new(); - let values = vec![]; - let signal = infinity(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!(val.is_infinite() && val.is_sign_positive()), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_round_up_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.14159), create_number_value(2.0)]; - let signal = round_up(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.15), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_round_up_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("3.14"), create_number_value(2.0)]; - let result = round_up(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_round_down_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.14159), create_number_value(2.0)]; - let signal = round_down(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.14), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_round_down_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.14), create_invalid_value()]; - let result = round_down(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_round_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.14159), create_number_value(2.0)]; - let signal = round(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.14), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_round_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_bool_value(true), create_number_value(2.0)]; - let result = round(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_square_root_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(16.0)]; - let signal = square_root(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 4.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_square_root_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("sixteen")]; - let result = square_root(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_root_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(8.0), create_number_value(1.0 / 3.0)]; - let signal = root(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 2.0).abs() < 0.001), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_root_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(8.0)]; - let result = root(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_log_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(100.0), create_number_value(10.0)]; - let signal = log(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 2.0).abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_log_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_invalid_value(), create_number_value(10.0)]; - let result = log(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_ln_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(std::f64::consts::E)]; - let signal = ln(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_ln_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_bool_value(true)]; - let result = ln(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_from_text_success() { - let mut ctx = Context::new(); - let values = vec![create_string_value("42.5")]; - let signal = from_text(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 42.5), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_from_text_runtime_exception() { - let mut ctx = Context::new(); - - // Test with invalid string - let values = vec![create_string_value("not_a_number")]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type - let values = vec![create_number_value(42.0)]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_as_text_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(42.5)]; - let signal = as_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "42.5"), - _ => panic!("Expected StringValue"), - } - } - - #[test] - fn test_as_text_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("already_text")]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_min_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.0), create_number_value(7.0)]; - let signal = min(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_min_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.0), create_bool_value(false)]; - let result = min(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_max_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.0), create_number_value(7.0)]; - let signal = max(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 7.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_max_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("three"), create_number_value(7.0)]; - let result = max(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_negate_success() { - let mut ctx = Context::new(); - - // Test positive number - let values = vec![create_number_value(5.0)]; - let signal = negate(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, -5.0), - _ => panic!("Expected NumberValue"), - } - - // Test negative number - let values = vec![create_number_value(-3.0)]; - let signal = negate(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_negate_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_invalid_value()]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_random_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0), create_number_value(10.0)]; - let signal = random(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => { - assert!(val >= 1.0 && val < 10.0); - } - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_random_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0), create_string_value("ten")]; - let result = random(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_sin_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(std::f64::consts::PI / 2.0)]; - let signal = sin(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_sin_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_bool_value(true)]; - let result = sin(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_cos_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(0.0)]; - let signal = cos(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_cos_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("zero")]; - let result = cos(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_tan_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(std::f64::consts::PI / 4.0)]; - let signal = tan(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < 0.0001), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_tan_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_invalid_value()]; - let result = tan(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_arcsin_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0)]; - let signal = arcsin(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => { - assert!((val - std::f64::consts::PI / 2.0).abs() < f64::EPSILON) - } - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_arcsin_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_bool_value(false)]; - let result = arcsin(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_arccos_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0)]; - let signal = arccos(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!(val.abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_arccos_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("one")]; - let result = arccos(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_arctan_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0)]; - let signal = arctan(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => { - assert!((val - std::f64::consts::PI / 4.0).abs() < f64::EPSILON) - } - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_arctan_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_invalid_value()]; - let result = arctan(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_sinh_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(0.0)]; - let signal = sinh(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!(val.abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_sinh_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_bool_value(true)]; - let result = sinh(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_cosh_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(0.0)]; - let signal = cosh(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_cosh_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_string_value("zero")]; - let result = cosh(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_clamp_success() { - let mut ctx = Context::new(); - - // Test value within range - let values = vec![ - create_number_value(5.0), - create_number_value(1.0), - create_number_value(10.0), - ]; - let signal = clamp(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), - _ => panic!("Expected NumberValue"), - } - - // Test value below range - let values = vec![ - create_number_value(-5.0), - create_number_value(1.0), - create_number_value(10.0), - ]; - let signal = clamp(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), - _ => panic!("Expected NumberValue"), - } - - // Test value above range - let values = vec![ - create_number_value(15.0), - create_number_value(1.0), - create_number_value(10.0), - ]; - let signal = clamp(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 10.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_clamp_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(5.0), create_string_value("one")]; - let result = clamp(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_equal_success() { - let mut ctx = Context::new(); - - // Test equal numbers - let values = vec![create_number_value(5.0), create_number_value(5.0)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test unequal numbers - let values = vec![create_number_value(5.0), create_number_value(3.0)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_is_equal_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(5.0), create_bool_value(true)]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } -} diff --git a/src/implementation/object.rs b/src/implementation/object.rs deleted file mode 100644 index db3864a..0000000 --- a/src/implementation/object.rs +++ /dev/null @@ -1,634 +0,0 @@ -use tucana::shared::{Value, value::Kind}; - -use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; - -pub fn collect_object_functions() -> Vec<(&'static str, HandlerFn)> { - vec![ - ("std::object::contains_key", contains_key), - ("std::object::keys", keys), - ("std::object::size", size), - ("std::object::set", set), - ] -} - -fn contains_key(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(object)), - }, - Value { - kind: Some(Kind::StringValue(key)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an object and a text as arguments but recieved: {:?}", - values - ), - )); - }; - - let contains = object.fields.contains_key(key); - - Signal::Success(Value { - kind: Some(Kind::BoolValue(contains)), - }) -} - -fn size(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(object)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an object as an argument but received {:?}", - values - ), - )); - }; - Signal::Success(Value { - kind: Some(Kind::NumberValue(object.fields.len() as f64)), - }) -} - -fn keys(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(object)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an object as an argument but received {:?}", - values - ), - )); - }; - - let keys = object - .fields - .keys() - .map(|key| Value { - kind: Some(Kind::StringValue(key.clone())), - }) - .collect::>(); - - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { values: keys })), - }) -} - -fn set(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(object)), - }, - Value { - kind: Some(Kind::StringValue(key)), - }, - value, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an object as an argument but received {:?}", - values - ), - )); - }; - - let mut new_object = object.clone(); - new_object.fields.insert(key.clone(), value.clone()); - - Signal::Success(Value { - kind: Some(Kind::StructValue(new_object)), - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::context::Context; - use std::collections::HashMap; - use tucana::shared::{Value, value::Kind}; - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), - } - } - - // Helper function to create a number value - fn create_number_value(num: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(num)), - } - } - - // Helper function to create a bool value - fn create_bool_value(b: bool) -> Value { - Value { - kind: Some(Kind::BoolValue(b)), - } - } - - // Helper function to create an object/struct value - fn create_object_value(fields: HashMap) -> Value { - Value { - kind: Some(Kind::StructValue(tucana::shared::Struct { fields })), - } - } - - // Helper function to create an empty object - fn create_empty_object() -> Value { - create_object_value(HashMap::new()) - } - - // Helper function to create a test object with some fields - fn create_test_object() -> Value { - let mut fields = HashMap::new(); - fields.insert("name".to_string(), create_string_value("John")); - fields.insert("age".to_string(), create_number_value(30.0)); - fields.insert("active".to_string(), create_bool_value(true)); - create_object_value(fields) - } - - // Helper function to create an invalid value (no kind) - fn create_invalid_value() -> Value { - Value { kind: None } - } - - #[test] - fn test_contains_key_success() { - let mut ctx = Context::new(); - let test_object = create_test_object(); - - // Test existing key - let values = vec![test_object.clone(), create_string_value("name")]; - let signal = contains_key(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test non-existing key - let values = vec![test_object, create_string_value("nonexistent")]; - let signal = contains_key(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_contains_key_empty_object() { - let mut ctx = Context::new(); - let empty_object = create_empty_object(); - - let values = vec![empty_object, create_string_value("any_key")]; - let signal = contains_key(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_contains_key_runtime_exception() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters - let values = vec![create_test_object()]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong first parameter type (not an object) - let values = vec![ - create_string_value("not_an_object"), - create_string_value("key"), - ]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong second parameter type (not a string) - let values = vec![create_test_object(), create_number_value(123.0)]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid values - let values = vec![create_invalid_value(), create_string_value("key")]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with too many parameters - let values = vec![ - create_test_object(), - create_string_value("key"), - create_string_value("extra"), - ]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_size_success() { - let mut ctx = Context::new(); - - // Test with object containing fields - let test_object = create_test_object(); - let values = vec![test_object]; - let signal = size(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.0), // name, age, active - _ => panic!("Expected NumberValue"), - } - - // Test with empty object - let empty_object = create_empty_object(); - let values = vec![empty_object]; - let signal = size(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_size_runtime_exception() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters (no parameters) - let values = vec![]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong parameter type - let values = vec![create_string_value("not_an_object")]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with too many parameters - let values = vec![create_test_object(), create_string_value("extra")]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_keys_success() { - let mut ctx = Context::new(); - - // Test with object containing fields - let test_object = create_test_object(); - let values = vec![test_object]; - let signal = keys(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 3); - - // Convert to strings to check if all expected keys are present - let mut key_strings: Vec = list - .values - .iter() - .filter_map(|v| match &v.kind { - Some(Kind::StringValue(s)) => Some(s.clone()), - _ => None, - }) - .collect(); - key_strings.sort(); - - let mut expected = - vec!["active".to_string(), "age".to_string(), "name".to_string()]; - expected.sort(); - - assert_eq!(key_strings, expected); - } - _ => panic!("Expected ListValue"), - } - - // Test with empty object - let empty_object = create_empty_object(); - let values = vec![empty_object]; - let signal = keys(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => assert_eq!(list.values.len(), 0), - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_keys_runtime_exception() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters - let values = vec![]; - let result = keys(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong parameter type - let values = vec![create_number_value(42.0)]; - let result = keys(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = keys(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with too many parameters - let values = vec![create_test_object(), create_string_value("extra")]; - let result = keys(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_set_success() { - let mut ctx = Context::new(); - - // Test setting a new key - let test_object = create_test_object(); - let values = vec![ - test_object.clone(), - create_string_value("email"), - create_string_value("john@example.com"), - ]; - let signal = set(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::StructValue(struct_val)) => { - assert_eq!(struct_val.fields.len(), 4); // original 3 + 1 new - assert!(struct_val.fields.contains_key("email")); - - match struct_val.fields.get("email") { - Some(Value { - kind: Some(Kind::StringValue(email)), - }) => { - assert_eq!(email, "john@example.com"); - } - _ => panic!("Expected email field to be a string"), - } - } - _ => panic!("Expected StructValue"), - } - - // Test overwriting an existing key - let values = vec![ - test_object, - create_string_value("age"), - create_number_value(31.0), - ]; - let signal = set(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::StructValue(struct_val)) => { - assert_eq!(struct_val.fields.len(), 3); // same number of fields - - match struct_val.fields.get("age") { - Some(Value { - kind: Some(Kind::NumberValue(age)), - }) => { - assert_eq!(*age, 31.0); - } - _ => panic!("Expected age field to be a number"), - } - } - _ => panic!("Expected StructValue"), - } - } - - #[test] - fn test_set_with_empty_object() { - let mut ctx = Context::new(); - let empty_object = create_empty_object(); - - let values = vec![ - empty_object, - create_string_value("first_key"), - create_bool_value(true), - ]; - let signal = set(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::StructValue(struct_val)) => { - assert_eq!(struct_val.fields.len(), 1); - assert!(struct_val.fields.contains_key("first_key")); - - match struct_val.fields.get("first_key") { - Some(Value { - kind: Some(Kind::BoolValue(val)), - }) => { - assert_eq!(*val, true); - } - _ => panic!("Expected first_key field to be a boolean"), - } - } - _ => panic!("Expected StructValue"), - } - } - - #[test] - fn test_set_with_different_value_types() { - let mut ctx = Context::new(); - let test_object = create_test_object(); - - // Test setting with a nested object - let mut nested_fields = HashMap::new(); - nested_fields.insert("street".to_string(), create_string_value("123 Main St")); - let nested_object = create_object_value(nested_fields); - - let values = vec![test_object, create_string_value("address"), nested_object]; - let signal = set(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::StructValue(struct_val)) => { - assert!(struct_val.fields.contains_key("address")); - - match struct_val.fields.get("address") { - Some(Value { - kind: Some(Kind::StructValue(_)), - }) => { - // Successfully set nested object - } - _ => panic!("Expected address field to be a struct"), - } - } - _ => panic!("Expected StructValue"), - } - } - - #[test] - fn test_set_runtime_exception() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters (too few) - let values = vec![create_test_object(), create_string_value("key")]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong first parameter type (not an object) - let values = vec![ - create_string_value("not_an_object"), - create_string_value("key"), - create_string_value("value"), - ]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong second parameter type (not a string key) - let values = vec![ - create_test_object(), - create_number_value(123.0), - create_string_value("value"), - ]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid values - let values = vec![ - create_invalid_value(), - create_string_value("key"), - create_string_value("value"), - ]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with no parameters - let values = vec![]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with too many parameters - let values = vec![ - create_test_object(), - create_string_value("key"), - create_string_value("value"), - create_string_value("extra"), - ]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_set_preserves_original_object() { - let mut ctx = Context::new(); - let original_object = create_test_object(); - - // Get the original size - let original_size = match &original_object.kind { - Some(Kind::StructValue(struct_val)) => struct_val.fields.len(), - _ => panic!("Expected StructValue"), - }; - - let values = vec![ - original_object.clone(), - create_string_value("new_key"), - create_string_value("new_value"), - ]; - let signal = set(&values, &mut ctx); - - let _value = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - // Verify original object is unchanged - match &original_object.kind { - Some(Kind::StructValue(struct_val)) => { - assert_eq!(struct_val.fields.len(), original_size); - assert!(!struct_val.fields.contains_key("new_key")); - } - _ => panic!("Expected StructValue"), - } - } - - #[test] - fn test_function_name_mapping() { - // Test that the function names in collect_object_functions match expected patterns - let functions = collect_object_functions(); - - assert_eq!(functions.len(), 4); - - let function_names: Vec<&str> = functions.iter().map(|(name, _)| *name).collect(); - assert!(function_names.contains(&"std::object::contains_key")); - assert!(function_names.contains(&"std::object::keys")); - assert!(function_names.contains(&"std::object::size")); - assert!(function_names.contains(&"std::object::set")); - } -} diff --git a/src/implementation/text.rs b/src/implementation/text.rs deleted file mode 100644 index 85f8854..0000000 --- a/src/implementation/text.rs +++ /dev/null @@ -1,2229 +0,0 @@ -use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; -use base64::Engine; -use tucana::shared::{ListValue, Value, value::Kind}; - -pub fn collect_text_functions() -> Vec<(&'static str, HandlerFn)> { - vec![ - ("std::text::as_bytes", as_bytes), - ("std::text::byte_size", byte_size), - ("std::text::capitalize", capitalize), - ("std::text::lowercase", lowercase), - ("std::text::uppercase", uppercase), - ("std::text::swapcase", swapcase), - ("std::text::trim", trim), - ("std::text::chars", chars), - ("std::text::at", at), - ("std::text::append", append), - ("std::text::prepend", prepend), - ("std::text::insert", insert), - ("std::text::length", length), - ("std::text::reverse", reverse), - ("std::text::remove", remove), - ("std::text::replace", replace), - ("std::text::replace_first", replace_first), - ("std::text::replace_last", replace_last), - ("std::text::hex", hex), - ("std::text::octal", octal), - ("std::text::index_of", index_of), - ("std::text::contains", contains), - ("std::text::split", split), - ("std::text::starts_with", starts_with), - ("std::text::ends_with", ends_with), - ("std::text::to_ascii", to_ascii), - ("std::text::from_ascii", from_ascii), - ("std::text::encode", encode), - ("std::text::decode", decode), - ("std::text::is_equal", is_equal), - ] -} - -fn as_bytes(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one string as argument but received {:?}", values), - )); - }; - - let bytes: Vec = value - .as_bytes() - .iter() - .map(|byte| Value { - kind: Some(Kind::NumberValue(*byte as f64)), - }) - .collect(); - - Signal::Success(Value { - kind: Some(Kind::ListValue(ListValue { values: bytes })), - }) -} - -fn byte_size(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.len() as f64)), - }) -} - -fn capitalize(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - let capitalized_value = value - .split(" ") - .map(|word| { - if word.is_empty() { - return String::from(word); - } - - if word.len() == 1 { - return word.to_uppercase(); - } - - let first = word.chars().next(); - - if first.is_some() { - let first = first.unwrap(); - String::from(first).to_uppercase() + &word[1..] - } else { - String::from(word) - } - }) - .collect::>() - .join(" "); - - Signal::Success(Value { - kind: Some(Kind::StringValue(capitalized_value)), - }) -} - -fn uppercase(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(value.to_uppercase())), - }) -} - -fn lowercase(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(value.to_lowercase())), - }) -} - -fn swapcase(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - let swapped = value - .chars() - .map(|c| { - if c.is_uppercase() { - c.to_lowercase().collect::() - } else if c.is_lowercase() { - c.to_uppercase().collect::() - } else { - c.to_string() - } - }) - .collect(); - - Signal::Success(Value { - kind: Some(Kind::StringValue(swapped)), - }) -} - -fn chars(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - let chars = value - .chars() - .map(|c| Value { - kind: Some(Kind::StringValue(c.to_string())), - }) - .collect::>(); - - Signal::Success(Value { - kind: Some(Kind::ListValue(ListValue { values: chars })), - }) -} - -fn at(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(index)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string followed by one number as arguments but received {:?}", - values - ), - )); - }; - - if index < &0.0 { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected a positive number as the second argument but received {}", - index - ), - )); - } - - let usize_index = *index as usize; - let char = value.chars().nth(usize_index); - - match char { - Some(c) => Signal::Success(Value { - kind: Some(Kind::StringValue(c.to_string())), - }), - None => Signal::Failure(RuntimeError::simple( - "IndexOutOfBoundsRuntimeError", - format!( - "Index {} is out of bounds for string of length {}", - index, - value.len() - ), - )), - } -} - -fn trim(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(value.trim().to_string())), - }) -} - -fn append(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(suffix)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two numbers as argument but received {:?}", values), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(value.clone() + suffix)), - }) -} - -fn prepend(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(prefix)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(prefix.clone() + value)), - }) -} - -fn insert(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(position)), - }, - Value { - kind: Some(Kind::StringValue(text)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected string, number, and string as arguments but received {:?}", - values - ), - )); - }; - - let usize_position = *position as usize; - let mut new_value = value.clone(); - new_value.insert_str(usize_position, text.as_str()); - - Signal::Success(Value { - kind: Some(Kind::StringValue(new_value)), - }) -} - -fn length(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value.chars().count() as f64)), - }) -} - -fn remove(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(from)), - }, - Value { - kind: Some(Kind::NumberValue(to)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string followed by two numbers as arguments but received {:?}", - values - ), - )); - }; - - let chars = value.chars().collect::>(); - - let new = chars - .into_iter() - .enumerate() - .filter(|&(i, _)| i < *from as usize || i >= *to as usize) - .map(|e| e.1) - .collect::(); - - Signal::Success(Value { - kind: Some(Kind::StringValue(new)), - }) -} - -fn replace(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(old)), - }, - Value { - kind: Some(Kind::StringValue(new)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected three strings as arguments but received {:?}", - values - ), - )); - }; - - let replaced = value.replace(old, new); - - Signal::Success(Value { - kind: Some(Kind::StringValue(replaced)), - }) -} - -fn replace_first(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(old)), - }, - Value { - kind: Some(Kind::StringValue(new)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected three strings as arguments but received {:?}", - values - ), - )); - }; - - let replaced = value.replacen(old, new, 1); - - Signal::Success(Value { - kind: Some(Kind::StringValue(replaced)), - }) -} - -fn replace_last(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(old)), - }, - Value { - kind: Some(Kind::StringValue(new)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected three strings as arguments but received {:?}", - values - ), - )); - }; - - fn replace_last(haystack: &str, needle: &str, replacement: &str) -> String { - if let Some(pos) = haystack.rfind(needle) { - let mut result = - String::with_capacity(haystack.len() - needle.len() + replacement.len()); - result.push_str(&haystack[..pos]); - result.push_str(replacement); - result.push_str(&haystack[pos + needle.len()..]); - result - } else { - haystack.to_string() // kein Vorkommen gefunden, original zurückgeben - } - } - - let replaced = replace_last(value.as_str(), old.as_str(), new.as_str()); - - Signal::Success(Value { - kind: Some(Kind::StringValue(replaced)), - }) -} - -fn hex(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - let hex = value - .as_bytes() - .iter() - .map(|byte| format!("{:02x}", byte)) - .collect::(); - - Signal::Success(Value { - kind: Some(Kind::StringValue(hex)), - }) -} - -fn octal(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - let hex = value - .as_bytes() - .iter() - .map(|byte| format!("{:03o}", byte)) - .collect::(); - - Signal::Success(Value { - kind: Some(Kind::StringValue(hex)), - }) -} - -fn index_of(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(sub_string)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - let index_option = value.find(sub_string); - - match index_option { - Some(index) => Signal::Success(Value { - kind: Some(Kind::NumberValue(index as f64)), - }), - None => Signal::Success(Value { - kind: Some(Kind::NumberValue(-1.0)), - }), - } -} - -fn contains(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(sub_string)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(value.contains(sub_string))), - }) -} - -fn split(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(delimiter)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - let words = value - .split(delimiter) - .map(|word| Value { - kind: Some(Kind::StringValue(word.to_string())), - }) - .collect::>(); - - Signal::Success(Value { - kind: Some(Kind::ListValue(ListValue { values: words })), - }) -} - -fn reverse(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - let reversed = value.chars().rev().collect::(); - - Signal::Success(Value { - kind: Some(Kind::StringValue(reversed)), - }) -} - -fn starts_with(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(prefix)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(value.starts_with(prefix))), - }) -} - -fn ends_with(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(suffix)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(value.ends_with(suffix))), - }) -} - -fn to_ascii(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - - let ascii_value = value - .bytes() - .map(|b| Value { - kind: Some(Kind::NumberValue(b as f64)), - }) - .collect::>(); - - Signal::Success(Value { - kind: Some(Kind::ListValue(ListValue { - values: ascii_value, - })), - }) -} - -fn from_ascii(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(list)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected a list of numbers as an argument but received {:?}", - values - ), - )); - }; - - let string = list - .values - .iter() - .map(|number_value| { - if let Value { - kind: Some(Kind::NumberValue(number)), - } = number_value - { - if (&0.0..=&127.0).contains(&number) { - Some(*number as u8 as char) - } else { - None - } - } else { - None - } - }) - .collect::>(); - - match string { - Some(string) => Signal::Success(Value { - kind: Some(Kind::StringValue(string)), - }), - None => Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - "Expected a list of numbers between 0 and 127".to_string(), - )), - } -} - -//TODO: Implement encode function , what about decode? UTF-8, 16 and 32 does not make sense - -fn encode(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(encoding)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - let encoded_string = match encoding.clone().to_lowercase().as_str() { - "base64" => base64::prelude::BASE64_STANDARD.encode(value), - _ => { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Unsupported encoding: {}", encoding), - )); - } - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(encoded_string)), - }) -} - -fn decode(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(encoding)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - let decoded_string = match encoding.clone().to_lowercase().as_str() { - "base64" => match base64::prelude::BASE64_STANDARD.decode(value) { - Ok(bytes) => match String::from_utf8(bytes) { - Ok(string) => string, - Err(err) => { - return Signal::Failure(RuntimeError::simple( - "DecodeError", - format!("Failed to decode base64 string: {:?}", err), - )); - } - }, - Err(err) => { - return Signal::Failure(RuntimeError::simple( - "DecodeError", - format!("Failed to decode base64 string: {:?}", err), - )); - } - }, - _ => { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Unsupported decoding: {}", encoding), - )); - } - }; - - Signal::Success(Value { - kind: Some(Kind::StringValue(decoded_string)), - }) -} - -fn is_equal(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(lhs)), - }, - Value { - kind: Some(Kind::StringValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(lhs == rhs)), - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::context::Context; - use tucana::shared::{ListValue, Value, value::Kind}; - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), - } - } - - // Helper function to create a number value - fn create_number_value(num: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(num)), - } - } - - // Helper function to create a bool value - fn create_bool_value(b: bool) -> Value { - Value { - kind: Some(Kind::BoolValue(b)), - } - } - - // Helper function to create a list value - fn create_list_value(values: Vec) -> Value { - Value { - kind: Some(Kind::ListValue(ListValue { values })), - } - } - - #[test] - fn test_as_bytes_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = as_bytes(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 5); - assert_eq!(list.values[0], create_number_value(104.0)); // 'h' - assert_eq!(list.values[1], create_number_value(101.0)); // 'e' - assert_eq!(list.values[2], create_number_value(108.0)); // 'l' - assert_eq!(list.values[3], create_number_value(108.0)); // 'l' - assert_eq!(list.values[4], create_number_value(111.0)); // 'o' - } else { - panic!("Expected ListValue"); - } - } - - #[test] - fn test_as_bytes_empty_string() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = as_bytes(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 0); - } else { - panic!("Expected ListValue"); - } - } - - #[test] - fn test_as_bytes_invalid_argument() { - let mut ctx = Context::new(); - let values = vec![create_number_value(123.0)]; - let result = as_bytes(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_byte_size_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = byte_size(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(5.0)); - } - - #[test] - fn test_byte_size_empty() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = byte_size(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(0.0)); - } - - #[test] - fn test_byte_size_unicode() { - let mut ctx = Context::new(); - let values = vec![create_string_value("café")]; - let signal = byte_size(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(5.0)); // 'é' is 2 bytes in UTF-8 - } - - #[test] - fn test_capitalize_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello world")]; - let signal = capitalize(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("Hello World")); - } - - #[test] - fn test_capitalize_empty() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = capitalize(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("")); - } - - #[test] - fn test_capitalize_single_char() { - let mut ctx = Context::new(); - let values = vec![create_string_value("a")]; - let signal = capitalize(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("A")); - } - - #[test] - fn test_uppercase_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("Hello World")]; - let signal = uppercase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("HELLO WORLD")); - } - - #[test] - fn test_uppercase_already_upper() { - let mut ctx = Context::new(); - let values = vec![create_string_value("HELLO")]; - let signal = uppercase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("HELLO")); - } - - #[test] - fn test_lowercase_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("Hello World")]; - let signal = lowercase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world")); - } - - #[test] - fn test_lowercase_already_lower() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = lowercase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello")); - } - - #[test] - fn test_swapcase_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("Hello World")]; - let signal = swapcase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hELLO wORLD")); - } - - #[test] - fn test_swapcase_mixed() { - let mut ctx = Context::new(); - let values = vec![create_string_value("HeLLo123")]; - let signal = swapcase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hEllO123")); - } - - #[test] - fn test_chars_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("abc")]; - let signal = chars(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 3); - assert_eq!(list.values[0], create_string_value("a")); - assert_eq!(list.values[1], create_string_value("b")); - assert_eq!(list.values[2], create_string_value("c")); - } else { - panic!("Expected ListValue"); - } - } - - #[test] - fn test_chars_empty() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = chars(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 0); - } else { - panic!("Expected ListValue"); - } - } - - #[test] - fn test_at_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_number_value(1.0)]; - let signal = at(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("e")); - } - - #[test] - fn test_at_first_char() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_number_value(0.0)]; - let signal = at(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("h")); - } - - #[test] - fn test_at_out_of_bounds() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_number_value(10.0)]; - let result = at(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_at_negative_index() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_number_value(-1.0)]; - let result = at(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_trim_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value(" hello world ")]; - let signal = trim(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world")); - } - - #[test] - fn test_trim_no_whitespace() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = trim(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello")); - } - - #[test] - fn test_append_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value(" world")]; - let signal = append(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world")); - } - - #[test] - fn test_append_empty_suffix() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value("")]; - let signal = append(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello")); - } - - #[test] - fn test_prepend_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("world"), create_string_value("hello ")]; - let signal = prepend(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world")); - } - - #[test] - fn test_prepend_empty_prefix() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value("")]; - let signal = prepend(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello")); - } - - #[test] - fn test_insert_valid() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello"), - create_number_value(2.0), - create_string_value("XXX"), - ]; - let signal = insert(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("heXXXllo")); - } - - #[test] - fn test_insert_at_beginning() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello"), - create_number_value(0.0), - create_string_value("XXX"), - ]; - let signal = insert(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("XXXhello")); - } - - #[test] - fn test_insert_at_end() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello"), - create_number_value(5.0), - create_string_value("XXX"), - ]; - let signal = insert(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("helloXXX")); - } - - #[test] - fn test_length_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = length(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(5.0)); - } - - #[test] - fn test_length_empty() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = length(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(0.0)); - } - - #[test] - fn test_length_unicode() { - let mut ctx = Context::new(); - let values = vec![create_string_value("café")]; - let signal = length(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(4.0)); // 4 characters - } - - #[test] - fn test_remove_valid() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_number_value(2.0), - create_number_value(7.0), - ]; - let signal = remove(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("heorld")); - } - - #[test] - fn test_remove_from_beginning() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello"), - create_number_value(0.0), - create_number_value(2.0), - ]; - let signal = remove(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("llo")); - } - - #[test] - fn test_replace_valid() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world hello"), - create_string_value("hello"), - create_string_value("hi"), - ]; - let signal = replace(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hi world hi")); - } - - #[test] - fn test_replace_not_found() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("xyz"), - create_string_value("abc"), - ]; - let signal = replace(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world")); - } - - #[test] - fn test_replace_first_valid() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world hello"), - create_string_value("hello"), - create_string_value("hi"), - ]; - let signal = replace_first(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hi world hello")); - } - - #[test] - fn test_replace_last_valid() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world hello"), - create_string_value("hello"), - create_string_value("hi"), - ]; - let signal = replace_last(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world hi")); - } - - #[test] - fn test_replace_last_not_found() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("xyz"), - create_string_value("abc"), - ]; - let signal = replace_last(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world")); - } - - #[test] - fn test_hex_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = hex(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("68656c6c6f")); - } - - #[test] - fn test_hex_empty() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = hex(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("")); - } - - #[test] - fn test_octal_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("A")]; - let signal = octal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("101")); // 'A' is 65 in ASCII, 101 in octal - } - - #[test] - fn test_index_of_found() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("world"), - ]; - let signal = index_of(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(6.0)); - } - - #[test] - fn test_index_of_not_found() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("xyz"), - ]; - let signal = index_of(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(-1.0)); - } - - #[test] - fn test_index_of_at_beginning() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("hello"), - ]; - let signal = index_of(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(0.0)); - } - - #[test] - fn test_contains_true() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("world"), - ]; - let signal = contains(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(true)); - } - - #[test] - fn test_contains_false() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("xyz"), - ]; - let signal = contains(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(false)); - } - - #[test] - fn test_split_valid() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello,world,test"), - create_string_value(","), - ]; - let signal = split(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 3); - assert_eq!(list.values[0], create_string_value("hello")); - assert_eq!(list.values[1], create_string_value("world")); - assert_eq!(list.values[2], create_string_value("test")); - } else { - panic!("Expected ListValue"); - } - } - - #[test] - fn test_split_no_delimiter() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value(",")]; - let signal = split(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 1); - assert_eq!(list.values[0], create_string_value("hello")); - } else { - panic!("Expected ListValue"); - } - } - - #[test] - fn test_reverse_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = reverse(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("olleh")); - } - - #[test] - fn test_reverse_empty() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = reverse(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("")); - } - - #[test] - fn test_reverse_palindrome() { - let mut ctx = Context::new(); - let values = vec![create_string_value("aba")]; - let signal = reverse(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("aba")); - } - - #[test] - fn test_encode_invalid_parameter() { - let mut ctx = Context::new(); - let values = vec![create_string_value("aba")]; - let result = encode(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_encode_invalid_encoding() { - let mut ctx = Context::new(); - let values = vec![create_string_value("aba"), create_string_value("gug")]; - let result = encode(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_encode_correct() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value("BASE64")]; - let signal = encode(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!( - result, - Value { - kind: Some(Kind::StringValue(String::from("aGVsbG8="))) - } - ); - } - - #[test] - fn test_decode_invalid_parameter() { - let mut ctx = Context::new(); - let values = vec![create_string_value("aba")]; - let result = decode(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_decode_invalid_encoding() { - let mut ctx = Context::new(); - let values = vec![create_string_value("aba"), create_string_value("gug")]; - let result = decode(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_decode_correct() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("aGVsbG8="), - create_string_value("BASE64"), - ]; - let signal = decode(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!( - result, - Value { - kind: Some(Kind::StringValue(String::from("hello"))) - } - ); - } - - #[test] - fn test_starts_with_true() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("hello"), - ]; - let signal = starts_with(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(true)); - } - - #[test] - fn test_starts_with_false() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("world"), - ]; - let signal = starts_with(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(false)); - } - - #[test] - fn test_ends_with_true() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("world"), - ]; - let signal = ends_with(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(true)); - } - - #[test] - fn test_ends_with_false() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("hello"), - ]; - let signal = ends_with(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(false)); - } - - #[test] - fn test_to_ascii_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("AB")]; - let signal = to_ascii(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 2); - assert_eq!(list.values[0], create_number_value(65.0)); // 'A' - assert_eq!(list.values[1], create_number_value(66.0)); // 'B' - } else { - panic!("Expected ListValue"); - } - } - - #[test] - fn test_from_ascii_valid() { - let mut ctx = Context::new(); - let ascii_values = vec![ - create_number_value(65.0), // 'A' - create_number_value(66.0), // 'B' - create_number_value(67.0), // 'C' - ]; - let values = vec![create_list_value(ascii_values)]; - let signal = from_ascii(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("ABC")); - } - - #[test] - fn test_from_ascii_empty_list() { - let mut ctx = Context::new(); - let values = vec![create_list_value(vec![])]; - let signal = from_ascii(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("")); - } - - #[test] - fn test_from_ascii_invalid_range() { - let mut ctx = Context::new(); - let ascii_values = vec![ - create_number_value(65.0), - create_number_value(128.0), // Out of ASCII range - ]; - let values = vec![create_list_value(ascii_values)]; - let result = from_ascii(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_from_ascii_negative_number() { - let mut ctx = Context::new(); - let ascii_values = vec![ - create_number_value(65.0), - create_number_value(-1.0), // Negative - ]; - let values = vec![create_list_value(ascii_values)]; - let result = from_ascii(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_from_ascii_non_number_in_list() { - let mut ctx = Context::new(); - let ascii_values = vec![ - create_number_value(65.0), - create_string_value("invalid"), // Non-number - ]; - let values = vec![create_list_value(ascii_values)]; - let result = from_ascii(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_equal_true() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value("hello")]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(true)); - } - - #[test] - fn test_is_equal_false() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value("world")]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(false)); - } - - #[test] - fn test_is_equal_empty_strings() { - let mut ctx = Context::new(); - let values = vec![create_string_value(""), create_string_value("")]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(true)); - } - - // Test invalid arguments for functions requiring specific argument counts - #[test] - fn test_invalid_arguments_single_string() { - let mut ctx = Context::new(); - - // Test functions that expect 1 string argument - let invalid_values = vec![create_number_value(123.0)]; - - assert_eq!( - as_bytes(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - byte_size(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - capitalize(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - uppercase(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - lowercase(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - swapcase(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - chars(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - trim(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - length(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - reverse(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - hex(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - octal(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - to_ascii(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - } - - #[test] - fn test_invalid_arguments_two_strings() { - let mut ctx = Context::new(); - - // Test functions that expect 2 string arguments - let invalid_values = vec![create_string_value("hello"), create_number_value(123.0)]; - - assert_eq!( - append(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - prepend(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - index_of(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - contains(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - split(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - starts_with(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - ends_with(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - is_equal(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - } - - #[test] - fn test_invalid_arguments_string_and_number() { - let mut ctx = Context::new(); - - // Test functions that expect string and number - let invalid_values = vec![create_number_value(123.0), create_string_value("test")]; - - assert_eq!( - at(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - } - - #[test] - fn test_invalid_arguments_three_params() { - let mut ctx = Context::new(); - - // Test functions that expect 3 arguments - let invalid_values = vec![ - create_string_value("hello"), - create_string_value("test"), - create_number_value(123.0), - ]; - - assert_eq!( - insert(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - remove(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - replace(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - replace_first(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - replace_last(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - } - - #[test] - fn test_wrong_argument_count() { - let mut ctx = Context::new(); - - // Test with wrong number of arguments - let empty_values = vec![]; - let too_many_values = vec![ - create_string_value("test1"), - create_string_value("test2"), - create_string_value("test3"), - create_string_value("test4"), - ]; - - assert_eq!( - as_bytes(&empty_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - as_bytes(&too_many_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - - assert_eq!( - append(&empty_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - append(&too_many_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - } - - #[test] - fn test_edge_cases() { - let mut ctx = Context::new(); - - // Test with very long string - let long_string = "a".repeat(1000); - let values = vec![create_string_value(&long_string)]; - - assert_eq!(length(&values, &mut ctx), Signal::Success(Value::default())); - assert_eq!( - reverse(&values, &mut ctx), - Signal::Success(Value::default()) - ); - assert_eq!( - uppercase(&values, &mut ctx), - Signal::Success(Value::default()) - ); - - // Test with special characters - let special_string = "!@#$%^&*(){}[]|\\:;\"'<>,.?/~`"; - let values = vec![create_string_value(special_string)]; - - assert_eq!(length(&values, &mut ctx), Signal::Success(Value::default())); - assert_eq!( - uppercase(&values, &mut ctx), - Signal::Success(Value::default()) - ); - assert_eq!( - reverse(&values, &mut ctx), - Signal::Success(Value::default()) - ); - - // Test with unicode characters - let unicode_string = "🦀🚀✨🎉"; - let values = vec![create_string_value(unicode_string)]; - - assert_eq!(length(&values, &mut ctx), Signal::Success(Value::default())); - assert_eq!( - reverse(&values, &mut ctx), - Signal::Success(Value::default()) - ); - assert_eq!(chars(&values, &mut ctx), Signal::Success(Value::default())); - } - - #[test] - fn test_boundary_conditions() { - let mut ctx = Context::new(); - - // Test insert at various positions - let base_string = "hello"; - for i in 0..=5 { - let values = vec![ - create_string_value(base_string), - create_number_value(i as f64), - create_string_value("X"), - ]; - let result = insert(&values, &mut ctx); - assert_eq!(result, Signal::Success(Value::default())); - } - - // Test remove with edge cases - let values = vec![ - create_string_value("hello"), - create_number_value(0.0), - create_number_value(0.0), // Remove nothing - ]; - let signal = remove(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello")); - - // Test remove entire string - let values = vec![ - create_string_value("hello"), - create_number_value(0.0), - create_number_value(5.0), - ]; - let signal = remove(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("")); - } -} diff --git a/src/registry/mod.rs b/src/registry/mod.rs deleted file mode 100644 index b523129..0000000 --- a/src/registry/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::context::Context; -use crate::context::signal::Signal; -use std::collections::HashMap; -use tucana::shared::Value; - -pub type HandlerFn = fn(&[Value], &mut Context) -> Signal; - -/// Holds all registered handlers. -pub struct FunctionStore { - functions: HashMap, -} - -impl Default for FunctionStore { - fn default() -> Self { - Self::new() - } -} - -impl FunctionStore { - /// Create a new, empty store. - pub fn new() -> Self { - FunctionStore { - functions: HashMap::new(), - } - } - - /// Look up a handler by its ID. - pub fn get(&self, id: &str) -> Option<&HandlerFn> { - self.functions.get(id) - } - - /// Execute all the registration closures to populate the map. - pub fn populate(&mut self, regs: Vec<(&'static str, HandlerFn)>) { - for (id, func) in regs { - self.functions.insert(id.to_string(), func); - } - } -} diff --git a/taurus/Cargo.toml b/taurus/Cargo.toml new file mode 100644 index 0000000..7298d37 --- /dev/null +++ b/taurus/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "taurus" +version = "0.1.0" +edition = "2024" + +[dependencies] +code0-flow = { workspace = true } +tucana = { workspace = true } +tokio = { workspace = true } +log = { workspace = true } +futures-lite ={ workspace = true } +rand = { workspace = true } +base64 = { workspace = true } +env_logger = { workspace = true } +async-nats = { workspace = true } +prost = { workspace = true } +tonic-health = { workspace = true } +tonic = { workspace = true } diff --git a/src/config/mod.rs b/taurus/src/config/mod.rs similarity index 100% rename from src/config/mod.rs rename to taurus/src/config/mod.rs diff --git a/taurus/src/context/argument.rs b/taurus/src/context/argument.rs new file mode 100644 index 0000000..8789fe8 --- /dev/null +++ b/taurus/src/context/argument.rs @@ -0,0 +1,105 @@ +use crate::context::signal::Signal; +use crate::error::RuntimeError; +use std::convert::Infallible; +use tucana::shared::value::Kind; +use tucana::shared::{ListValue, Struct, Value}; + +#[derive(Clone, Debug)] +pub enum Argument { + // Eval => Evaluated Value + // - can be consumed directly by a function + Eval(Value), + // Thunk of NodeFunction identifier + // - used for lazy execution of nodes + Thunk(i64), +} + +#[derive(Clone, Copy, Debug)] +pub enum ParameterNode { + Eager, + Lazy, +} + +pub trait TryFromArgument: Sized { + fn try_from_argument(a: &Argument) -> Result; +} + +fn type_err(msg: &str) -> Signal { + Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + msg.to_string(), + )) +} + +impl TryFromArgument for Value { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(v) => Ok(v.clone()), + _ => Err(type_err("Expected evaluated value but got lazy thunk")), + } + } +} + +impl TryFromArgument for f64 { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { + kind: Some(Kind::NumberValue(n)), + }) => Ok(*n), + _ => Err(type_err("Expected number")), + } + } +} + +impl TryFromArgument for bool { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { + kind: Some(Kind::BoolValue(b)), + }) => Ok(*b), + _ => Err(type_err("Expected boolean")), + } + } +} + +impl TryFromArgument for String { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { + kind: Some(Kind::StringValue(s)), + }) => Ok(s.clone()), + _ => Err(type_err("Expected string")), + } + } +} + +impl TryFromArgument for Struct { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { + kind: Some(Kind::StructValue(s)), + }) => Ok(s.clone()), + _ => Err(type_err("Expected struct")), + } + } +} + +impl TryFromArgument for ListValue { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { + kind: Some(Kind::ListValue(list)), + }) => Ok(list.clone()), + _ => Err(Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected array (ListValue)", + ))), + } + } +} + +impl From for RuntimeError { + fn from(never: Infallible) -> Self { + match never {} + } +} diff --git a/taurus/src/context/context.rs b/taurus/src/context/context.rs new file mode 100644 index 0000000..3b4cdef --- /dev/null +++ b/taurus/src/context/context.rs @@ -0,0 +1,32 @@ +use crate::error::RuntimeError; +use std::collections::HashMap; +use tucana::shared::Value; + +#[derive(Clone)] +pub enum ContextResult { + Error(RuntimeError), + Success(Value), + NotFound, +} + +#[derive(Default)] +pub struct Context { + results: HashMap, +} + +impl Context { + pub fn get(&mut self, id: i64) -> ContextResult { + match self.results.get(&id) { + None => ContextResult::NotFound, + Some(result) => result.clone(), + } + } + + pub fn insert_success(&mut self, id: i64, value: Value) { + self.results.insert(id, ContextResult::Success(value)); + } + + pub fn insert_error(&mut self, id: i64, runtime_error: RuntimeError) { + self.results.insert(id, ContextResult::Error(runtime_error)); + } +} diff --git a/taurus/src/context/executor.rs b/taurus/src/context/executor.rs new file mode 100644 index 0000000..05ef442 --- /dev/null +++ b/taurus/src/context/executor.rs @@ -0,0 +1,141 @@ +use crate::context::argument::{Argument, ParameterNode}; +use crate::context::context::{Context, ContextResult}; +use crate::context::registry::FunctionStore; +use crate::context::signal::Signal; +use crate::error::RuntimeError; +use std::cell::RefCell; +use std::collections::HashMap; +use tucana::shared::NodeFunction; + +pub struct Executor<'a> { + functions: &'a FunctionStore, + nodes: HashMap, + context: RefCell, +} + +impl<'a> Executor<'a> { + pub fn new( + functions: &'a FunctionStore, + nodes: HashMap, + context: Context, + ) -> Self { + Executor { + functions, + nodes, + context: RefCell::new(context), + } + } + + pub fn execute(&self, starting_node_id: i64) -> Signal { + let mut current_node_id = starting_node_id; + + loop { + let node = match self.nodes.get(¤t_node_id) { + None => { + return Signal::Failure(RuntimeError::simple_str( + "NodeNotFound", + "The node with the id was not found", + )); + } + Some(n) => n.clone(), + }; + + let entry = match self.functions.get(node.runtime_function_id.as_str()) { + None => { + return Signal::Failure(RuntimeError::simple_str( + "FunctionNotFound", + "The function was not found", + )); + } + Some(f) => f, + }; + + let mut args: Vec = Vec::with_capacity(node.parameters.len()); + for parameter in &node.parameters { + let node_value = match ¶meter.value { + Some(v) => v, + None => { + return Signal::Failure(RuntimeError::simple_str( + "NodeValueNotFound", + "Missing parameter value", + )); + } + }; + let value = match &node_value.value { + Some(v) => v, + None => { + return Signal::Failure(RuntimeError::simple_str( + "NodeValueNotFound", + "Missing inner value", + )); + } + }; + + match value { + tucana::shared::node_value::Value::LiteralValue(val) => { + args.push(Argument::Eval(val.clone())) + } + tucana::shared::node_value::Value::ReferenceValue(reference) => { + let mut ctx = self.context.borrow_mut(); + let value = ctx.get(reference.node_id); + match value { + ContextResult::Error(runtime_error) => { + return Signal::Failure(runtime_error); + } + ContextResult::Success(result) => { + args.push(Argument::Eval(result.clone())); + } + ContextResult::NotFound => { + return Signal::Failure(RuntimeError::simple_str( + "ReferenceValueNotFound", + "The given node has not been executed but referenced.", + )); + } + } + } + tucana::shared::node_value::Value::NodeFunctionId(id) => { + args.push(Argument::Thunk(*id)) + } + } + } + + for (i, a) in args.iter_mut().enumerate() { + let mode = entry + .param_modes + .get(i) + .copied() + .unwrap_or(ParameterNode::Eager); + if matches!(mode, ParameterNode::Eager) + && let Argument::Thunk(id) = *a + { + match self.execute(id) { + Signal::Success(v) => *a = Argument::Eval(v), + s @ (Signal::Failure(_) + | Signal::Return(_) + | Signal::Respond(_) + | Signal::Stop) => return s, + } + } + } + + let mut run = |node_id: i64| self.execute(node_id); + let mut ctx = self.context.borrow_mut(); + let result = (entry.handler)(&args, &mut ctx, &mut run); + + match result { + Signal::Success(value) => { + if let Some(next_node_id) = node.next_node_id { + current_node_id = next_node_id; + continue; + } else { + return Signal::Success(value); + } + } + Signal::Failure(e) => return Signal::Failure(e), + Signal::Return(v) => return Signal::Return(v), + Signal::Respond(v) => return Signal::Respond(v), + Signal::Stop => return Signal::Stop, + } + } + } +} diff --git a/taurus/src/context/macros.rs b/taurus/src/context/macros.rs new file mode 100644 index 0000000..27f7826 --- /dev/null +++ b/taurus/src/context/macros.rs @@ -0,0 +1,43 @@ +/// Pulls typed parameters from a slice of `Argument` using your `TryFromArgument` +/// impls. Fails early with your `Signal::Failure(RuntimeError::simple(...))`. +macro_rules! args { + ($args_ident:ident => $( $name:ident : $ty:ty ),+ $(,)?) => { + // Arity check + let __expected: usize = 0usize $(+ { let _ = ::core::any::type_name::<$ty>(); 1usize })*; + if $args_ident.len() != __expected { + return $crate::context::signal::Signal::Failure( + $crate::error::RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected {__expected} args but received {}", $args_ident.len()), + ) + ); + } + + // Typed extraction + let mut __i: usize = 0; + $( + let $name: $ty = match < + $ty as $crate::context::argument::TryFromArgument + >::try_from_argument(& $args_ident[__i]) { + Ok(v) => v, + Err(sig) => return sig, + }; + __i += 1; + )+ + }; +} + +/// Asserts there are no arguments. +macro_rules! no_args { + ($args_ident:ident) => { + if !$args_ident.is_empty() { + return $crate::context::signal::Signal::Failure($crate::error::RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected 0 args but received {}", $args_ident.len()), + )); + } + }; +} + +pub(crate) use args; +pub(crate) use no_args; diff --git a/taurus/src/context/mod.rs b/taurus/src/context/mod.rs new file mode 100644 index 0000000..9ca0f43 --- /dev/null +++ b/taurus/src/context/mod.rs @@ -0,0 +1,6 @@ +pub mod argument; +pub mod context; +pub mod executor; +pub mod macros; +pub mod registry; +pub mod signal; diff --git a/taurus/src/context/registry.rs b/taurus/src/context/registry.rs new file mode 100644 index 0000000..c2b399c --- /dev/null +++ b/taurus/src/context/registry.rs @@ -0,0 +1,89 @@ +use crate::context::argument::{Argument, ParameterNode}; +use crate::context::context::Context; +use crate::context::signal::Signal; +use std::collections::HashMap; + +/// HandlerFm +/// - For eager params, the executor will already convert them to Argument::Eval(Value). +/// - For lazy params, the executor will pass Argument::Thunk(node_id). +/// - If a handler wants to execute a lazy arg, it calls run(node_id). +pub type HandlerFn = + fn(args: &[Argument], ctx: &mut Context, run: &mut dyn FnMut(i64) -> Signal) -> Signal; + +pub struct HandlerFunctionEntry { + pub handler: HandlerFn, + pub param_modes: Vec, +} + +/// Holds all registered handlers. +pub struct FunctionStore { + functions: HashMap, +} + +impl Default for FunctionStore { + fn default() -> Self { + Self::new() + } +} + +pub trait IntoFunctionEntry { + fn into_function_entry(self, param: Vec) -> HandlerFunctionEntry; + fn eager(self, param_amount: i8) -> HandlerFunctionEntry; + fn lazy(self, param_amount: i8) -> HandlerFunctionEntry; +} + +impl IntoFunctionEntry for HandlerFn { + fn into_function_entry(self, param: Vec) -> HandlerFunctionEntry { + HandlerFunctionEntry { + handler: self, + param_modes: param, + } + } + + fn eager(self, param_amount: i8) -> HandlerFunctionEntry { + let mut params = vec![]; + + for _ in 0..param_amount { + params.push(ParameterNode::Eager) + } + + HandlerFunctionEntry { + handler: self, + param_modes: params, + } + } + + fn lazy(self, param_amount: i8) -> HandlerFunctionEntry { + let mut params = vec![]; + + for _ in 0..param_amount { + params.push(ParameterNode::Lazy) + } + + HandlerFunctionEntry { + handler: self, + param_modes: params, + } + } +} + +impl FunctionStore { + /// Create a new, empty store. + pub fn new() -> Self { + FunctionStore { + functions: HashMap::new(), + } + } + + /// Look up a handler by its ID. + pub fn get(&self, id: &str) -> Option<&HandlerFunctionEntry> { + self.functions.get(id) + } + + /// Execute all the registration closures to populate the map. + pub fn populate(&mut self, regs: Vec<(&'static str, HandlerFunctionEntry)>) { + for (id, func) in regs { + self.functions.insert(id.to_string(), func); + } + } +} diff --git a/src/context/signal.rs b/taurus/src/context/signal.rs similarity index 80% rename from src/context/signal.rs rename to taurus/src/context/signal.rs index 9e0cf30..0c40add 100644 --- a/src/context/signal.rs +++ b/taurus/src/context/signal.rs @@ -6,20 +6,20 @@ pub enum Signal { // Will be signaled if a function has been executed successfully Success(Value), // Will be signaled if - // - a function recieves wrong parameter + // - a function receives wrong parameter // - a function throws an error - // - taurus itself throwns an error - // - will stop the execution of the flow completly + // - taurus itself throws an error + // - will stop the execution of the flow completely Failure(RuntimeError), // Will be signaled if the `return` function has been executed // - will break the current context and return the value to the upper node Return(Value), // Will be signaled if the `respond` function has been executed - // - will stop the execution of the flow completly + // - will stop the execution of the flow completely // - will return the value to the adapter Respond(Value), // Will be signaled if the `stop` function has been executed - // - will stop the execution of the flow completly + // - will stop the execution of the flow completely Stop, } diff --git a/src/error/mod.rs b/taurus/src/error/mod.rs similarity index 100% rename from src/error/mod.rs rename to taurus/src/error/mod.rs diff --git a/taurus/src/implementation/array.rs b/taurus/src/implementation/array.rs new file mode 100644 index 0000000..c80c0a8 --- /dev/null +++ b/taurus/src/implementation/array.rs @@ -0,0 +1,1259 @@ +use std::cmp::Ordering; + +use tucana::shared::{ListValue, Value, value::Kind}; + +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; +use crate::context::signal::Signal; +use crate::{context::context::Context, error::RuntimeError}; + +pub fn collect_array_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { + vec![ + ("std::array::at", HandlerFn::eager(at, 2)), + ("std::array::concat", HandlerFn::eager(concat, 2)), + ("std::array::filter", HandlerFn::eager(filter, 2)), + ("std::array::find", HandlerFn::eager(find, 2)), + ("std::array::find_last", HandlerFn::eager(find_last, 2)), + ("std::array::find_index", HandlerFn::eager(find_index, 2)), + ("std::array::first", HandlerFn::eager(first, 1)), + ("std::array::last", HandlerFn::eager(last, 1)), + ("std::array::for_each", HandlerFn::eager(for_each, 0)), + ("std::array::map", HandlerFn::eager(map, 2)), + ("std::array::push", HandlerFn::eager(push, 2)), + ("std::array::pop", HandlerFn::eager(pop, 1)), + ("std::array::remove", HandlerFn::eager(remove, 2)), + ("std::array::is_empty", HandlerFn::eager(is_empty, 1)), + ("std::array::size", HandlerFn::eager(size, 1)), + ("std::array::index_of", HandlerFn::eager(index_of, 2)), + ("std::array::to_unique", HandlerFn::eager(to_unique, 1)), + ("std::array::sort", HandlerFn::eager(sort, 2)), + ( + "std::array::sort_reverse", + HandlerFn::eager(sort_reverse, 2), + ), + ("std::array::reverse", HandlerFn::eager(reverse, 1)), + ("std::array::flat", HandlerFn::eager(flat, 1)), + ("std::array::min", HandlerFn::eager(min, 1)), + ("std::array::max", HandlerFn::eager(max, 1)), + ("std::array::sum", HandlerFn::eager(sum, 1)), + ("std::array::join", HandlerFn::eager(join, 2)), + ] +} + +fn at(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + // array, index + args!(args => array: ListValue, index: f64); + + if index < 0.0 { + return Signal::Failure(RuntimeError::simple_str( + "IndexOutOfBoundsRuntimeError", + "Negative index", + )); + } + let i = index as usize; + match array.values.get(i) { + Some(item) => Signal::Success(item.clone()), + None => Signal::Failure(RuntimeError::simple( + "IndexOutOfBoundsRuntimeError", + format!("Index {} out of bounds (len={})", i, array.values.len()), + )), + } +} + +fn concat(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs_v: Value, rhs_v: Value); + + let Kind::ListValue(lhs) = lhs_v.kind.clone().ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two arrays as arguments but received lhs={:?}", + lhs_v + ), + )); + }; + let Kind::ListValue(rhs) = rhs_v.kind.clone().ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two arrays as arguments but received rhs={:?}", + rhs_v + ), + )); + }; + + let mut result = lhs.values.clone(); + result.extend(rhs.values.iter().cloned()); + + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values: result })), + }) +} + +fn filter(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, predicate_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array".to_string(), + )); + }; + let Kind::ListValue(resolved_predicate) = + predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of booleans".to_string(), + )); + }; + + let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); + for v in &resolved_predicate.values { + if let Some(Kind::BoolValue(b)) = v.kind { + preds.push(b); + } + } + + let mut i = 0usize; + let new_array = array + .values + .iter() + .filter(|_| { + let keep = *preds.get(i).unwrap_or(&false); + i += 1; + keep + }) + .cloned() + .collect::>(); + + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values: new_array })), + }) +} + +fn find(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, predicate_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); + }; + let Kind::ListValue(resolved_predicate) = + predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of booleans", + )); + }; + + let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); + for v in &resolved_predicate.values { + if let Some(Kind::BoolValue(b)) = v.kind { + preds.push(b); + } + } + + let mut i = 0usize; + let item = array + .values + .iter() + .find(|&_| { + let keep = *preds.get(i).unwrap_or(&false); + i += 1; + keep + }) + .cloned(); + + match item { + Some(v) => Signal::Success(v), + None => Signal::Failure(RuntimeError::simple_str( + "NotFoundError", + "No item found that satisfies the predicate", + )), + } +} + +fn find_last(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, predicate_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); + }; + let Kind::ListValue(resolved_predicate) = + predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of booleans", + )); + }; + + let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); + for v in &resolved_predicate.values { + if let Some(Kind::BoolValue(b)) = v.kind { + preds.push(b); + } + } + + let mut i = 0usize; + let mut reversed = array.values.clone(); + reversed.reverse(); + + let item = reversed.into_iter().find(|_| { + let keep = *preds.get(i).unwrap_or(&false); + i += 1; + keep + }); + + match item { + Some(v) => Signal::Success(v), + None => Signal::Failure(RuntimeError::simple_str( + "NotFoundError", + "No item found that satisfies the predicate", + )), + } +} + +fn find_index( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => array_v: Value, predicate_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); + }; + let Kind::ListValue(resolved_predicate) = + predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of booleans", + )); + }; + + let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); + for v in &resolved_predicate.values { + if let Some(Kind::BoolValue(b)) = v.kind { + preds.push(b); + } + } + + let mut idx = 0usize; + let found = array.values.iter().find(|_| { + let keep = *preds.get(idx).unwrap_or(&false); + if !keep { + idx += 1; + } + keep + }); + + match found { + Some(_) => Signal::Success(Value { + kind: Some(Kind::NumberValue(idx as f64)), + }), + None => Signal::Failure(RuntimeError::simple_str( + "NotFoundError", + "No item found that satisfies the predicate", + )), + } +} + +fn first(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); + + match array.values.first() { + Some(v) => Signal::Success(v.clone()), + None => Signal::Failure(RuntimeError::simple_str( + "ArrayEmptyRuntimeError", + "This array is empty", + )), + } +} + +fn last(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); + match array.values.last() { + Some(v) => Signal::Success(v.clone()), + None => Signal::Failure(RuntimeError::simple_str( + "ArrayEmptyRuntimeError", + "This array is empty", + )), + } +} + +/// for_each has no implementation +/// +/// Reason: +/// The definition itself takes in an array and a node +/// The node itself will be executed on the arrays elements +/// If the node is (CONSUMER) resolved it goes in this function --> therefor all code is already executed +fn for_each(_args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + // Already executed by the engine (consumer); return Null + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) +} + +fn map(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + // (array, transformed_results[]) + args!(args => _array_v: Value, transform_v: Value); + let Kind::ListValue(transform_result) = + transform_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected transform result to be an array", + )); + }; + Signal::Success(Value { + kind: Some(Kind::ListValue(transform_result.clone())), + }) +} + +fn push(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, item: Value); + let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); + }; + array.values.push(item); + Signal::Success(Value { + kind: Some(Kind::ListValue(array)), + }) +} + +fn pop(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected an array as an argument", + )); + }; + array.values.pop(); + Signal::Success(Value { + kind: Some(Kind::ListValue(array)), + }) +} + +fn remove(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, item: Value); + let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); + }; + + if let Some(index) = array.values.iter().position(|x| *x == item) { + array.values.remove(index); + Signal::Success(Value { + kind: Some(Kind::ListValue(array)), + }) + } else { + Signal::Failure(RuntimeError::simple( + "ValueNotFoundRuntimeError", + format!("Item {:?} not found in array", item), + )) + } +} + +fn is_empty(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected an array as an argument", + )); + }; + Signal::Success(Value { + kind: Some(Kind::BoolValue(array.values.is_empty())), + }) +} + +fn size(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected an array as an argument", + )); + }; + Signal::Success(Value { + kind: Some(Kind::NumberValue(array.values.len() as f64)), + }) +} + +fn index_of(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, item: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); + }; + + match array.values.iter().position(|x| *x == item) { + Some(i) => Signal::Success(Value { + kind: Some(Kind::NumberValue(i as f64)), + }), + None => Signal::Failure(RuntimeError::simple( + "ValueNotFoundRuntimeError", + format!("Item {:?} not found in array", item), + )), + } +} + +fn to_unique(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected an array as an argument", + )); + }; + + let mut unique = Vec::::new(); + for v in &array.values { + if !unique.contains(v) { + unique.push(v.clone()); + } + } + + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values: unique })), + }) +} + +fn sort(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + // array, resolved comparator yields -1/0/1 sequence + args!(args => array_v: Value, cmp_v: Value); + let Kind::ListValue(mut arr) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); + }; + let Kind::ListValue(cmp_vals) = cmp_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of numbers", + )); + }; + + let mut comps: Vec = Vec::new(); + for v in &cmp_vals.values { + if let Some(Kind::NumberValue(n)) = v.kind { + comps.push(n); + } + } + + let mut i = 0usize; + arr.values.sort_by(|_, _| { + let comp = *comps.get(i).unwrap_or(&0.0); + i += 1; + match comp { + n if n < 0.0 => Ordering::Less, + 0.0 => Ordering::Equal, + _ => Ordering::Greater, + } + }); + + Signal::Success(Value { + kind: Some(Kind::ListValue(arr)), + }) +} + +fn sort_reverse( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => array_v: Value, cmp_v: Value); + let Kind::ListValue(mut arr) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); + }; + let Kind::ListValue(cmp_vals) = cmp_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of numbers", + )); + }; + + let mut comps: Vec = Vec::new(); + for v in &cmp_vals.values { + if let Some(Kind::NumberValue(n)) = v.kind { + comps.push(n); + } + } + + arr.values.reverse(); // keep behavior consistent with original + + let mut i = 0usize; + arr.values.sort_by(|_, _| { + let comp = *comps.get(i).unwrap_or(&0.0); + i += 1; + match comp { + n if n < 0.0 => Ordering::Less, + 0.0 => Ordering::Equal, + _ => Ordering::Greater, + } + }); + + Signal::Success(Value { + kind: Some(Kind::ListValue(arr)), + }) +} + +fn reverse(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected an array as an argument", + )); + }; + array.values.reverse(); + Signal::Success(Value { + kind: Some(Kind::ListValue(array)), + }) +} + +fn flat(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected an array as an argument", + )); + }; + + let mut out: Vec = Vec::new(); + for item in &array.values { + match &item.kind { + Some(Kind::ListValue(sub)) => out.extend(sub.values.iter().cloned()), + _ => out.push(item.clone()), + } + } + + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values: out })), + }) +} + +fn min(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); + + let mut nums: Vec = Vec::new(); + for v in &array.values { + if let Some(Kind::NumberValue(n)) = v.kind { + nums.push(n); + } + } + + match nums.iter().min_by(|a, b| a.total_cmp(b)) { + Some(m) => Signal::Success(Value { + kind: Some(Kind::NumberValue(*m)), + }), + None => Signal::Failure(RuntimeError::simple_str( + "ArrayEmptyRuntimeError", + "Array is empty", + )), + } +} + +fn max(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); + + let mut nums: Vec = Vec::new(); + for v in &array.values { + if let Some(Kind::NumberValue(n)) = v.kind { + nums.push(n); + } + } + + match nums.iter().max_by(|a, b| a.total_cmp(b)) { + Some(m) => Signal::Success(Value { + kind: Some(Kind::NumberValue(*m)), + }), + None => Signal::Failure(RuntimeError::simple_str( + "ArrayEmptyRuntimeError", + "Array is empty", + )), + } +} + +fn sum(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); + + let mut s = 0.0; + for v in &array.values { + if let Some(Kind::NumberValue(n)) = v.kind { + s += n; + } + } + + Signal::Success(Value { + kind: Some(Kind::NumberValue(s)), + }) +} + +fn join(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue, separator: String); + + let mut parts: Vec = Vec::new(); + for v in &array.values { + if let Some(Kind::StringValue(s)) = &v.kind { + parts.push(s.clone()); + } + } + + Signal::Success(Value { + kind: Some(Kind::StringValue(parts.join(&separator))), + }) +} +#[cfg(test)] +mod tests { + use super::*; + use crate::context::context::Context; + use tucana::shared::{ListValue, Value, value::Kind}; + + // --- helpers ------------------------------------------------------------- + fn a_val(v: Value) -> Argument { + Argument::Eval(v) + } + fn v_num(n: f64) -> Value { + Value { + kind: Some(Kind::NumberValue(n)), + } + } + fn v_str(s: &str) -> Value { + Value { + kind: Some(Kind::StringValue(s.to_string())), + } + } + fn v_bool(b: bool) -> Value { + Value { + kind: Some(Kind::BoolValue(b)), + } + } + fn v_list(values: Vec) -> Value { + Value { + kind: Some(Kind::ListValue(ListValue { values })), + } + } + + fn expect_num(sig: Signal) -> f64 { + match sig { + Signal::Success(Value { + kind: Some(Kind::NumberValue(n)), + }) => n, + x => panic!("Expected NumberValue, got {:?}", x), + } + } + fn expect_str(sig: Signal) -> String { + match sig { + Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }) => s, + x => panic!("Expected StringValue, got {:?}", x), + } + } + fn expect_list(sig: Signal) -> Vec { + match sig { + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values })), + }) => values, + x => panic!("Expected ListValue, got {:?}", x), + } + } + fn expect_bool(sig: Signal) -> bool { + match sig { + Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }) => b, + x => panic!("Expected BoolValue, got {:?}", x), + } + } + + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) + } + + // --- at ------------------------------------------------------------------ + #[test] + fn test_at_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let arr = v_list(vec![v_num(10.0), v_num(20.0), v_num(30.0)]); + + assert_eq!( + expect_num(at( + &[a_val(arr.clone()), a_val(v_num(0.0))], + &mut ctx, + &mut run + )), + 10.0 + ); + assert_eq!( + expect_num(at( + &[a_val(arr.clone()), a_val(v_num(1.0))], + &mut ctx, + &mut run + )), + 20.0 + ); + assert_eq!( + expect_num(at(&[a_val(arr), a_val(v_num(2.0))], &mut ctx, &mut run)), + 30.0 + ); + } + + #[test] + fn test_at_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let arr = v_list(vec![v_num(1.0)]); + + // wrong arg count + match at(&[a_val(arr.clone())], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + // wrong type first arg + match at( + &[a_val(v_str("not_array")), a_val(v_num(0.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + // wrong type second arg + match at(&[a_val(arr.clone()), a_val(v_str("x"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + // oob / negative + match at(&[a_val(arr.clone()), a_val(v_num(9.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match at(&[a_val(arr), a_val(v_num(-1.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + // --- concat -------------------------------------------------------------- + #[test] + fn test_concat_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let a = v_list(vec![v_num(1.0), v_num(2.0)]); + let b = v_list(vec![v_num(3.0), v_num(4.0)]); + let out = expect_list(concat(&[a_val(a), a_val(b)], &mut ctx, &mut run)); + assert_eq!(out.len(), 4); + assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); + assert_eq!(out[3].kind, Some(Kind::NumberValue(4.0))); + } + + #[test] + fn test_concat_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let arr = v_list(vec![v_num(1.0)]); + match concat(&[a_val(arr.clone())], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match concat( + &[a_val(v_str("not_array")), a_val(arr.clone())], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match concat(&[a_val(arr), a_val(v_num(42.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + // --- filter / find / find_last / find_index ------------------------------ + #[test] + fn test_filter_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let array = v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]); + let predicate = v_list(vec![v_bool(true), v_bool(false), v_bool(true)]); + let out = expect_list(filter( + &[a_val(array), a_val(predicate)], + &mut ctx, + &mut run, + )); + assert_eq!(out.len(), 2); + assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); + assert_eq!(out[1].kind, Some(Kind::NumberValue(3.0))); + } + + #[test] + fn test_filter_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let array = v_list(vec![v_num(1.0)]); + let predicate = v_list(vec![v_bool(true)]); + match filter(&[a_val(array.clone())], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match filter( + &[a_val(v_str("not_array")), a_val(predicate.clone())], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match filter(&[a_val(array), a_val(v_num(1.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + // --- first / last -------------------------------------------------------- + #[test] + fn test_first_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let arr = v_list(vec![v_str("first"), v_str("second"), v_str("third")]); + assert_eq!( + expect_str(first(&[a_val(arr)], &mut ctx, &mut run)), + "first" + ); + } + + #[test] + fn test_first_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + match first(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match first(&[a_val(v_str("not_array"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match first(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + #[test] + fn test_last_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let arr = v_list(vec![v_str("first"), v_str("second"), v_str("last")]); + assert_eq!(expect_str(last(&[a_val(arr)], &mut ctx, &mut run)), "last"); + } + + #[test] + fn test_last_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + match last(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match last(&[a_val(v_str("not_array"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + // --- for_each / map ------------------------------------------------------ + #[test] + fn test_for_each_and_map() { + let mut ctx = Context::default(); + let mut run = dummy_run; + match for_each(&[], &mut ctx, &mut run) { + Signal::Success(Value { + kind: Some(Kind::NullValue(_)), + }) => {} + x => panic!("expected NullValue, got {:?}", x), + } + let transformed = v_list(vec![v_str("X"), v_str("Y")]); + let out = expect_list(map( + &[ + a_val(v_list(vec![v_num(1.0), v_num(2.0)])), + a_val(transformed.clone()), + ], + &mut ctx, + &mut run, + )); + let expected = match transformed.kind { + Some(Kind::ListValue(ListValue { values })) => values, + _ => unreachable!(), + }; + assert_eq!(out, expected); + } + + // --- push / pop / remove ------------------------------------------------- + #[test] + fn test_push_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let out = expect_list(push( + &[ + a_val(v_list(vec![v_num(1.0), v_num(2.0)])), + a_val(v_num(3.0)), + ], + &mut ctx, + &mut run, + )); + assert_eq!(out.len(), 3); + assert_eq!(out[2].kind, Some(Kind::NumberValue(3.0))); + } + + #[test] + fn test_push_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + match push(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match push( + &[a_val(v_str("nope")), a_val(v_num(1.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + #[test] + fn test_pop_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let out = expect_list(pop( + &[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], + &mut ctx, + &mut run, + )); + assert_eq!(out.len(), 2); + assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); + assert_eq!(out[1].kind, Some(Kind::NumberValue(2.0))); + } + + #[test] + fn test_pop_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + match pop(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match pop(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + #[test] + fn test_remove_success_and_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + // success + let arr = v_list(vec![v_str("first"), v_str("second"), v_str("third")]); + let out = expect_list(remove( + &[a_val(arr), a_val(v_str("second"))], + &mut ctx, + &mut run, + )); + assert_eq!(out.len(), 2); + assert_eq!(out[0].kind, Some(Kind::StringValue("first".into()))); + assert_eq!(out[1].kind, Some(Kind::StringValue("third".into()))); + // errors + match remove(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match remove( + &[a_val(v_str("nope")), a_val(v_num(0.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match remove( + &[a_val(v_list(vec![v_num(1.0)])), a_val(v_num(999.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + // --- is_empty / size ----------------------------------------------------- + #[test] + fn test_is_empty_and_size() { + let mut ctx = Context::default(); + let mut run = dummy_run; + assert!(expect_bool(is_empty( + &[a_val(v_list(vec![]))], + &mut ctx, + &mut run + ))); + assert!(!expect_bool(is_empty( + &[a_val(v_list(vec![v_num(1.0)]))], + &mut ctx, + &mut run + ))); + assert_eq!( + expect_num(size(&[a_val(v_list(vec![]))], &mut ctx, &mut run)), + 0.0 + ); + assert_eq!( + expect_num(size( + &[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], + &mut ctx, + &mut run + )), + 3.0 + ); + } + + #[test] + fn test_is_empty_error_and_size_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + match is_empty(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match is_empty(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match size(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match size(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + // --- index_of / to_unique ------------------------------------------------ + #[test] + fn test_index_of_and_to_unique() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let arr = v_list(vec![v_num(10.0), v_num(42.0), v_num(30.0), v_num(42.0)]); + assert_eq!( + expect_num(index_of( + &[a_val(arr.clone()), a_val(v_num(42.0))], + &mut ctx, + &mut run + )), + 1.0 + ); + match index_of( + &[a_val(arr.clone()), a_val(v_num(999.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + + let uniq = expect_list(to_unique(&[a_val(arr)], &mut ctx, &mut run)); + assert_eq!(uniq.len(), 3); + assert_eq!(uniq[0].kind, Some(Kind::NumberValue(10.0))); + assert_eq!(uniq[1].kind, Some(Kind::NumberValue(42.0))); + assert_eq!(uniq[2].kind, Some(Kind::NumberValue(30.0))); + } + + #[test] + fn test_index_of_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + match index_of(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match index_of( + &[a_val(v_str("nope")), a_val(v_num(1.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + // --- sort / sort_reverse ------------------------------------------------- + #[test] + fn test_sort_and_sort_reverse() { + let mut ctx = Context::default(); + let mut run = dummy_run; + + // We don't rely on actual values; ordering is driven by the comparator sequence. + let arr = v_list(vec![v_str("a"), v_str("b"), v_str("c"), v_str("d")]); + let comps = v_list(vec![v_num(-1.0), v_num(1.0), v_num(0.0), v_num(-1.0)]); + let out = expect_list(sort( + &[a_val(arr.clone()), a_val(comps.clone())], + &mut ctx, + &mut run, + )); + assert_eq!(out.len(), 4); + + let out_r = expect_list(sort_reverse( + &[a_val(arr), a_val(comps)], + &mut ctx, + &mut run, + )); + assert_eq!(out_r.len(), 4); + } + + // --- reverse / flat ------------------------------------------------------ + #[test] + fn test_reverse_success_and_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let out = expect_list(reverse( + &[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], + &mut ctx, + &mut run, + )); + assert_eq!(out[0].kind, Some(Kind::NumberValue(3.0))); + assert_eq!(out[2].kind, Some(Kind::NumberValue(1.0))); + + match reverse(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match reverse(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + #[test] + fn test_flat_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let nested = v_list(vec![ + v_num(1.0), + v_list(vec![v_num(2.0), v_num(3.0)]), + v_list(vec![]), + v_num(4.0), + ]); + let out = expect_list(flat(&[a_val(nested)], &mut ctx, &mut run)); + assert_eq!(out.len(), 4); + assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); + assert_eq!(out[1].kind, Some(Kind::NumberValue(2.0))); + assert_eq!(out[2].kind, Some(Kind::NumberValue(3.0))); + assert_eq!(out[3].kind, Some(Kind::NumberValue(4.0))); + } + + // --- min / max / sum ----------------------------------------------------- + #[test] + fn test_min_max_sum_success_and_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let nums = v_list(vec![v_num(5.0), v_num(1.0), v_num(8.0), v_num(2.0)]); + assert_eq!( + expect_num(min(&[a_val(nums.clone())], &mut ctx, &mut run)), + 1.0 + ); + assert_eq!( + expect_num(max(&[a_val(nums.clone())], &mut ctx, &mut run)), + 8.0 + ); + assert_eq!(expect_num(sum(&[a_val(nums)], &mut ctx, &mut run)), 16.0); + + // empty + match min(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match max(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + assert_eq!( + expect_num(sum(&[a_val(v_list(vec![]))], &mut ctx, &mut run)), + 0.0 + ); + + // wrong type + match sum(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match min(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match max(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } + + // --- join ---------------------------------------------------------------- + #[test] + fn test_join_success_and_error() { + let mut ctx = Context::default(); + let mut run = dummy_run; + let arr = v_list(vec![v_str("hello"), v_str("world"), v_str("test")]); + assert_eq!( + expect_str(join(&[a_val(arr), a_val(v_str(", "))], &mut ctx, &mut run)), + "hello, world, test" + ); + + // errors + let arr2 = v_list(vec![v_str("hello")]); + match join(&[a_val(arr2.clone())], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match join( + &[a_val(v_str("not_array")), a_val(v_str(","))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match join(&[a_val(arr2), a_val(v_num(42.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + } +} diff --git a/taurus/src/implementation/boolean.rs b/taurus/src/implementation/boolean.rs new file mode 100644 index 0000000..7161a13 --- /dev/null +++ b/taurus/src/implementation/boolean.rs @@ -0,0 +1,393 @@ +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; +use crate::context::signal::Signal; +use crate::{context::context::Context, error::RuntimeError}; +use tucana::shared::{Value, value::Kind}; + +pub fn collect_boolean_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { + vec![ + ("std::boolean::as_number", HandlerFn::eager(as_number, 1)), + ("std::boolean::as_text", HandlerFn::eager(as_text, 1)), + ( + "std::boolean::from_number", + HandlerFn::eager(from_number, 1), + ), + ("std::boolean::from_text", HandlerFn::eager(from_text, 1)), + ("std::boolean::is_equal", HandlerFn::eager(is_equal, 2)), + ("std::boolean::negate", HandlerFn::eager(negate, 1)), + ] +} + +fn as_number(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: bool); + Signal::Success(Value { + kind: Some(Kind::NumberValue((value as i64) as f64)), + }) +} + +fn as_text(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: bool); + Signal::Success(Value { + kind: Some(Kind::StringValue(value.to_string())), + }) +} + +fn from_number( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => number: f64); + let is_zero = number == 0.0; + Signal::Success(Value { + kind: Some(Kind::BoolValue(!is_zero)), + }) +} + +fn from_text(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => text: String); + + match text.to_lowercase().parse::() { + Ok(b) => Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }), + Err(_) => Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse boolean from string: {:?}", text), + )), + } +} + +fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: bool, rhs: bool); + Signal::Success(Value { + kind: Some(Kind::BoolValue(lhs == rhs)), + }) +} + +fn negate(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: bool); + Signal::Success(Value { + kind: Some(Kind::BoolValue(!value)), + }) +} +#[cfg(test)] +mod tests { + use super::*; + use crate::context::context::Context; + use tucana::shared::{Value, value::Kind}; + + // ---- helpers: make Arguments ---- + fn a_bool(b: bool) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::BoolValue(b)), + }) + } + fn a_num(n: f64) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::NumberValue(n)), + }) + } + fn a_str(s: &str) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::StringValue(s.to_string())), + }) + } + + // ---- helpers: unwrap Signal ---- + fn expect_num(sig: Signal) -> f64 { + match sig { + Signal::Success(Value { + kind: Some(Kind::NumberValue(n)), + }) => n, + other => panic!("Expected NumberValue, got {:?}", other), + } + } + fn expect_str(sig: Signal) -> String { + match sig { + Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }) => s, + other => panic!("Expected StringValue, got {:?}", other), + } + } + fn expect_bool(sig: Signal) -> bool { + match sig { + Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }) => b, + other => panic!("Expected BoolValue, got {:?}", other), + } + } + + // dummy `run` closure (unused by these handlers) + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { + kind: Some(Kind::BoolValue(true)), + }) + } + + // ---- tests ---- + + #[test] + fn test_as_number_success() { + let mut ctx = Context::default(); + let mut run = dummy_run; + assert_eq!( + expect_num(as_number(&[a_bool(true)], &mut ctx, &mut run)), + 1.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(as_number(&[a_bool(false)], &mut ctx, &mut run)), + 0.0 + ); + } + + #[test] + fn test_as_number_errors() { + let mut ctx = Context::default(); + + // wrong arity: none + let mut run = dummy_run; + match as_number(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), + } + + // wrong type + let mut run = dummy_run; + match as_number(&[a_num(1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-bool, got {:?}", s), + } + + // too many args + let mut run = dummy_run; + match as_number(&[a_bool(true), a_bool(false)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), + } + } + + #[test] + fn test_as_text_success() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_str(as_text(&[a_bool(true)], &mut ctx, &mut run)), + "true" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(as_text(&[a_bool(false)], &mut ctx, &mut run)), + "false" + ); + } + + #[test] + fn test_as_text_errors() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + match as_text(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), + } + + let mut run = dummy_run; + match as_text(&[a_num(5.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-bool, got {:?}", s), + } + + let mut run = dummy_run; + match as_text(&[a_bool(true), a_bool(false)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), + } + } + + #[test] + fn test_from_number_success() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_bool(from_number(&[a_num(0.0)], &mut ctx, &mut run)), + false + ); + + let mut run = dummy_run; + assert_eq!( + expect_bool(from_number(&[a_num(3.5)], &mut ctx, &mut run)), + true + ); + + let mut run = dummy_run; + assert_eq!( + expect_bool(from_number(&[a_num(-2.0)], &mut ctx, &mut run)), + true + ); + + // -0.0 should behave like 0.0 + let mut run = dummy_run; + assert_eq!( + expect_bool(from_number(&[a_num(-0.0)], &mut ctx, &mut run)), + false + ); + } + + #[test] + fn test_from_number_errors() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + match from_number(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), + } + + let mut run = dummy_run; + match from_number(&[a_bool(true)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-number, got {:?}", s), + } + + let mut run = dummy_run; + match from_number(&[a_num(1.0), a_num(2.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), + } + } + + #[test] + fn test_from_text_success_and_errors() { + let mut ctx = Context::default(); + + // success (case-insensitive) + let mut run = dummy_run; + assert_eq!( + expect_bool(from_text(&[a_str("true")], &mut ctx, &mut run)), + true + ); + + let mut run = dummy_run; + assert_eq!( + expect_bool(from_text(&[a_str("FALSE")], &mut ctx, &mut run)), + false + ); + + // errors + let mut run = dummy_run; + match from_text(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), + } + + let mut run = dummy_run; + match from_text(&[a_str("yes")], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for unparseable bool, got {:?}", s), + } + + let mut run = dummy_run; + match from_text(&[a_num(1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-string, got {:?}", s), + } + + let mut run = dummy_run; + match from_text(&[a_str("true"), a_str("false")], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), + } + } + + #[test] + fn test_is_equal_and_errors() { + let mut ctx = Context::default(); + + // equalities + let mut run = dummy_run; + assert_eq!( + expect_bool(is_equal(&[a_bool(true), a_bool(true)], &mut ctx, &mut run)), + true + ); + + let mut run = dummy_run; + assert_eq!( + expect_bool(is_equal( + &[a_bool(false), a_bool(false)], + &mut ctx, + &mut run + )), + true + ); + + let mut run = dummy_run; + assert_eq!( + expect_bool(is_equal(&[a_bool(true), a_bool(false)], &mut ctx, &mut run)), + false + ); + + // arity/type errors + let mut run = dummy_run; + match is_equal(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), + } + + let mut run = dummy_run; + match is_equal(&[a_bool(true)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 1, got {:?}", s), + } + + let mut run = dummy_run; + match is_equal(&[a_bool(true), a_num(1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-bool rhs, got {:?}", s), + } + } + + #[test] + fn test_negate_success_and_errors() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_bool(negate(&[a_bool(true)], &mut ctx, &mut run)), + false + ); + + let mut run = dummy_run; + assert_eq!( + expect_bool(negate(&[a_bool(false)], &mut ctx, &mut run)), + true + ); + + // errors + let mut run = dummy_run; + match negate(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), + } + + let mut run = dummy_run; + match negate(&[a_num(1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-bool, got {:?}", s), + } + + let mut run = dummy_run; + match negate(&[a_bool(true), a_bool(false)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), + } + } +} diff --git a/taurus/src/implementation/control.rs b/taurus/src/implementation/control.rs new file mode 100644 index 0000000..d9671a8 --- /dev/null +++ b/taurus/src/implementation/control.rs @@ -0,0 +1,98 @@ +use crate::context::argument::Argument; +use crate::context::argument::ParameterNode::{Eager, Lazy}; +use crate::context::context::Context; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; +use crate::context::signal::Signal; +use crate::error::RuntimeError; +use tucana::shared::Value; +use tucana::shared::value::Kind; + +pub fn collect_control_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { + vec![ + ("std::control::stop", HandlerFn::eager(stop, 0)), + ("std::control::return", HandlerFn::eager(r#return, 1)), + ( + "std::control::if", + HandlerFn::into_function_entry(r#if, vec![Eager, Lazy]), + ), + ( + "std::control::if_else", + HandlerFn::into_function_entry(if_else, vec![Eager, Lazy, Lazy]), + ), + ] +} + +fn stop(_args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + Signal::Stop +} + +fn r#return(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: Value); + Signal::Return(value) +} + +fn r#if(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + let [ + Argument::Eval(Value { + kind: Some(Kind::StringValue(text)), + }), + Argument::Thunk(if_pointer), + ] = args + else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a string value but received {:?}", args), + )); + }; + + let bool: bool = match text.to_lowercase().parse() { + Ok(value) => value, + Err(_) => { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse boolean from string: {:?}", text), + )); + } + }; + + if bool { + _run(*if_pointer) + } else { + Signal::Return(Value { + kind: Some(Kind::NullValue(0)), + }) + } +} + +fn if_else(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + let [ + Argument::Eval(Value { + kind: Some(Kind::StringValue(text)), + }), + Argument::Thunk(if_pointer), + Argument::Thunk(else_pointer), + ] = args + else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a string value but received {:?}", args), + )); + }; + + let bool: bool = match text.to_lowercase().parse() { + Ok(value) => value, + Err(_) => { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse boolean from string: {:?}", text), + )); + } + }; + + if bool { + _run(*if_pointer) + } else { + _run(*else_pointer) + } +} diff --git a/src/implementation/http.rs b/taurus/src/implementation/http.rs similarity index 61% rename from src/implementation/http.rs rename to taurus/src/implementation/http.rs index d1605e0..b7a7576 100644 --- a/src/implementation/http.rs +++ b/taurus/src/implementation/http.rs @@ -1,31 +1,25 @@ -use crate::context::Context; +use crate::context::argument::Argument; +use crate::context::context::Context; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; use crate::error::RuntimeError; -use crate::registry::HandlerFn; use tucana::shared::value::Kind; use tucana::shared::{Struct, Value}; -pub fn collect_http_functions() -> Vec<(&'static str, HandlerFn)> { +pub fn collect_http_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ - ("http::request::create", create_request), - ("http::response::create", create_response), - ("http::control::respond", respond), + ("http::request::create", HandlerFn::eager(create_request, 1)), + ( + "http::response::create", + HandlerFn::eager(create_response, 4), + ), + ("http::control::respond", HandlerFn::eager(respond, 3)), ] } -fn respond(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(struct_val)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected exactly one response struct, got {:?}", values), - )); - }; - +fn respond(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => struct_val: Struct); let fields = &struct_val.fields; let Some(headers_val) = fields.get("headers") else { @@ -75,29 +69,12 @@ fn respond(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn create_request(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(http_method)), - }, - Value { - kind: Some(Kind::StructValue(headers)), - }, - Value { - kind: Some(Kind::StringValue(http_url)), - }, - payload, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected [method, headers, url, payload] but received {:?}", - values - ), - )); - }; - +fn create_request( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => http_method: String, headers: Struct, http_url: String, payload: Value); let mut fields = std::collections::HashMap::new(); fields.insert( @@ -127,32 +104,18 @@ fn create_request(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn create_response(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(http_status_code)), - }, - Value { - kind: Some(Kind::StructValue(headers)), - }, - payload, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected [http_status_code, headers, payload] but received {:?}", - values - ), - )); - }; - +fn create_response( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => http_status_code: f64, headers: Struct, payload: Value); let mut fields = std::collections::HashMap::new(); fields.insert( "status_code".to_string(), Value { - kind: Some(Kind::NumberValue(*http_status_code)), + kind: Some(Kind::NumberValue(http_status_code)), }, ); diff --git a/src/implementation/mod.rs b/taurus/src/implementation/mod.rs similarity index 81% rename from src/implementation/mod.rs rename to taurus/src/implementation/mod.rs index caa93b9..e466e94 100644 --- a/src/implementation/mod.rs +++ b/taurus/src/implementation/mod.rs @@ -1,4 +1,4 @@ -use crate::registry::HandlerFn; +use crate::context::registry::HandlerFunctionEntry; mod array; mod boolean; @@ -8,7 +8,7 @@ mod number; mod object; mod text; -pub fn collect() -> Vec<(&'static str, HandlerFn)> { +pub fn collect() -> Vec<(&'static str, HandlerFunctionEntry)> { let mut result = vec![]; result.extend(array::collect_array_functions()); diff --git a/taurus/src/implementation/number.rs b/taurus/src/implementation/number.rs new file mode 100644 index 0000000..8e489a6 --- /dev/null +++ b/taurus/src/implementation/number.rs @@ -0,0 +1,738 @@ +use std::f64; + +use tucana::shared::{Value, value::Kind}; + +use crate::context::argument::Argument; +use crate::context::macros::{args, no_args}; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; +use crate::context::signal::Signal; +use crate::{context::context::Context, error::RuntimeError}; + +pub fn collect_number_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { + vec![ + ("std::number::add", HandlerFn::eager(add, 2)), + ("std::number::multiply", HandlerFn::eager(multiply, 2)), + ("std::number::substract", HandlerFn::eager(substract, 2)), + ("std::number::divide", HandlerFn::eager(divide, 2)), + ("std::number::modulo", HandlerFn::eager(modulo, 2)), + ("std::number::abs", HandlerFn::eager(abs, 1)), + ("std::number::is_positive", HandlerFn::eager(is_positive, 1)), + ("std::number::is_greater", HandlerFn::eager(is_greater, 2)), + ("std::number::is_less", HandlerFn::eager(is_less, 2)), + ("std::number::is_zero", HandlerFn::eager(is_zero, 1)), + ("std::number::square", HandlerFn::eager(square, 2)), + ("std::number::exponential", HandlerFn::eager(exponential, 2)), + ("std::number::pi", HandlerFn::eager(pi, 0)), + ("std::number::euler", HandlerFn::eager(euler, 0)), + ("std::number::infinity", HandlerFn::eager(infinity, 0)), + ("std::number::round_up", HandlerFn::eager(round_up, 2)), + ("std::number::round_down", HandlerFn::eager(round_down, 2)), + ("std::number::round", HandlerFn::eager(round, 2)), + ("std::number::square_root", HandlerFn::eager(square_root, 1)), + ("std::number::root", HandlerFn::eager(root, 2)), + ("std::number::log", HandlerFn::eager(log, 2)), + ("std::number::ln", HandlerFn::eager(ln, 1)), + ("std::number::from_text", HandlerFn::eager(from_text, 1)), + ("std::number::as_text", HandlerFn::eager(as_text, 1)), + ("std::number::min", HandlerFn::eager(min, 2)), + ("std::number::max", HandlerFn::eager(max, 2)), + ("std::number::negate", HandlerFn::eager(negate, 1)), + ("std::number::random", HandlerFn::eager(random, 2)), + ("std::number::sin", HandlerFn::eager(sin, 1)), + ("std::number::cos", HandlerFn::eager(cos, 1)), + ("std::number::tan", HandlerFn::eager(tan, 1)), + ("std::number::arcsin", HandlerFn::eager(arcsin, 1)), + ("std::number::arccos", HandlerFn::eager(arccos, 1)), + ("std::number::arctan", HandlerFn::eager(arctan, 1)), + ("std::number::sinh", HandlerFn::eager(sinh, 1)), + ("std::number::cosh", HandlerFn::eager(cosh, 1)), + ("std::number::clamp", HandlerFn::eager(clamp, 3)), + ("std::number::is_equal", HandlerFn::eager(is_equal, 2)), + ] +} + +fn add(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(lhs + rhs)), + }) +} + +fn multiply(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(lhs * rhs)), + }) +} + +fn substract(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(lhs - rhs)), + }) +} + +fn divide(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + + if rhs == 0.0 { + return Signal::Failure(RuntimeError::simple_str( + "DivisionByZero", + "You cannot divide by zero", + )); + } + + Signal::Success(Value { + kind: Some(Kind::NumberValue(lhs / rhs)), + }) +} + +fn modulo(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + + if rhs == 0.0 { + return Signal::Failure(RuntimeError::simple_str( + "DivisionByZero", + "You cannot divide by zero", + )); + } + + Signal::Success(Value { + kind: Some(Kind::NumberValue(lhs % rhs)), + }) +} + +fn abs(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.abs())), + }) +} + +fn is_positive( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::BoolValue(!value.is_sign_negative())), + }) +} + +fn is_greater( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => lhs: f64, rhs: f64); + Signal::Success(Value { + kind: Some(Kind::BoolValue(lhs > rhs)), + }) +} + +fn is_less(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + Signal::Success(Value { + kind: Some(Kind::BoolValue(lhs < rhs)), + }) +} + +fn is_zero(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::BoolValue(value == 0.0)), + }) +} + +fn square(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.powf(2.0))), + }) +} + +fn exponential( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => base: f64, exponent: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(base.powf(exponent))), + }) +} + +fn pi(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + no_args!(args); + Signal::Success(Value { + kind: Some(Kind::NumberValue(f64::consts::PI)), + }) +} + +fn euler(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + no_args!(args); + Signal::Success(Value { + kind: Some(Kind::NumberValue(f64::consts::E)), + }) +} + +fn infinity(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + no_args!(args); + Signal::Success(Value { + kind: Some(Kind::NumberValue(f64::INFINITY)), + }) +} + +fn round_up(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, decimal_places: f64); + let factor = 10_f64.powi(decimal_places as i32); + Signal::Success(Value { + kind: Some(Kind::NumberValue((value * factor).ceil() / factor)), + }) +} + +fn round_down( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: f64, decimal_places: f64); + let factor = 10_f64.powi(decimal_places as i32); + Signal::Success(Value { + kind: Some(Kind::NumberValue((value * factor).floor() / factor)), + }) +} + +fn round(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, decimal_places: f64); + let factor = 10_f64.powi(decimal_places as i32); + Signal::Success(Value { + kind: Some(Kind::NumberValue((value * factor).round() / factor)), + }) +} + +fn square_root( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.sqrt())), + }) +} + +fn root(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, root: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.powf(root))), + }) +} + +fn log(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, base: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.log(base))), + }) +} + +fn ln(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.ln())), + }) +} + +fn from_text(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => string_value: String); + + match string_value.parse::() { + Ok(v) => Signal::Success(Value { + kind: Some(Kind::NumberValue(v)), + }), + Err(_) => Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse string as number: {}", string_value), + )), + } +} + +fn as_text(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::StringValue(value.to_string())), + }) +} + +fn min(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(lhs.min(rhs))), + }) +} + +fn max(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(lhs.max(rhs))), + }) +} + +fn negate(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(-value)), + }) +} + +fn random(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => min: f64, max: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(rand::random_range(min..max))), + }) +} + +fn sin(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.sin())), + }) +} + +fn cos(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.cos())), + }) +} + +fn tan(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.tan())), + }) +} + +fn arcsin(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.asin())), + }) +} + +fn arccos(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.acos())), + }) +} + +fn arctan(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.atan())), + }) +} + +fn sinh(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.sinh())), + }) +} + +fn cosh(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.cosh())), + }) +} + +fn clamp(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, min: f64, max: f64); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.clamp(min, max))), + }) +} + +fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); + Signal::Success(Value { + kind: Some(Kind::BoolValue(lhs == rhs)), + }) +} +#[cfg(test)] +mod tests { + use super::*; + use crate::context::argument::Argument; + use crate::context::context::Context; + use tucana::shared::{Value, value::Kind}; + + // ---- helpers: Arguments ---- + fn a_num(n: f64) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::NumberValue(n)), + }) + } + fn a_str(s: &str) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::StringValue(s.to_string())), + }) + } + + // ---- helpers: extractors ---- + fn expect_num(sig: Signal) -> f64 { + match sig { + Signal::Success(Value { + kind: Some(Kind::NumberValue(n)), + }) => n, + other => panic!("Expected NumberValue, got {:?}", other), + } + } + fn expect_bool(sig: Signal) -> bool { + match sig { + Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }) => b, + other => panic!("Expected BoolValue, got {:?}", other), + } + } + fn expect_str(sig: Signal) -> String { + match sig { + Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }) => s, + other => panic!("Expected StringValue, got {:?}", other), + } + } + + // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) + } + + #[test] + fn test_add_and_multiply() { + let mut ctx = Context::default(); + let mut run = dummy_run; + assert_eq!( + expect_num(add(&[a_num(5.0), a_num(3.0)], &mut ctx, &mut run)), + 8.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(multiply(&[a_num(4.0), a_num(2.5)], &mut ctx, &mut run)), + 10.0 + ); + } + + #[test] + fn test_substract_and_divide() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_num(substract(&[a_num(10.0), a_num(4.0)], &mut ctx, &mut run)), + 6.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(divide(&[a_num(15.0), a_num(3.0)], &mut ctx, &mut run)), + 5.0 + ); + + // divide by zero -> Failure + let mut run = dummy_run; + match divide(&[a_num(10.0), a_num(0.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure on divide by zero, got {:?}", s), + } + } + + #[test] + fn test_modulo_and_abs() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_num(modulo(&[a_num(10.0), a_num(3.0)], &mut ctx, &mut run)), + 1.0 + ); + + // modulo by zero -> Failure + let mut run = dummy_run; + match modulo(&[a_num(10.0), a_num(0.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure on modulo by zero, got {:?}", s), + } + + let mut run = dummy_run; + assert_eq!(expect_num(abs(&[a_num(-7.5)], &mut ctx, &mut run)), 7.5); + } + + #[test] + fn test_comparisons_and_zero() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert!(expect_bool(is_positive(&[a_num(5.0)], &mut ctx, &mut run))); + let mut run = dummy_run; + assert!(!expect_bool(is_positive( + &[a_num(-1.0)], + &mut ctx, + &mut run + ))); + let mut run = dummy_run; + assert!(expect_bool(is_positive(&[a_num(0.0)], &mut ctx, &mut run))); + + let mut run = dummy_run; + assert!(expect_bool(is_greater( + &[a_num(10.0), a_num(5.0)], + &mut ctx, + &mut run + ))); + let mut run = dummy_run; + assert!(expect_bool(is_less( + &[a_num(3.0), a_num(7.0)], + &mut ctx, + &mut run + ))); + + let mut run = dummy_run; + assert!(expect_bool(is_zero(&[a_num(0.0)], &mut ctx, &mut run))); + let mut run = dummy_run; + assert!(!expect_bool(is_zero(&[a_num(0.01)], &mut ctx, &mut run))); + } + + #[test] + fn test_powers_and_exponential() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!(expect_num(square(&[a_num(4.0)], &mut ctx, &mut run)), 16.0); + + let mut run = dummy_run; + assert_eq!( + expect_num(exponential(&[a_num(2.0), a_num(3.0)], &mut ctx, &mut run)), + 8.0 + ); + } + + #[test] + fn test_constants() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert!( + (expect_num(pi(&[], &mut ctx, &mut run)) - std::f64::consts::PI).abs() < f64::EPSILON + ); + + let mut run = dummy_run; + assert!( + (expect_num(euler(&[], &mut ctx, &mut run)) - std::f64::consts::E).abs() < f64::EPSILON + ); + + let mut run = dummy_run; + let inf = expect_num(infinity(&[], &mut ctx, &mut run)); + assert!(inf.is_infinite() && inf.is_sign_positive()); + } + + #[test] + fn test_rounding() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_num(round_up( + &[a_num(f64::consts::PI), a_num(2.0)], + &mut ctx, + &mut run + )), + 3.15 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(round_down( + &[a_num(f64::consts::PI), a_num(2.0)], + &mut ctx, + &mut run + )), + 3.14 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(round(&[a_num(3.145), a_num(2.0)], &mut ctx, &mut run)), + 3.15 + ); + } + + #[test] + fn test_roots_and_logs() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_num(square_root(&[a_num(16.0)], &mut ctx, &mut run)), + 4.0 + ); + + // cube root via exponent 1/3 + let mut run = dummy_run; + let r = expect_num(root(&[a_num(8.0), a_num(1.0 / 3.0)], &mut ctx, &mut run)); + assert!((r - 2.0).abs() < 1e-6); + + let mut run = dummy_run; + let lg = expect_num(log(&[a_num(100.0), a_num(10.0)], &mut ctx, &mut run)); + assert!((lg - 2.0).abs() < f64::EPSILON); + + let mut run = dummy_run; + let ln1 = expect_num(ln(&[a_num(f64::consts::E)], &mut ctx, &mut run)); + assert!((ln1 - 1.0).abs() < f64::EPSILON); + } + + #[test] + fn test_text_conversions() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_num(from_text(&[a_str("42.5")], &mut ctx, &mut run)), + 42.5 + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(as_text(&[a_num(42.5)], &mut ctx, &mut run)), + "42.5".to_string() + ); + + // from_text failure + let mut run = dummy_run; + match from_text(&[a_str("not_a_number")], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for invalid parse, got {:?}", s), + } + } + + #[test] + fn test_min_max_and_negate() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_num(min(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run)), + 3.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(max(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run)), + 7.0 + ); + + let mut run = dummy_run; + assert_eq!(expect_num(negate(&[a_num(5.0)], &mut ctx, &mut run)), -5.0); + } + + #[test] + fn test_random_range() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + let r = expect_num(random(&[a_num(1.0), a_num(10.0)], &mut ctx, &mut run)); + assert!(r >= 1.0 && r < 10.0); + } + + #[test] + fn test_trig_and_hyperbolic() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + let s = expect_num(sin(&[a_num(f64::consts::PI / 2.0)], &mut ctx, &mut run)); + assert!((s - 1.0).abs() < 1e-12); + + let mut run = dummy_run; + let c = expect_num(cos(&[a_num(0.0)], &mut ctx, &mut run)); + assert!((c - 1.0).abs() < 1e-12); + + let mut run = dummy_run; + let t = expect_num(tan(&[a_num(f64::consts::PI / 4.0)], &mut ctx, &mut run)); + assert!((t - 1.0).abs() < 1e-4); + + let mut run = dummy_run; + let asn = expect_num(arcsin(&[a_num(1.0)], &mut ctx, &mut run)); + assert!((asn - f64::consts::PI / 2.0).abs() < 1e-12); + + let mut run = dummy_run; + let acs = expect_num(arccos(&[a_num(1.0)], &mut ctx, &mut run)); + assert!(acs.abs() < 1e-12); + + let mut run = dummy_run; + let atn = expect_num(arctan(&[a_num(1.0)], &mut ctx, &mut run)); + assert!((atn - f64::consts::PI / 4.0).abs() < 1e-12); + + let mut run = dummy_run; + let sh = expect_num(sinh(&[a_num(0.0)], &mut ctx, &mut run)); + assert!(sh.abs() < 1e-12); + + let mut run = dummy_run; + let ch = expect_num(cosh(&[a_num(0.0)], &mut ctx, &mut run)); + assert!((ch - 1.0).abs() < 1e-12); + } + + #[test] + fn test_clamp_and_is_equal() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_num(clamp( + &[a_num(5.0), a_num(1.0), a_num(10.0)], + &mut ctx, + &mut run + )), + 5.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(clamp( + &[a_num(-5.0), a_num(1.0), a_num(10.0)], + &mut ctx, + &mut run + )), + 1.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(clamp( + &[a_num(15.0), a_num(1.0), a_num(10.0)], + &mut ctx, + &mut run + )), + 10.0 + ); + + let mut run = dummy_run; + assert!(expect_bool(is_equal( + &[a_num(5.0), a_num(5.0)], + &mut ctx, + &mut run + ))); + + let mut run = dummy_run; + assert!(!expect_bool(is_equal( + &[a_num(5.0), a_num(3.0)], + &mut ctx, + &mut run + ))); + } +} diff --git a/taurus/src/implementation/object.rs b/taurus/src/implementation/object.rs new file mode 100644 index 0000000..2cbd292 --- /dev/null +++ b/taurus/src/implementation/object.rs @@ -0,0 +1,403 @@ +use tucana::shared::{Struct, Value, value::Kind}; + +use crate::context::argument::Argument; +use crate::context::context::Context; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; +use crate::context::signal::Signal; + +pub fn collect_object_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { + vec![ + ( + "std::object::contains_key", + HandlerFn::eager(contains_key, 2), + ), + ("std::object::keys", HandlerFn::eager(keys, 1)), + ("std::object::size", HandlerFn::eager(size, 1)), + ("std::object::set", HandlerFn::eager(set, 3)), + ] +} + +fn contains_key( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => object: Struct, key: String); + let contains = object.fields.contains_key(&key); + + Signal::Success(Value { + kind: Some(Kind::BoolValue(contains)), + }) +} + +fn size(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => object: Struct); + Signal::Success(Value { + kind: Some(Kind::NumberValue(object.fields.len() as f64)), + }) +} + +fn keys(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => object: Struct); + + let keys = object + .fields + .keys() + .map(|key| Value { + kind: Some(Kind::StringValue(key.clone())), + }) + .collect::>(); + + Signal::Success(Value { + kind: Some(Kind::ListValue(tucana::shared::ListValue { values: keys })), + }) +} + +fn set(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => object: Struct, key: String, value: Value); + let mut new_object = object.clone(); + new_object.fields.insert(key.clone(), value.clone()); + + Signal::Success(Value { + kind: Some(Kind::StructValue(new_object)), + }) +} +#[cfg(test)] +mod tests { + use super::*; + use crate::context::argument::Argument; + use crate::context::context::Context; + use std::collections::HashMap; + use tucana::shared::{Struct as TcStruct, Value, value::Kind}; + + // ---- helpers: Value builders ---- + fn v_string(s: &str) -> Value { + Value { + kind: Some(Kind::StringValue(s.to_string())), + } + } + fn v_number(n: f64) -> Value { + Value { + kind: Some(Kind::NumberValue(n)), + } + } + fn v_bool(b: bool) -> Value { + Value { + kind: Some(Kind::BoolValue(b)), + } + } + fn v_struct(fields: HashMap) -> Value { + Value { + kind: Some(Kind::StructValue(TcStruct { fields })), + } + } + + // ---- helpers: Struct builders (for args that expect Struct) ---- + fn s_empty() -> TcStruct { + TcStruct { + fields: HashMap::new(), + } + } + fn s_from(mut kv: Vec<(&str, Value)>) -> TcStruct { + let mut map = HashMap::::new(); + for (k, v) in kv.drain(..) { + map.insert(k.to_string(), v); + } + TcStruct { fields: map } + } + fn s_test() -> TcStruct { + s_from(vec![ + ("name", v_string("John")), + ("age", v_number(30.0)), + ("active", v_bool(true)), + ]) + } + + // ---- helpers: Argument builders ---- + #[allow(dead_code)] + fn a_value(v: Value) -> Argument { + Argument::Eval(v) + } + fn a_string(s: &str) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::StringValue(s.to_string())), + }) + } + fn a_struct(s: TcStruct) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::StructValue(s)), + }) + } + + // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) + } + + #[test] + fn test_contains_key_success() { + let mut ctx = Context::default(); + + // existing key + let mut run = dummy_run; + let args = vec![a_struct(s_test()), a_string("name")]; + let signal = contains_key(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::BoolValue(b)) => assert!(b), + _ => panic!("Expected BoolValue"), + } + + // non-existing key + let mut run = dummy_run; + let args = vec![a_struct(s_test()), a_string("nonexistent")]; + let signal = contains_key(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::BoolValue(b)) => assert!(!b), + _ => panic!("Expected BoolValue"), + } + + // empty object + let mut run = dummy_run; + let args = vec![a_struct(s_empty()), a_string("any_key")]; + let signal = contains_key(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::BoolValue(b)) => assert!(!b), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_size_success() { + let mut ctx = Context::default(); + + // non-empty object + let mut run = dummy_run; + let args = vec![a_struct(s_test())]; + let signal = size(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::NumberValue(n)) => assert_eq!(n, 3.0), + _ => panic!("Expected NumberValue"), + } + + // empty object + let mut run = dummy_run; + let args = vec![a_struct(s_empty())]; + let signal = size(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::NumberValue(n)) => assert_eq!(n, 0.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_keys_success() { + let mut ctx = Context::default(); + + // with fields + let mut run = dummy_run; + let args = vec![a_struct(s_test())]; + let signal = keys(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::ListValue(list)) => { + let mut got: Vec = list + .values + .iter() + .filter_map(|v| { + if let Some(Kind::StringValue(s)) = &v.kind { + Some(s.clone()) + } else { + None + } + }) + .collect(); + got.sort(); + + let mut expected = + vec!["active".to_string(), "age".to_string(), "name".to_string()]; + expected.sort(); + assert_eq!(got, expected); + } + _ => panic!("Expected ListValue"), + } + + // empty object => empty list + let mut run = dummy_run; + let args = vec![a_struct(s_empty())]; + let signal = keys(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::ListValue(list)) => assert_eq!(list.values.len(), 0), + _ => panic!("Expected ListValue"), + } + } + + #[test] + fn test_set_success_and_overwrite() { + let mut ctx = Context::default(); + + // set new key + let mut run = dummy_run; + let args = vec![ + a_struct(s_test()), + a_string("email"), + Argument::Eval(v_string("john@example.com")), + ]; + let signal = set(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::StructValue(st)) => { + assert_eq!(st.fields.len(), 4); + match st.fields.get("email") { + Some(Value { + kind: Some(Kind::StringValue(s)), + .. + }) => assert_eq!(s, "john@example.com"), + _ => panic!("Expected email to be a string"), + } + } + _ => panic!("Expected StructValue"), + } + + // overwrite existing key + let mut run = dummy_run; + let args = vec![ + a_struct(s_test()), + a_string("age"), + Argument::Eval(v_number(31.0)), + ]; + let signal = set(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::StructValue(st)) => { + assert_eq!(st.fields.len(), 3); + match st.fields.get("age") { + Some(Value { + kind: Some(Kind::NumberValue(n)), + .. + }) => assert_eq!(*n, 31.0), + _ => panic!("Expected age to be a number"), + } + } + _ => panic!("Expected StructValue"), + } + } + + #[test] + fn test_set_with_empty_object_and_nested() { + let mut ctx = Context::default(); + + // empty object -> add first key + let mut run = dummy_run; + let args = vec![ + a_struct(s_empty()), + a_string("first_key"), + Argument::Eval(v_bool(true)), + ]; + let signal = set(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::StructValue(st)) => { + assert_eq!(st.fields.len(), 1); + match st.fields.get("first_key") { + Some(Value { + kind: Some(Kind::BoolValue(b)), + .. + }) => assert_eq!(*b, true), + _ => panic!("Expected first_key to be a bool"), + } + } + _ => panic!("Expected StructValue"), + } + + // nested object value + let nested = { + let mut nf = HashMap::new(); + nf.insert("street".to_string(), v_string("123 Main St")); + v_struct(nf) + }; + let mut run = dummy_run; + let args = vec![ + a_struct(s_test()), + a_string("address"), + Argument::Eval(nested), + ]; + let signal = set(&args, &mut ctx, &mut run); + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::StructValue(st)) => { + match st.fields.get("address") { + Some(Value { + kind: Some(Kind::StructValue(_)), + .. + }) => { /* ok */ } + _ => panic!("Expected address to be a struct"), + } + } + _ => panic!("Expected StructValue"), + } + } + + #[test] + fn test_set_preserves_original_struct() { + let mut ctx = Context::default(); + let original = s_test(); + let original_len = original.fields.len(); + + // keep a clone to assert immutability + let orig_clone = original.clone(); + + let mut run = dummy_run; + let args = vec![ + a_struct(original), + a_string("new_key"), + Argument::Eval(v_string("new_val")), + ]; + let _ = set(&args, &mut ctx, &mut run); + + // ensure original (captured clone) unchanged + assert_eq!(orig_clone.fields.len(), original_len); + assert!(!orig_clone.fields.contains_key("new_key")); + } +} diff --git a/taurus/src/implementation/text.rs b/taurus/src/implementation/text.rs new file mode 100644 index 0000000..67d7e25 --- /dev/null +++ b/taurus/src/implementation/text.rs @@ -0,0 +1,890 @@ +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; +use crate::context::signal::Signal; +use crate::{context::context::Context, error::RuntimeError}; +use base64::Engine; +use tucana::shared::{ListValue, Value, value::Kind}; + +pub fn collect_text_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { + vec![ + ("std::text::as_bytes", HandlerFn::eager(as_bytes, 1)), + ("std::text::byte_size", HandlerFn::eager(byte_size, 1)), + ("std::text::capitalize", HandlerFn::eager(capitalize, 1)), + ("std::text::lowercase", HandlerFn::eager(lowercase, 1)), + ("std::text::uppercase", HandlerFn::eager(uppercase, 1)), + ("std::text::swapcase", HandlerFn::eager(swapcase, 1)), + ("std::text::trim", HandlerFn::eager(trim, 1)), + ("std::text::chars", HandlerFn::eager(chars, 1)), + ("std::text::at", HandlerFn::eager(at, 2)), + ("std::text::append", HandlerFn::eager(append, 2)), + ("std::text::prepend", HandlerFn::eager(prepend, 2)), + ("std::text::insert", HandlerFn::eager(insert, 3)), + ("std::text::length", HandlerFn::eager(length, 1)), + ("std::text::reverse", HandlerFn::eager(reverse, 1)), + ("std::text::remove", HandlerFn::eager(remove, 3)), + ("std::text::replace", HandlerFn::eager(replace, 3)), + ( + "std::text::replace_first", + HandlerFn::eager(replace_first, 3), + ), + ("std::text::replace_last", HandlerFn::eager(replace_last, 3)), + ("std::text::hex", HandlerFn::eager(hex, 1)), + ("std::text::octal", HandlerFn::eager(octal, 1)), + ("std::text::index_of", HandlerFn::eager(index_of, 2)), + ("std::text::contains", HandlerFn::eager(contains, 2)), + ("std::text::split", HandlerFn::eager(split, 2)), + ("std::text::starts_with", HandlerFn::eager(starts_with, 2)), + ("std::text::ends_with", HandlerFn::eager(ends_with, 2)), + ("std::text::to_ascii", HandlerFn::eager(to_ascii, 1)), + ("std::text::from_ascii", HandlerFn::eager(from_ascii, 1)), + ("std::text::encode", HandlerFn::eager(encode, 2)), + ("std::text::decode", HandlerFn::eager(decode, 2)), + ("std::text::is_equal", HandlerFn::eager(is_equal, 2)), + ] +} + +fn arg_err>(msg: S) -> Signal { + Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + msg.into(), + )) +} + +fn as_bytes(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + + let bytes: Vec = value + .as_bytes() + .iter() + .map(|b| Value { + kind: Some(Kind::NumberValue(*b as f64)), + }) + .collect(); + + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values: bytes })), + }) +} + +fn byte_size(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.len() as f64)), + }) +} + +fn capitalize( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: String); + + let capitalized = value + .split(' ') + .map(|word| { + if word.is_empty() { + return String::from(word); + } + let mut chars = word.chars(); + match chars.next() { + Some(first) => first.to_uppercase().collect::() + chars.as_str(), + None => String::from(word), + } + }) + .collect::>() + .join(" "); + + Signal::Success(Value { + kind: Some(Kind::StringValue(capitalized)), + }) +} + +fn uppercase(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + Signal::Success(Value { + kind: Some(Kind::StringValue(value.to_uppercase())), + }) +} + +fn lowercase(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + Signal::Success(Value { + kind: Some(Kind::StringValue(value.to_lowercase())), + }) +} + +fn swapcase(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + + let swapped = value + .chars() + .map(|c| { + if c.is_uppercase() { + c.to_lowercase().collect::() + } else if c.is_lowercase() { + c.to_uppercase().collect::() + } else { + c.to_string() + } + }) + .collect::(); + + Signal::Success(Value { + kind: Some(Kind::StringValue(swapped)), + }) +} + +fn trim(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + Signal::Success(Value { + kind: Some(Kind::StringValue(value.trim().to_string())), + }) +} + +fn chars(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + + let list = value + .chars() + .map(|c| Value { + kind: Some(Kind::StringValue(c.to_string())), + }) + .collect::>(); + + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values: list })), + }) +} + +fn at(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, index: f64); + + if index < 0.0 { + return arg_err("Expected a non-negative index"); + } + + let idx = index as usize; + match value.chars().nth(idx) { + Some(c) => Signal::Success(Value { + kind: Some(Kind::StringValue(c.to_string())), + }), + None => Signal::Failure(RuntimeError::simple( + "IndexOutOfBoundsRuntimeError", + format!( + "Index {} is out of bounds for string of length {}", + index, + value.chars().count() + ), + )), + } +} + +fn append(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, suffix: String); + Signal::Success(Value { + kind: Some(Kind::StringValue(value + &suffix)), + }) +} + +fn prepend(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, prefix: String); + Signal::Success(Value { + kind: Some(Kind::StringValue(prefix + &value)), + }) +} + +fn insert(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, position: f64, text: String); + + if position < 0.0 { + return arg_err("Expected a non-negative position"); + } + + let pos = position as usize; + // Byte-wise position (consistent with previous behavior). For char-wise, compute byte index from char idx. + if pos > value.len() { + return Signal::Failure(RuntimeError::simple( + "IndexOutOfBoundsRuntimeError", + format!("Position {} exceeds byte length {}", pos, value.len()), + )); + } + + let mut new_value = value; + new_value.insert_str(pos, &text); + + Signal::Success(Value { + kind: Some(Kind::StringValue(new_value)), + }) +} + +fn length(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + Signal::Success(Value { + kind: Some(Kind::NumberValue(value.chars().count() as f64)), + }) +} + +fn remove(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, from: f64, to: f64); + + if from < 0.0 || to < 0.0 { + return arg_err("Expected non-negative indices"); + } + + let from_u = from as usize; + let to_u = to as usize; + + let chars = value.chars().collect::>(); + if from_u > chars.len() || to_u > chars.len() { + return Signal::Failure(RuntimeError::simple( + "IndexOutOfBoundsRuntimeError", + format!( + "Indices [{}, {}) out of bounds for length {}", + from_u, + to_u, + chars.len() + ), + )); + } + + let new = chars + .into_iter() + .enumerate() + .filter(|&(i, _)| i < from_u || i >= to_u) + .map(|e| e.1) + .collect::(); + + Signal::Success(Value { + kind: Some(Kind::StringValue(new)), + }) +} + +fn replace(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, old: String, new: String); + let replaced = value.replace(&old, &new); + Signal::Success(Value { + kind: Some(Kind::StringValue(replaced)), + }) +} + +fn replace_first( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: String, old: String, new: String); + let replaced = value.replacen(&old, &new, 1); + Signal::Success(Value { + kind: Some(Kind::StringValue(replaced)), + }) +} + +fn replace_last( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: String, old: String, new: String); + + fn replace_last_impl(haystack: &str, needle: &str, replacement: &str) -> String { + if let Some(pos) = haystack.rfind(needle) { + let mut result = + String::with_capacity(haystack.len() - needle.len() + replacement.len()); + result.push_str(&haystack[..pos]); + result.push_str(replacement); + result.push_str(&haystack[pos + needle.len()..]); + result + } else { + haystack.to_string() + } + } + + let replaced = replace_last_impl(&value, &old, &new); + Signal::Success(Value { + kind: Some(Kind::StringValue(replaced)), + }) +} + +fn hex(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + + let hex = value + .as_bytes() + .iter() + .map(|b| format!("{:02x}", b)) + .collect::(); + + Signal::Success(Value { + kind: Some(Kind::StringValue(hex)), + }) +} + +fn octal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + + let oct = value + .as_bytes() + .iter() + .map(|b| format!("{:03o}", b)) + .collect::(); + + Signal::Success(Value { + kind: Some(Kind::StringValue(oct)), + }) +} + +fn index_of(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, sub: String); + + match value.find(&sub) { + Some(idx) => Signal::Success(Value { + kind: Some(Kind::NumberValue(idx as f64)), + }), + None => Signal::Success(Value { + kind: Some(Kind::NumberValue(-1.0)), + }), + } +} + +fn contains(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, sub: String); + Signal::Success(Value { + kind: Some(Kind::BoolValue(value.contains(&sub))), + }) +} + +fn split(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, delimiter: String); + + let parts = value + .split(&delimiter) + .map(|s| Value { + kind: Some(Kind::StringValue(s.to_string())), + }) + .collect::>(); + + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values: parts })), + }) +} + +fn reverse(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + + let reversed = value.chars().rev().collect::(); + Signal::Success(Value { + kind: Some(Kind::StringValue(reversed)), + }) +} + +fn starts_with( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: String, prefix: String); + Signal::Success(Value { + kind: Some(Kind::BoolValue(value.starts_with(&prefix))), + }) +} + +fn ends_with(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, suffix: String); + Signal::Success(Value { + kind: Some(Kind::BoolValue(value.ends_with(&suffix))), + }) +} + +fn to_ascii(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + + let ascii = value + .bytes() + .map(|b| Value { + kind: Some(Kind::NumberValue(b as f64)), + }) + .collect::>(); + + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values: ascii })), + }) +} + +fn from_ascii( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + // Requires a TryFromArg impl for ListValue in your macro system. + args!(args => list: ListValue); + + let string = list + .values + .iter() + .map(|v| match v { + Value { + kind: Some(Kind::NumberValue(n)), + } if *n >= 0.0 && *n <= 127.0 => Some(*n as u8 as char), + _ => None, + }) + .collect::>(); + + match string { + Some(s) => Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }), + None => arg_err("Expected a list of numbers between 0 and 127"), + } +} + +// NOTE: "encode"/"decode" currently only support base64. +fn encode(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, encoding: String); + + let encoded = match encoding.to_lowercase().as_str() { + "base64" => base64::prelude::BASE64_STANDARD.encode(value), + _ => { + return arg_err(format!("Unsupported encoding: {}", encoding)); + } + }; + + Signal::Success(Value { + kind: Some(Kind::StringValue(encoded)), + }) +} + +fn decode(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, encoding: String); + + let decoded = match encoding.to_lowercase().as_str() { + "base64" => match base64::prelude::BASE64_STANDARD.decode(value) { + Ok(bytes) => match String::from_utf8(bytes) { + Ok(s) => s, + Err(err) => { + return Signal::Failure(RuntimeError::simple( + "DecodeError", + format!("Failed to decode base64 bytes to UTF-8: {:?}", err), + )); + } + }, + Err(err) => { + return Signal::Failure(RuntimeError::simple( + "DecodeError", + format!("Failed to decode base64 string: {:?}", err), + )); + } + }, + _ => return arg_err(format!("Unsupported decoding: {}", encoding)), + }; + + Signal::Success(Value { + kind: Some(Kind::StringValue(decoded)), + }) +} + +fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: String, rhs: String); + Signal::Success(Value { + kind: Some(Kind::BoolValue(lhs == rhs)), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::context::context::Context; + use tucana::shared::{ListValue, Value, value::Kind}; + + // ---------- helpers: build Arguments ---------- + fn a_str(s: &str) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::StringValue(s.to_string())), + }) + } + fn a_num(n: f64) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::NumberValue(n)), + }) + } + fn a_list(vals: Vec) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::ListValue(ListValue { values: vals })), + }) + } + + // ---------- helpers: build bare Values ---------- + fn v_str(s: &str) -> Value { + Value { + kind: Some(Kind::StringValue(s.to_string())), + } + } + fn v_num(n: f64) -> Value { + Value { + kind: Some(Kind::NumberValue(n)), + } + } + + // ---------- helpers: extract from Signal ---------- + fn expect_num(sig: Signal) -> f64 { + match sig { + Signal::Success(Value { + kind: Some(Kind::NumberValue(n)), + }) => n, + other => panic!("Expected NumberValue, got {:?}", other), + } + } + fn expect_bool(sig: Signal) -> bool { + match sig { + Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }) => b, + other => panic!("Expected BoolValue, got {:?}", other), + } + } + fn expect_str(sig: Signal) -> String { + match sig { + Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }) => s, + other => panic!("Expected StringValue, got {:?}", other), + } + } + fn expect_list(sig: Signal) -> Vec { + match sig { + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values })), + }) => values, + other => panic!("Expected ListValue, got {:?}", other), + } + } + + // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) + } + + // ---------- tests ---------- + + #[test] + fn test_as_bytes_and_byte_size() { + let mut ctx = Context::default(); + let mut run = dummy_run; + + // "hello" -> 5 bytes + let bytes = expect_list(as_bytes(&[a_str("hello")], &mut ctx, &mut run)); + assert_eq!(bytes.len(), 5); + assert_eq!(bytes[0], v_num(104.0)); // 'h' + + let mut run = dummy_run; + assert_eq!( + expect_num(byte_size(&[a_str("hello")], &mut ctx, &mut run)), + 5.0 + ); + + // unicode: "café" -> 5 bytes, 4 chars + let mut run = dummy_run; + assert_eq!( + expect_num(byte_size(&[a_str("café")], &mut ctx, &mut run)), + 5.0 + ); + let mut run = dummy_run; + assert_eq!( + expect_num(length(&[a_str("café")], &mut ctx, &mut run)), + 4.0 + ); + } + + #[test] + fn test_case_ops_and_trim() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_str(capitalize(&[a_str("hello world")], &mut ctx, &mut run)), + "Hello World" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(uppercase(&[a_str("Hello")], &mut ctx, &mut run)), + "HELLO" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(lowercase(&[a_str("Hello")], &mut ctx, &mut run)), + "hello" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(swapcase(&[a_str("HeLLo123")], &mut ctx, &mut run)), + "hEllO123" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(trim(&[a_str(" hi ")], &mut ctx, &mut run)), + "hi" + ); + } + + #[test] + fn test_chars_and_at() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + let chars_list = expect_list(chars(&[a_str("abc")], &mut ctx, &mut run)); + assert_eq!(chars_list, vec![v_str("a"), v_str("b"), v_str("c")]); + + let mut run = dummy_run; + assert_eq!( + expect_str(at(&[a_str("hello"), a_num(1.0)], &mut ctx, &mut run)), + "e" + ); + + // out-of-bounds + let mut run = dummy_run; + match at(&[a_str("hi"), a_num(5.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure, got {:?}", s), + } + // negative + let mut run = dummy_run; + match at(&[a_str("hi"), a_num(-1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure, got {:?}", s), + } + } + + #[test] + fn test_append_prepend_insert_length() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_str(append( + &[a_str("hello"), a_str(" world")], + &mut ctx, + &mut run + )), + "hello world" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(prepend( + &[a_str("world"), a_str("hello ")], + &mut ctx, + &mut run + )), + "hello world" + ); + + // insert uses BYTE index; for ASCII this matches char index + let mut run = dummy_run; + assert_eq!( + expect_str(insert( + &[a_str("hello"), a_num(2.0), a_str("XXX")], + &mut ctx, + &mut run + )), + "heXXXllo" + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(length(&[a_str("hello")], &mut ctx, &mut run)), + 5.0 + ); + } + + #[test] + fn test_remove_replace_variants() { + let mut ctx = Context::default(); + + // remove uses CHAR indices [from, to) + let mut run = dummy_run; + assert_eq!( + expect_str(remove( + &[a_str("hello world"), a_num(2.0), a_num(7.0)], + &mut ctx, + &mut run + )), + "heorld" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(replace( + &[a_str("hello world hello"), a_str("hello"), a_str("hi")], + &mut ctx, + &mut run + )), + "hi world hi" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(replace_first( + &[a_str("one two one"), a_str("one"), a_str("1")], + &mut ctx, + &mut run + )), + "1 two one" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(replace_last( + &[a_str("one two one"), a_str("one"), a_str("1")], + &mut ctx, + &mut run + )), + "one two 1" + ); + } + + #[test] + fn test_hex_octal_reverse() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_str(hex(&[a_str("hello")], &mut ctx, &mut run)), + "68656c6c6f" + ); + + let mut run = dummy_run; + assert_eq!(expect_str(octal(&[a_str("A")], &mut ctx, &mut run)), "101"); + + let mut run = dummy_run; + assert_eq!( + expect_str(reverse(&[a_str("hello")], &mut ctx, &mut run)), + "olleh" + ); + } + + #[test] + fn test_index_contains_split_starts_ends() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_num(index_of( + &[a_str("hello world"), a_str("world")], + &mut ctx, + &mut run + )), + 6.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(index_of( + &[a_str("hello"), a_str("xyz")], + &mut ctx, + &mut run + )), + -1.0 + ); + + let mut run = dummy_run; + assert!(expect_bool(contains( + &[a_str("hello world"), a_str("world")], + &mut ctx, + &mut run + ))); + + let mut run = dummy_run; + let split_list = expect_list(split(&[a_str("a,b,c"), a_str(",")], &mut ctx, &mut run)); + assert_eq!(split_list, vec![v_str("a"), v_str("b"), v_str("c")]); + + let mut run = dummy_run; + assert!(expect_bool(starts_with( + &[a_str("hello"), a_str("he")], + &mut ctx, + &mut run + ))); + + let mut run = dummy_run; + assert!(expect_bool(ends_with( + &[a_str("hello"), a_str("lo")], + &mut ctx, + &mut run + ))); + } + + #[test] + fn test_to_ascii_and_from_ascii() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + let ascii_vals = expect_list(to_ascii(&[a_str("AB")], &mut ctx, &mut run)); + assert_eq!(ascii_vals, vec![v_num(65.0), v_num(66.0)]); + + let mut run = dummy_run; + let list_arg = a_list(vec![v_num(65.0), v_num(66.0), v_num(67.0)]); + assert_eq!( + expect_str(from_ascii(&[list_arg], &mut ctx, &mut run)), + "ABC" + ); + + // invalid element + let mut run = dummy_run; + let list_arg = a_list(vec![v_num(65.0), v_num(128.0)]); + match from_ascii(&[list_arg], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for invalid ASCII, got {:?}", s), + } + } + + #[test] + fn test_encode_decode_base64_and_is_equal() { + let mut ctx = Context::default(); + + let mut run = dummy_run; + assert_eq!( + expect_str(encode( + &[a_str("hello"), a_str("BASE64")], + &mut ctx, + &mut run + )), + "aGVsbG8=" + ); + + let mut run = dummy_run; + assert_eq!( + expect_str(decode( + &[a_str("aGVsbG8="), a_str("base64")], + &mut ctx, + &mut run + )), + "hello" + ); + + // unsupported codec + let mut run = dummy_run; + match encode(&[a_str("data"), a_str("gug")], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for unsupported encoding, got {:?}", s), + } + + let mut run = dummy_run; + assert!(expect_bool(is_equal( + &[a_str("x"), a_str("x")], + &mut ctx, + &mut run + ))); + let mut run = dummy_run; + assert!(!expect_bool(is_equal( + &[a_str("x"), a_str("y")], + &mut ctx, + &mut run + ))); + } +} diff --git a/src/main.rs b/taurus/src/main.rs similarity index 95% rename from src/main.rs rename to taurus/src/main.rs index 1300e01..56eec1b 100644 --- a/src/main.rs +++ b/taurus/src/main.rs @@ -1,27 +1,25 @@ mod config; pub mod context; pub mod error; -mod executor; pub mod implementation; -pub mod registry; use crate::config::Config; +use crate::context::executor::Executor; +use crate::context::registry::FunctionStore; use crate::context::signal::Signal; -use crate::executor::Executor; use crate::implementation::collect; use code0_flow::flow_config::load_env_file; -use context::Context; +use context::context::Context; use futures_lite::StreamExt; use log::error; use prost::Message; -use registry::FunctionStore; use std::collections::HashMap; use tonic_health::pb::health_server::HealthServer; use tucana::shared::value::Kind; use tucana::shared::{ExecutionFlow, NodeFunction, Value}; fn handle_message(flow: ExecutionFlow, store: &FunctionStore) -> Signal { - let context = Context::new(); + let context = Context::default(); let node_functions: HashMap = flow .node_functions