diff --git a/Cargo.lock b/Cargo.lock index 430562da..8a0260de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,15 +240,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - [[package]] name = "aho-corasick" version = "1.0.2" @@ -330,7 +321,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -340,7 +331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -351,9 +342,9 @@ checksum = "ea50b14b7a4b9343f8c627a7a53c52076482bd4bdad0a24fd3ec533ed616cc2c" [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "approx" @@ -492,9 +483,9 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" -version = "0.1.69" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", @@ -546,9 +537,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" +checksum = "a6a1de45611fdb535bfde7b7de4fd54f4fd2b17b1737c0a59b69bf9b92074b8c" dependencies = [ "async-trait", "axum-core", @@ -711,7 +702,7 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "prettyplease 0.2.9", + "prettyplease 0.2.10", "proc-macro2", "quote", "regex", @@ -1312,9 +1303,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -1322,37 +1313,37 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn 2.0.26", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 2.0.26", ] [[package]] name = "dashmap" -version = "5.4.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" dependencies = [ "cfg-if", - "hashbrown 0.12.3", + "hashbrown 0.14.0", "lock_api", "once_cell", "parking_lot_core 0.9.8", @@ -1508,9 +1499,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" @@ -1520,7 +1511,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1577,7 +1568,7 @@ checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", "rustix 0.38.4", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1892,9 +1883,9 @@ dependencies = [ [[package]] name = "geo-types" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1019f6d372c5b53143f08deee4168d05c22920fe5e0f51f0dfb0e8ffb67ec11e" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" dependencies = [ "approx", "num-traits", @@ -1949,11 +1940,11 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" dependencies = [ - "aho-corasick 0.7.20", + "aho-corasick", "bstr", "fnv", "log", @@ -2223,13 +2214,14 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", - "rustls 0.21.2", + "rustls 0.21.5", "tokio", "tokio-rustls 0.24.1", ] @@ -2415,7 +2407,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.2", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2432,7 +2424,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", "rustix 0.38.4", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2455,9 +2447,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" @@ -2746,7 +2738,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -3147,9 +3139,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -3348,9 +3340,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9825a04601d60621feed79c4e6b56d65db77cdca55cef43b46b0de1096d1c282" +checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387" dependencies = [ "proc-macro2", "syn 2.0.26", @@ -3401,9 +3393,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -3602,9 +3594,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" dependencies = [ "proc-macro2", ] @@ -3775,7 +3767,7 @@ version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", "regex-automata 0.3.3", "regex-syntax 0.7.4", @@ -3796,7 +3788,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", "regex-syntax 0.7.4", ] @@ -3854,7 +3846,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.2", + "rustls 0.21.5", "rustls-pemfile", "serde", "serde_json", @@ -3998,9 +3990,9 @@ dependencies = [ [[package]] name = "rquickjs" -version = "0.3.1" +version = "0.4.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db7788c2818f4546daabe9ae2d1ee2f4db61ab1998d4b483494c4193cc38dab" +checksum = "ccd00408a820b4b0986ef746b9d614a248930b05059a64f621fe7f991bfff3c0" dependencies = [ "rquickjs-core", "rquickjs-macro", @@ -4008,9 +4000,9 @@ dependencies = [ [[package]] name = "rquickjs-core" -version = "0.3.1" +version = "0.4.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12cf8646fe0af5bcff2822ccd162990f0679a1f9287c7257f4f4193a9d31ea9" +checksum = "73d8e512a3ecb683bf3ae03346ee60450790592ca179a9613491ae0a141c42a9" dependencies = [ "async-lock", "relative-path", @@ -4019,9 +4011,9 @@ dependencies = [ [[package]] name = "rquickjs-macro" -version = "0.3.1" +version = "0.4.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80564583a91b0ae6b2d6b9b3d0f8ffd69a4b17202cc63a12df78dfa8983885fc" +checksum = "254f40a1a45a65e2fed556ee5f2d54eba1ba537e85109e3ac7e11fca759e9c0d" dependencies = [ "darling", "fnv", @@ -4032,14 +4024,14 @@ dependencies = [ "proc-macro2", "quote", "rquickjs-core", - "syn 1.0.109", + "syn 2.0.26", ] [[package]] name = "rquickjs-sys" -version = "0.3.1" +version = "0.4.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b747058afd4d988d056e4972ec8516a5a86fdfc103c1c1485bfee8966a0743ae" +checksum = "351f5b86c2588d2f5eb7c50df37adc4dedff944d0f6cd7913dd6e3df1636ec6e" dependencies = [ "bindgen 0.65.1", "cc", @@ -4116,7 +4108,7 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4129,7 +4121,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.3", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4146,9 +4138,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", @@ -4167,9 +4159,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e" dependencies = [ "ring", "untrusted", @@ -4177,9 +4169,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rustyline" @@ -4218,9 +4210,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salsa20" @@ -4242,11 +4234,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -4314,9 +4306,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" dependencies = [ "serde", ] @@ -4338,9 +4330,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] @@ -4368,9 +4360,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" dependencies = [ "itoa", "ryu", @@ -4379,9 +4371,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1b6471d7496b051e03f1958802a73f88b947866f5146f329e47e36554f4e55" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" dependencies = [ "itoa", "serde", @@ -4516,9 +4508,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "snap" @@ -4915,7 +4907,7 @@ dependencies = [ "fastrand", "redox_syscall 0.3.5", "rustix 0.37.23", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4934,7 +4926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" dependencies = [ "rustix 0.37.23", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -5070,7 +5062,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -5121,7 +5113,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.2", + "rustls 0.21.5", "tokio", ] @@ -5186,9 +5178,9 @@ checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.11" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap 2.0.0", "toml_datetime", @@ -5437,9 +5429,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -5499,9 +5491,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "atomic", "getrandom 0.2.10", @@ -5777,21 +5769,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -5807,93 +5784,51 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" @@ -5902,9 +5837,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.7" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] diff --git a/lib/Cargo.toml b/lib/Cargo.toml index f3830b5b..db217307 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -75,7 +75,7 @@ fuzzy-matcher = "0.3.7" geo = { version = "0.25.1", features = ["use-serde"] } indexmap = { version = "1.9.3", features = ["serde"] } indxdb = { version = "0.3.0", optional = true } -js = { version = "0.3.1" , package = "rquickjs", features = ["array-buffer", "bindgen", "classes", "futures", "loader", "macro", "parallel", "properties","rust-alloc"], optional = true } +js = { version = "0.4.0-beta.2" , package = "rquickjs", features = ["array-buffer", "bindgen", "classes", "futures", "loader", "macro", "parallel", "properties","rust-alloc"], optional = true } jsonwebtoken = "8.3.0" lexicmp = "0.1.0" lru = "0.10.1" diff --git a/lib/src/fnc/script/classes/duration.rs b/lib/src/fnc/script/classes/duration.rs index 7631deb3..7b36849b 100644 --- a/lib/src/fnc/script/classes/duration.rs +++ b/lib/src/fnc/script/classes/duration.rs @@ -1,51 +1,48 @@ -#[js::bind(object, public)] -#[quickjs(bare)] -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(clippy::module_inception)] -pub mod duration { +use js::class::Trace; - use crate::sql::duration; - use crate::sql::value::Value; - use js::{class::Ref, function::Rest}; +use crate::sql::duration; - #[derive(Clone)] - #[quickjs(cloneable)] - pub struct Duration { - pub(crate) value: Option<duration::Duration>, +#[derive(Clone, Trace)] +#[js::class] +pub struct Duration { + #[qjs(skip_trace)] + pub(crate) value: Option<duration::Duration>, +} + +#[js::methods] +impl Duration { + #[qjs(constructor)] + pub fn new(value: String) -> Self { + Self { + value: duration::Duration::try_from(value).ok(), + } } - impl Duration { - #[quickjs(constructor)] - pub fn new(value: String, args: Rest<Value>) -> Self { - Self { - value: duration::Duration::try_from(value).ok(), - } + #[qjs(get)] + pub fn value(&self) -> String { + match &self.value { + Some(v) => v.to_raw(), + None => String::from("Invalid Duration"), } - #[quickjs(get)] - pub fn value(&self) -> String { - match &self.value { - Some(v) => v.to_raw(), - None => String::from("Invalid Duration"), - } + } + // Compare two Duration instances + pub fn is(a: &Duration, b: &Duration) -> bool { + a.value.is_some() && b.value.is_some() && a.value == b.value + } + /// Convert the object to a string + #[qjs(rename = "toString")] + pub fn js_to_string(&self) -> String { + match &self.value { + Some(v) => v.to_raw(), + None => String::from("Invalid Duration"), } - // Compare two Duration instances - pub fn is(a: Ref<Duration>, b: Ref<Duration>, args: Rest<()>) -> bool { - a.value.is_some() && b.value.is_some() && a.value == b.value - } - /// Convert the object to a string - pub fn toString(&self, args: Rest<()>) -> String { - match &self.value { - Some(v) => v.to_raw(), - None => String::from("Invalid Duration"), - } - } - /// Convert the object to JSON - pub fn toJSON(&self, args: Rest<()>) -> String { - match &self.value { - Some(v) => v.to_raw(), - None => String::from("Invalid Duration"), - } + } + /// Convert the object to JSON + #[qjs(rename = "toJSON")] + pub fn to_json(&self) -> String { + match &self.value { + Some(v) => v.to_raw(), + None => String::from("Invalid Duration"), } } } diff --git a/lib/src/fnc/script/classes/mod.rs b/lib/src/fnc/script/classes/mod.rs index 11041b2d..6699419e 100644 --- a/lib/src/fnc/script/classes/mod.rs +++ b/lib/src/fnc/script/classes/mod.rs @@ -1,13 +1,13 @@ -use js::{Ctx, Result}; +use js::{Class, Ctx, Result}; pub mod duration; pub mod record; pub mod uuid; -pub fn init(ctx: Ctx<'_>) -> Result<()> { +pub fn init(ctx: &Ctx<'_>) -> Result<()> { let globals = ctx.globals(); - globals.init_def::<duration::Duration>()?; - globals.init_def::<record::Record>()?; - globals.init_def::<uuid::Uuid>()?; + Class::<duration::Duration>::define(&globals)?; + Class::<record::Record>::define(&globals)?; + Class::<uuid::Uuid>::define(&globals)?; Ok(()) } diff --git a/lib/src/fnc/script/classes/record.rs b/lib/src/fnc/script/classes/record.rs index 0508f8be..f8c27ce8 100644 --- a/lib/src/fnc/script/classes/record.rs +++ b/lib/src/fnc/script/classes/record.rs @@ -1,56 +1,52 @@ -#[js::bind(object, public)] -#[quickjs(bare)] -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(clippy::module_inception)] -pub mod record { +use crate::sql::thing; +use crate::sql::value::Value; +use js::class::Trace; - use crate::sql::thing; - use crate::sql::value::Value; - use js::{class::Ref, function::Rest}; +#[derive(Clone, Trace)] +#[js::class] +pub struct Record { + #[qjs(skip_trace)] + pub(crate) value: thing::Thing, +} - #[derive(Clone)] - #[quickjs(cloneable)] - pub struct Record { - pub(crate) value: thing::Thing, +#[js::methods] +impl Record { + #[qjs(constructor)] + pub fn new(tb: String, id: Value) -> Self { + Self { + value: thing::Thing { + tb, + id: match id { + Value::Array(v) => v.into(), + Value::Object(v) => v.into(), + Value::Number(v) => v.into(), + v => v.as_string().into(), + }, + }, + } } - impl Record { - #[quickjs(constructor)] - pub fn new(tb: String, id: Value, args: Rest<()>) -> Self { - Self { - value: thing::Thing { - tb, - id: match id { - Value::Array(v) => v.into(), - Value::Object(v) => v.into(), - Value::Number(v) => v.into(), - v => v.as_string().into(), - }, - }, - } - } + #[qjs(get)] + pub fn tb(&self) -> String { + self.value.tb.clone() + } - #[quickjs(get)] - pub fn tb(&self) -> String { - self.value.tb.clone() - } - - #[quickjs(get)] - pub fn id(&self) -> String { - self.value.id.to_raw() - } - // Compare two Record instances - pub fn is<'js>(a: Ref<'js, Record>, b: Ref<'js, Record>, args: Rest<()>) -> bool { - a.value == b.value - } - /// Convert the object to a string - pub fn toString(&self, args: Rest<()>) -> String { - self.value.to_raw() - } - /// Convert the object to JSON - pub fn toJSON(&self, args: Rest<()>) -> String { - self.value.to_raw() - } + #[qjs(get)] + pub fn id(&self) -> String { + self.value.id.to_raw() + } + // Compare two Record instances + pub fn is(a: &Record, b: &Record) -> bool { + a.value == b.value + } + /// Convert the object to a string + #[qjs(rename = "toString")] + pub fn js_to_string(&self) -> String { + self.value.to_raw() + } + /// Convert the object to JSON + #[qjs(rename = "toJSON")] + pub fn to_json(&self) -> String { + self.value.to_raw() } } diff --git a/lib/src/fnc/script/classes/uuid.rs b/lib/src/fnc/script/classes/uuid.rs index 4f759ec4..e89e412e 100644 --- a/lib/src/fnc/script/classes/uuid.rs +++ b/lib/src/fnc/script/classes/uuid.rs @@ -1,51 +1,46 @@ -#[js::bind(object, public)] -#[quickjs(bare)] -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(clippy::module_inception)] -pub mod uuid { +use crate::sql::uuid; +use js::class::Trace; - use crate::sql::uuid; - use crate::sql::value::Value; - use js::{class::Ref, function::Rest}; +#[derive(Clone, Trace)] +#[js::class] +pub struct Uuid { + #[qjs(skip_trace)] + pub(crate) value: Option<uuid::Uuid>, +} - #[derive(Clone)] - #[quickjs(cloneable)] - pub struct Uuid { - pub(crate) value: Option<uuid::Uuid>, +#[js::methods] +impl Uuid { + #[qjs(constructor)] + pub fn new(value: String) -> Self { + Self { + value: uuid::Uuid::try_from(value).ok(), + } } - - impl Uuid { - #[quickjs(constructor)] - pub fn new(value: String, args: Rest<Value>) -> Self { - Self { - value: uuid::Uuid::try_from(value).ok(), - } + #[qjs(get)] + pub fn value(&self) -> String { + match &self.value { + Some(v) => v.to_raw(), + None => String::from("Invalid Uuid"), } - #[quickjs(get)] - pub fn value(&self) -> String { - match &self.value { - Some(v) => v.to_raw(), - None => String::from("Invalid Uuid"), - } + } + // Compare two Uuid instances + pub fn is(a: &Uuid, b: &Uuid) -> bool { + a.value.is_some() && b.value.is_some() && a.value == b.value + } + /// Convert the object to a string + #[qjs(rename = "toString")] + pub fn js_to_string(&self) -> String { + match &self.value { + Some(v) => v.to_raw(), + None => String::from("Invalid Uuid"), } - // Compare two Uuid instances - pub fn is<'js>(a: Ref<'js, Uuid>, b: Ref<'js, Uuid>, _args: Rest<()>) -> bool { - a.value.is_some() && b.value.is_some() && a.value == b.value - } - /// Convert the object to a string - pub fn toString(&self, _args: Rest<()>) -> String { - match &self.value { - Some(v) => v.to_raw(), - None => String::from("Invalid Uuid"), - } - } - /// Convert the object to JSON - pub fn toJSON(&self, _args: Rest<()>) -> String { - match &self.value { - Some(v) => v.to_raw(), - None => String::from("Invalid Uuid"), - } + } + /// Convert the object to JSON + #[qjs(rename = "toJSON")] + pub fn to_json(&self) -> String { + match &self.value { + Some(v) => v.to_raw(), + None => String::from("Invalid Uuid"), } } } diff --git a/lib/src/fnc/script/fetch/body.rs b/lib/src/fnc/script/fetch/body.rs index 6e7fa863..17cb19b7 100644 --- a/lib/src/fnc/script/fetch/body.rs +++ b/lib/src/fnc/script/fetch/body.rs @@ -1,4 +1,4 @@ -use crate::fnc::script::fetch::{classes::BlobClass, stream::ReadableStream, RequestError}; +use crate::fnc::script::fetch::{stream::ReadableStream, RequestError}; use bytes::{Bytes, BytesMut}; use futures::{future, Stream, TryStreamExt}; use js::{ArrayBuffer, Class, Ctx, Error, Exception, FromJs, Result, Type, TypedArray, Value}; @@ -7,6 +7,8 @@ use std::{ result::Result as StdResult, }; +use super::classes::Blob; + pub type StreamItem = StdResult<Bytes, RequestError>; #[derive(Clone)] @@ -127,7 +129,7 @@ impl Body { } impl<'js> FromJs<'js> for Body { - fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> { + fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { let object = match value.type_of() { Type::String => { let string = value.as_string().unwrap().to_string()?; @@ -142,7 +144,7 @@ impl<'js> FromJs<'js> for Body { }) } }; - if let Ok(x) = Class::<BlobClass>::from_object(object.clone()) { + if let Some(x) = Class::<Blob>::from_object(object.clone()) { let borrow = x.borrow(); return Ok(Body::buffer(BodyKind::Blob(borrow.mime.clone()), borrow.data.clone())); } @@ -194,7 +196,7 @@ impl<'js> FromJs<'js> for Body { .ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?; return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes))); } - if let Ok(x) = ArrayBuffer::from_object(object.clone()) { + if let Some(x) = ArrayBuffer::from_object(object.clone()) { let bytes = x .as_bytes() .ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?; diff --git a/lib/src/fnc/script/fetch/classes/blob.rs b/lib/src/fnc/script/fetch/classes/blob.rs index a616b4de..95e18bb0 100644 --- a/lib/src/fnc/script/fetch/classes/blob.rs +++ b/lib/src/fnc/script/fetch/classes/blob.rs @@ -1,9 +1,11 @@ //! Blob class implementation -use bytes::BytesMut; -use js::{bind, prelude::Coerced, ArrayBuffer, Class, Ctx, Exception, FromJs, Result, Value}; - -pub use blob::Blob as BlobClass; +use bytes::{Bytes, BytesMut}; +use js::{ + class::Trace, + prelude::{Coerced, Opt}, + ArrayBuffer, Class, Ctx, Exception, FromJs, Object, Result, Value, +}; #[derive(Clone, Copy)] pub enum EndingType { @@ -12,7 +14,7 @@ pub enum EndingType { } fn append_blob_part<'js>( - ctx: Ctx<'js>, + ctx: &Ctx<'js>, value: Value<'js>, ending: EndingType, data: &mut BytesMut, @@ -23,11 +25,11 @@ fn append_blob_part<'js>( const LINE_ENDING: &[u8] = b"\n"; if let Some(object) = value.as_object() { - if let Ok(x) = Class::<BlobClass>::from_object(object.clone()) { + if let Some(x) = Class::<Blob>::from_object(object.clone()) { data.extend_from_slice(&x.borrow().data); return Ok(()); } - if let Ok(x) = ArrayBuffer::from_object(object.clone()) { + if let Some(x) = ArrayBuffer::from_object(object.clone()) { data.extend_from_slice(x.as_bytes().ok_or_else(|| { Exception::throw_type(ctx, "Tried to construct blob with detached buffer") })?); @@ -74,144 +76,121 @@ fn normalize_type(mut ty: String) -> String { } } -#[bind(object, public)] -#[quickjs(bare)] -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(clippy::module_inception)] -mod blob { - use super::*; +#[derive(Clone, Trace)] +#[js::class] +pub struct Blob { + pub(crate) mime: String, + // TODO: make bytes? + #[qjs(skip_trace)] + pub(crate) data: Bytes, +} - use bytes::{Bytes, BytesMut}; - use js::{ - function::{Opt, Rest}, - ArrayBuffer, Ctx, Exception, Object, Result, Value, - }; +#[js::methods] +impl Blob { + // ------------------------------ + // Constructor + // ------------------------------ - #[derive(Clone)] - #[quickjs(cloneable)] - pub struct Blob { - pub(crate) mime: String, - // TODO: make bytes? - pub(crate) data: Bytes, + #[qjs(constructor)] + pub fn new<'js>( + ctx: Ctx<'js>, + parts: Opt<Value<'js>>, + options: Opt<Object<'js>>, + ) -> Result<Self> { + let mut r#type = String::new(); + let mut endings = EndingType::Transparent; + + if let Some(obj) = options.into_inner() { + if let Some(x) = obj.get::<_, Option<Coerced<String>>>("type")? { + r#type = normalize_type(x.to_string()); + } + if let Some(Coerced(x)) = obj.get::<_, Option<Coerced<String>>>("endings")? { + if x == "native" { + endings = EndingType::Native; + } else if x != "transparent" { + return Err(Exception::throw_type( + &ctx, + ",expected endings to be either 'transparent' or 'native'", + )); + } + } + } + + let data = if let Some(parts) = parts.into_inner() { + let array = parts + .into_array() + .ok_or_else(|| Exception::throw_type(&ctx, "Blob parts are not a sequence"))?; + + let mut buffer = BytesMut::new(); + + for elem in array.iter::<Value>() { + let elem = elem?; + append_blob_part(&ctx, elem, endings, &mut buffer)?; + } + buffer.freeze() + } else { + Bytes::new() + }; + Ok(Self { + mime: r#type, + data, + }) } - impl Blob { - // ------------------------------ - // Constructor - // ------------------------------ + // ------------------------------ + // Instance properties + // ------------------------------ - #[quickjs(constructor)] - pub fn new<'js>( - ctx: Ctx<'js>, - parts: Opt<Value<'js>>, - options: Opt<Object<'js>>, - _rest: Rest<()>, - ) -> Result<Self> { - let mut r#type = String::new(); - let mut endings = EndingType::Transparent; + #[qjs(get)] + pub fn size(&self) -> usize { + self.data.len() + } - if let Some(obj) = options.into_inner() { - if let Some(x) = obj.get::<_, Option<Coerced<String>>>("type")? { - r#type = normalize_type(x.to_string()); - } - if let Some(Coerced(x)) = obj.get::<_, Option<Coerced<String>>>("endings")? { - if x == "native" { - endings = EndingType::Native; - } else if x != "transparent" { - return Err(Exception::throw_type( - ctx, - ",expected endings to be either 'transparent' or 'native'", - )); - } - } - } + #[qjs(get, rename = "type")] + pub fn r#type(&self) -> String { + self.mime.clone() + } - let data = if let Some(parts) = parts.into_inner() { - let array = parts - .into_array() - .ok_or_else(|| Exception::throw_type(ctx, "Blob parts are not a sequence"))?; - - let mut buffer = BytesMut::new(); - - for elem in array.iter::<Value>() { - let elem = elem?; - append_blob_part(ctx, elem, endings, &mut buffer)?; - } - buffer.freeze() - } else { - Bytes::new() - }; - Ok(Self { - mime: r#type, - data, - }) + pub fn slice(&self, start: Opt<isize>, end: Opt<isize>, content_type: Opt<String>) -> Blob { + // see https://w3c.github.io/FileAPI/#slice-blob + let start = start.into_inner().unwrap_or_default(); + let start = if start < 0 { + (self.data.len() as isize + start).max(0) as usize + } else { + start as usize + }; + let end = end.into_inner().unwrap_or_default(); + let end = if end < 0 { + (self.data.len() as isize + end).max(0) as usize + } else { + end as usize + }; + let data = self.data.slice(start..end); + let content_type = content_type.into_inner().map(normalize_type).unwrap_or_default(); + Blob { + mime: content_type, + data, } + } - // ------------------------------ - // Instance properties - // ------------------------------ + pub async fn text(&self) -> Result<String> { + let text = String::from_utf8(self.data.to_vec())?; + Ok(text) + } - #[quickjs(get)] - pub fn size(&self) -> usize { - self.data.len() - } + #[qjs(rename = "arrayBuffer")] + pub async fn array_buffer<'js>(&self, ctx: Ctx<'js>) -> Result<ArrayBuffer<'js>> { + ArrayBuffer::new(ctx, self.data.to_vec()) + } - #[quickjs(get)] - #[quickjs(rename = "type")] - pub fn r#type(&self) -> String { - self.mime.clone() - } + // ------------------------------ + // Instance methods + // ------------------------------ - pub fn slice( - &self, - start: Opt<isize>, - end: Opt<isize>, - content_type: Opt<String>, - _rest: Rest<()>, - ) -> Blob { - // see https://w3c.github.io/FileAPI/#slice-blob - let start = start.into_inner().unwrap_or_default(); - let start = if start < 0 { - (self.data.len() as isize + start).max(0) as usize - } else { - start as usize - }; - let end = end.into_inner().unwrap_or_default(); - let end = if end < 0 { - (self.data.len() as isize + end).max(0) as usize - } else { - end as usize - }; - let data = self.data.slice(start..end); - let content_type = content_type.into_inner().map(normalize_type).unwrap_or_default(); - Blob { - mime: content_type, - data, - } - } - - pub async fn text(&self, _rest: Rest<()>) -> Result<String> { - let text = String::from_utf8(self.data.to_vec())?; - Ok(text) - } - - pub async fn arrayBuffer<'js>( - &self, - ctx: Ctx<'js>, - _rest: Rest<()>, - ) -> Result<ArrayBuffer<'js>> { - ArrayBuffer::new(ctx, self.data.to_vec()) - } - - // ------------------------------ - // Instance methods - // ------------------------------ - - // Convert the object to a string - pub fn toString(&self, _rest: Rest<()>) -> String { - String::from("[object Blob]") - } + // Convert the object to a string + #[qjs(rename = "toString")] + pub fn js_to_string(&self) -> String { + String::from("[object Blob]") } } @@ -242,17 +221,17 @@ mod test { blob = new Blob(["\n\r\n \n\r"],{endings: "transparent"}); assert.eq(blob.size,6) - assert.eq(await blob.text(),"\n\r\n \n\r"); + assert.eq(await blob.text(),"\n\r\n \n\r"); blob = new Blob(["\n\r\n \n\r"],{endings: "native"}); // \n \r\n and the \n from \n\r are converted. // the part of the string which isn't converted is the space and the \r assert.eq(await blob.text(),`${NATIVE_LINE_ENDING}${NATIVE_LINE_ENDING} ${NATIVE_LINE_ENDING}\r`); assert.eq(blob.size,NATIVE_LINE_ENDING.length*3 + 2) - assert.mustThrow(() => new Blob("text")); + assert.mustThrow(() => new Blob("text")); assert.mustThrow(() => new Blob(["text"], {endings: "invalid value"})); })() - "#).catch(ctx).unwrap().await.catch(ctx).unwrap(); + "#).catch(&ctx).unwrap().await.catch(&ctx).unwrap(); }) .await } diff --git a/lib/src/fnc/script/fetch/classes/form_data.rs b/lib/src/fnc/script/fetch/classes/form_data.rs index f77cd8f7..a963bc98 100644 --- a/lib/src/fnc/script/fetch/classes/form_data.rs +++ b/lib/src/fnc/script/fetch/classes/form_data.rs @@ -1,34 +1,36 @@ //! FormData class implementation use js::{ - bind, function::Opt, prelude::Coerced, Class, Ctx, Exception, FromJs, Persistent, Result, - String, Value, + class::{Class, Trace}, + function::{Opt, Rest}, + prelude::Coerced, + Ctx, Exception, FromJs, Result, String, Value, }; -use std::string::String as StdString; +use reqwest::multipart::{Form, Part}; +use std::{collections::HashMap, string::String as StdString}; -use crate::fnc::script::fetch::classes::BlobClass; +use crate::fnc::script::fetch::classes::Blob; #[derive(Clone)] -pub enum FormDataValue { - String(Persistent<String<'static>>), +pub enum FormDataValue<'js> { + String(String<'js>), Blob { - data: Persistent<Class<'static, BlobClass>>, - filename: Option<Persistent<String<'static>>>, + data: Class<'js, Blob>, + filename: Option<String<'js>>, }, } -impl FormDataValue { - fn from_arguments<'js>( - ctx: Ctx<'js>, +impl<'js> FormDataValue<'js> { + fn from_arguments( + ctx: &Ctx<'js>, value: Value<'js>, filename: Opt<Coerced<String<'js>>>, error: &'static str, - ) -> Result<FormDataValue> { + ) -> Result<Self> { if let Some(blob) = - value.as_object().and_then(|value| Class::<BlobClass>::from_object(value.clone()).ok()) + value.as_object().and_then(|value| Class::<Blob>::from_object(value.clone())) { - let blob = Persistent::save(ctx, blob); - let filename = filename.into_inner().map(|x| Persistent::save(ctx, x.0)); + let filename = filename.into_inner().map(|x| x.0); Ok(FormDataValue::Blob { data: blob, @@ -38,128 +40,109 @@ impl FormDataValue { return Err(Exception::throw_type(ctx, error)); } else { let value = Coerced::<String>::from_js(ctx, value)?; - let value = Persistent::save(ctx, value.0); - Ok(FormDataValue::String(value)) + Ok(FormDataValue::String(value.0)) } } } -pub use form_data::FormData as FormDataClass; -#[bind(object, public)] -#[quickjs(bare)] -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(clippy::module_inception)] -pub mod form_data { - use super::*; - use std::{cell::RefCell, collections::HashMap}; +#[js::class] +#[derive(Clone, Trace)] +pub struct FormData<'js> { + #[qjs(skip_trace)] + pub(crate) values: HashMap<StdString, Vec<FormDataValue<'js>>>, +} - use js::{ - function::Opt, - prelude::{Coerced, Rest}, - Ctx, Result, String, Value, - }; - use reqwest::multipart::{Form, Part}; +#[js::methods] +impl<'js> FormData<'js> { + // ------------------------------ + // Constructor + // ------------------------------ - #[derive(Clone)] - #[quickjs(cloneable)] - pub struct FormData { - pub(crate) values: RefCell<HashMap<StdString, Vec<FormDataValue>>>, + // FormData spec states that FormDa takes two html elements as arguments + // which does not make sense implementing fetch outside a browser. + // So we ignore those arguments. + #[qjs(constructor)] + pub fn new(ctx: Ctx<'js>, args: Rest<()>) -> Result<Self> { + if args.len() > 0 { + return Err(Exception::throw_internal( + &ctx, + "Cant call FormData with arguments as the dom elements required are not available", + )); + } + Ok(FormData { + values: HashMap::new(), + }) } - impl FormData { - // ------------------------------ - // Constructor - // ------------------------------ + pub fn append( + &mut self, + ctx: Ctx<'js>, + name: Coerced<StdString>, + value: Value<'js>, + filename: Opt<Coerced<String<'js>>>, + ) -> Result<()> { + let value = FormDataValue::from_arguments( + &ctx, + value, + filename, + "Can't call `append` on `FormData` with a filename when value isn't of type `Blob`", + )?; - // FormData spec states that FormDa takes two html elements as arguments - // which does not make sense implementing fetch outside a browser. - // So we ignore those arguments. - #[quickjs(constructor)] - pub fn new(ctx: Ctx<'_>, args: Rest<()>) -> Result<FormData> { - if args.len() > 0 { - return Err(Exception::throw_internal(ctx,"Cant call FormData with arguments as the dom elements required are not available")); - } - Ok(FormData { - values: RefCell::new(HashMap::new()), - }) - } + self.values.entry(name.0).or_insert_with(Vec::new).push(value); - pub fn append<'js>( - &self, - ctx: Ctx<'js>, - name: Coerced<StdString>, - value: Value<'js>, - filename: Opt<Coerced<String<'js>>>, - ) -> Result<()> { - let value = FormDataValue::from_arguments( - ctx, - value, - filename, - "Can't call `append` on `FormData` with a filename when value isn't of type `Blob`", - )?; + Ok(()) + } - self.values.borrow_mut().entry(name.0).or_insert_with(Vec::new).push(value); + pub fn set( + &mut self, + ctx: Ctx<'js>, + name: Coerced<StdString>, + value: Value<'js>, + filename: Opt<Coerced<String<'js>>>, + ) -> Result<()> { + let value = FormDataValue::from_arguments( + &ctx, + value, + filename, + "Can't call `set` on `FormData` with a filename when value isn't of type `Blob`", + )?; - Ok(()) - } + self.values.insert(name.0, vec![value]); - pub fn set<'js>( - &self, - ctx: Ctx<'js>, - name: Coerced<StdString>, - value: Value<'js>, - filename: Opt<Coerced<String<'js>>>, - ) -> Result<()> { - let value = FormDataValue::from_arguments( - ctx, - value, - filename, - "Can't call `set` on `FormData` with a filename when value isn't of type `Blob`", - )?; + Ok(()) + } - self.values.borrow_mut().insert(name.0, vec![value]); + pub fn has(&self, name: Coerced<StdString>) -> bool { + self.values.contains_key(&name.0) + } - Ok(()) - } + pub fn delete(&mut self, name: Coerced<StdString>) { + self.values.remove(&name.0); + } - pub fn has(&self, ctx: Ctx<'_>, name: Coerced<StdString>) -> bool { - self.values.borrow().contains_key(&name.0) - } - - pub fn delete(&self, ctx: Ctx<'_>, name: Coerced<StdString>) { - self.values.borrow_mut().remove(&name.0); - } - - #[quickjs(skip)] - pub fn to_form(&self, ctx: Ctx<'_>) -> Result<Form> { - let lock = self.values.borrow(); - let mut res = Form::new(); - for (k, v) in lock.iter() { - for v in v { - match v { - FormDataValue::String(x) => { - let x = x.clone().restore(ctx).unwrap(); - res = res.text(k.clone(), x.to_string()?); - } - FormDataValue::Blob { - data, - filename, - } => { - let mut part = Part::bytes( - data.clone().restore(ctx).unwrap().borrow().data.to_vec(), - ); - if let Some(filename) = filename { - let filename = - filename.clone().restore(ctx).unwrap().to_string()?; - part = part.file_name(filename); - } - res = res.part(k.clone(), part); + #[qjs(skip)] + pub fn to_form(&self) -> Result<Form> { + let mut res = Form::new(); + for (k, v) in self.values.iter() { + for v in v { + match v { + FormDataValue::String(x) => { + res = res.text(k.clone(), x.to_string()?); + } + FormDataValue::Blob { + data, + filename, + } => { + let mut part = Part::bytes(data.borrow().data.to_vec()); + if let Some(filename) = filename { + let filename = filename.to_string()?; + part = part.file_name(filename); } + res = res.part(k.clone(), part); } } } - Ok(res) } + Ok(res) } } diff --git a/lib/src/fnc/script/fetch/classes/headers.rs b/lib/src/fnc/script/fetch/classes/headers.rs index 01e06053..ecb015d0 100644 --- a/lib/src/fnc/script/fetch/classes/headers.rs +++ b/lib/src/fnc/script/fetch/classes/headers.rs @@ -1,272 +1,258 @@ //! Headers class implementation -use js::bind; +use std::str::FromStr; -pub use headers::Headers as HeadersClass; +use js::{ + class::Trace, + prelude::{Coerced, List}, + Array, Ctx, Exception, Result, Value, +}; +use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; -#[bind(object, public)] -#[quickjs(bare)] -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(clippy::module_inception)] -mod headers { - use std::{cell::RefCell, str::FromStr}; +#[derive(Clone, Trace)] +#[js::class] +pub struct Headers { + #[qjs(skip_trace)] + pub(crate) inner: HeaderMap, +} - use js::{function::Rest, prelude::Coerced, Array, Ctx, Exception, Result, Value}; - use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; +#[js::methods] +impl Headers { + // ------------------------------ + // Constructor + // ------------------------------ - #[derive(Clone)] - #[quickjs(cloneable)] - #[allow(dead_code)] - pub struct Headers { - pub(crate) inner: RefCell<HeaderMap>, + #[qjs(constructor)] + pub fn new<'js>(ctx: Ctx<'js>, init: Value<'js>) -> Result<Self> { + Headers::new_inner(&ctx, init) } - impl Headers { - // ------------------------------ - // Constructor - // ------------------------------ + // ------------------------------ + // Instance methods + // ------------------------------ - #[quickjs(constructor)] - pub fn new<'js>(ctx: Ctx<'js>, init: Value<'js>, args: Rest<()>) -> Result<Self> { - Headers::new_inner(ctx, init) - } - - // ------------------------------ - // Instance methods - // ------------------------------ - - // Convert the object to a string - pub fn toString(&self, args: Rest<()>) -> String { - String::from("[object Header]") - } - - // Adds or appends a new value to a header - pub fn append(&self, ctx: Ctx<'_>, key: String, val: String, args: Rest<()>) -> Result<()> { - self.append_inner(ctx, &key, &val) - } - - // Deletes a header from the header set - pub fn delete(&self, ctx: Ctx<'_>, key: String, args: Rest<()>) -> Result<()> { - // Process and check the header name is valid - let key = HeaderName::from_str(&key) - .map_err(|e| Exception::throw_type(ctx, &format!("{e}")))?; - // Remove the header entry from the map - self.inner.borrow_mut().remove(&key); - // Everything ok - Ok(()) - } - - // Returns all header entries in the header set - pub fn entries(&self, args: Rest<()>) -> Vec<(String, String)> { - let lock = self.inner.borrow(); - let mut res = Vec::<(String, String)>::with_capacity(lock.len()); - - for (k, v) in lock.iter() { - let k = k.as_str(); - if Some(k) == res.last().map(|x| x.0.as_str()) { - let ent = res.last_mut().unwrap(); - ent.1.push_str(", "); - // Header value came from a string, so it should also be able to be cast back - // to a string - ent.1.push_str(v.to_str().unwrap()); - } else { - res.push((k.to_owned(), v.to_str().unwrap().to_owned())); - } - } - - res - } - - // Returns all values of a header in the header set - pub fn get(&self, ctx: Ctx<'_>, key: String, args: Rest<()>) -> Result<Option<String>> { - // Process and check the header name is valid - let key = HeaderName::from_str(&key) - .map_err(|e| Exception::throw_type(ctx, &format!("{e}")))?; - // Convert the header values to strings - let lock = self.inner.borrow(); - let all = lock.get_all(&key); - - // Header value came from a string, so it should also be able to be cast back - // to a string - let mut res = String::new(); - for (idx, v) in all.iter().enumerate() { - if idx != 0 { - res.push_str(", "); - } - res.push_str(v.to_str().unwrap()); - } - - if res.is_empty() { - return Ok(None); - } - Ok(Some(res)) - } - - // Returns all values for the `Set-Cookie` header. - #[quickjs(rename = "getSetCookie")] - pub fn get_set_cookie(&self, args: Rest<()>) -> Vec<String> { - // This should always be a correct cookie; - let key = HeaderName::from_str("set-cookie").unwrap(); - self.inner - .borrow() - .get_all(key) - .iter() - .map(|x| x.to_str().unwrap().to_owned()) - .collect() - } - - // Checks to see if the header set contains a header - pub fn has(&self, ctx: Ctx<'_>, key: String, args: Rest<()>) -> Result<bool> { - // Process and check the header name is valid - let key = HeaderName::from_str(&key) - .map_err(|e| Exception::throw_type(ctx, &format!("{e}")))?; - // Check if the header entry exists - Ok(self.inner.borrow().contains_key(&key)) - } - - // Returns all header keys contained in the header set - pub fn keys(&self, args: Rest<()>) -> Vec<String> { - // TODO: Incorrect, should return an iterator but iterators are not supported yet by quickjs - self.inner.borrow().keys().map(|v| v.as_str().to_owned()).collect::<Vec<String>>() - } - - // Sets a new value or adds a header to the header set - pub fn set(&self, ctx: Ctx<'_>, key: String, val: String, args: Rest<()>) -> Result<()> { - // Process and check the header name is valid - let key = HeaderName::from_str(&key) - .map_err(|e| Exception::throw_type(ctx, &format!("Invalid header name: {e}")))?; - // Process and check the header name is valid - let val = HeaderValue::from_str(&val) - .map_err(|e| Exception::throw_type(ctx, &format!("Invalid header value: {e}")))?; - // Insert and overwrite the header entry - self.inner.borrow_mut().insert(key, val); - // Everything ok - Ok(()) - } - - // Returns all header values contained in the header set - pub fn values(&self, args: Rest<()>) -> Vec<String> { - let lock = self.inner.borrow(); - let mut res = Vec::<String>::with_capacity(lock.len()); - - let mut pref = None; - for (k, v) in lock.iter() { - if Some(k) == pref { - let ent = res.last_mut().unwrap(); - ent.push_str(", "); - ent.push_str(v.to_str().unwrap()) - } else { - pref = Some(k); - res.push(v.to_str().unwrap().to_owned()); - } - } - - res - } + // Convert the object to a string + #[qjs(rename = "toString")] + pub fn js_to_string(&self) -> String { + String::from("[object Header]") } - #[quickjs(skip)] - impl Headers { - pub fn from_map(map: HeaderMap) -> Self { - Self { - inner: RefCell::new(map), - } - } + // Adds or appends a new value to a header + pub fn append(&mut self, ctx: Ctx<'_>, key: String, val: String) -> Result<()> { + self.append_inner(&ctx, &key, &val) + } - pub fn new_empty() -> Self { - Self::from_map(HeaderMap::new()) - } + // Deletes a header from the header set + pub fn delete(&mut self, ctx: Ctx<'_>, key: String) -> Result<()> { + // Process and check the header name is valid + let key = + HeaderName::from_str(&key).map_err(|e| Exception::throw_type(&ctx, &format!("{e}")))?; + // Remove the header entry from the map + self.inner.remove(&key); + // Everything ok + Ok(()) + } - pub fn new_inner<'js>(ctx: Ctx<'js>, val: Value<'js>) -> Result<Self> { - static INVALID_ERROR: &str = "Headers constructor: init was neither sequence<sequence<ByteString>> or record<ByteString, ByteString>"; - let res = Self::new_empty(); + // Returns all header entries in the header set + pub fn entries(&self) -> Vec<List<(String, String)>> { + let mut res = Vec::<List<(String, String)>>::with_capacity(self.inner.len()); - // TODO Set and Map, - if let Some(array) = val.as_array() { - // a sequence<sequence<String>>; - for v in array.iter::<Array>() { - let v = match v { - Ok(x) => x, - Err(e) => { - if e.is_from_js() { - return Err(Exception::throw_type(ctx, INVALID_ERROR)); - } - return Err(e); - } - }; - let key = match v.get::<Coerced<String>>(0) { - Ok(x) => x, - Err(e) => { - if e.is_from_js() { - return Err(Exception::throw_type(ctx, INVALID_ERROR)); - } - return Err(e); - } - }; - let value = match v.get::<Coerced<String>>(1) { - Ok(x) => x, - Err(e) => { - if e.is_from_js() { - return Err(Exception::throw_type(ctx, INVALID_ERROR)); - } - return Err(e); - } - }; - res.append_inner(ctx, &key, &value)?; - } - } else if let Some(obj) = val.as_object() { - // a record<String,String>; - for prop in obj.props::<String, Coerced<String>>() { - let (key, value) = match prop { - Ok(x) => x, - Err(e) => { - if e.is_from_js() { - return Err(Exception::throw_type(ctx, INVALID_ERROR)); - } - return Err(e); - } - }; - res.append_inner(ctx, &key, &value.0)?; - } + for (k, v) in self.inner.iter() { + let k = k.as_str(); + if Some(k) == res.last().map(|x| x.0 .0.as_str()) { + let ent = res.last_mut().unwrap(); + ent.0 .1.push_str(", "); + // Header value came from a string, so it should also be able to be cast back + // to a string + ent.0 .1.push_str(v.to_str().unwrap()); } else { - return Err(Exception::throw_type(ctx, INVALID_ERROR)); + res.push(List((k.to_owned(), v.to_str().unwrap().to_owned()))); } - - Ok(res) } - fn append_inner(&self, ctx: Ctx<'_>, key: &str, val: &str) -> Result<()> { - // Unsure what to do exactly here. - // Spec dictates normalizing string before adding it as a header value, i.e. removing - // any leading and trailing whitespace: - // [`https://fetch.spec.whatwg.org/#concept-header-value-normalize`] - // But non of the platforms I tested, normalize, instead they throw an error - // with `Invalid header value`. I'll chose to just do what the platforms do. + res + } - let key = match HeaderName::from_bytes(key.as_bytes()) { - Ok(x) => x, - Err(e) => { - return Err(Exception::throw_type( - ctx, - &format!("invalid header name `{key}`: {e}"), - )) - } - }; - let val = match HeaderValue::from_bytes(val.as_bytes()) { - Ok(x) => x, - Err(e) => { - return Err(Exception::throw_type( - ctx, - &format!("invalid header value `{val}`: {e}"), - )) - } - }; + // Returns all values of a header in the header set + pub fn get(&self, ctx: Ctx<'_>, key: String) -> Result<Option<String>> { + // Process and check the header name is valid + let key = + HeaderName::from_str(&key).map_err(|e| Exception::throw_type(&ctx, &format!("{e}")))?; + // Convert the header values to strings + let all = self.inner.get_all(&key); - self.inner.borrow_mut().append(key, val); - - Ok(()) + // Header value came from a string, so it should also be able to be cast back + // to a string + let mut res = String::new(); + for (idx, v) in all.iter().enumerate() { + if idx != 0 { + res.push_str(", "); + } + res.push_str(v.to_str().unwrap()); } + + if res.is_empty() { + return Ok(None); + } + Ok(Some(res)) + } + + // Returns all values for the `Set-Cookie` header. + #[qjs(rename = "getSetCookie")] + pub fn get_set_cookie(&self) -> Vec<String> { + // This should always be a correct cookie; + let key = HeaderName::from_str("set-cookie").unwrap(); + self.inner.get_all(key).iter().map(|x| x.to_str().unwrap().to_owned()).collect() + } + + // Checks to see if the header set contains a header + pub fn has(&self, ctx: Ctx<'_>, key: String) -> Result<bool> { + // Process and check the header name is valid + let key = + HeaderName::from_str(&key).map_err(|e| Exception::throw_type(&ctx, &format!("{e}")))?; + // Check if the header entry exists + Ok(self.inner.contains_key(&key)) + } + + // Returns all header keys contained in the header set + pub fn keys(&self) -> Vec<String> { + // TODO: Incorrect, should return an iterator but iterators are not supported yet by quickjs + self.inner.keys().map(|v| v.as_str().to_owned()).collect::<Vec<String>>() + } + + // Sets a new value or adds a header to the header set + pub fn set(&mut self, ctx: Ctx<'_>, key: String, val: String) -> Result<()> { + // Process and check the header name is valid + let key = HeaderName::from_str(&key) + .map_err(|e| Exception::throw_type(&ctx, &format!("Invalid header name: {e}")))?; + // Process and check the header name is valid + let val = HeaderValue::from_str(&val) + .map_err(|e| Exception::throw_type(&ctx, &format!("Invalid header value: {e}")))?; + // Insert and overwrite the header entry + self.inner.insert(key, val); + // Everything ok + Ok(()) + } + + // Returns all header values contained in the header set + pub fn values(&self) -> Vec<String> { + let mut res = Vec::<String>::with_capacity(self.inner.len()); + + let mut pref = None; + for (k, v) in self.inner.iter() { + if Some(k) == pref { + let ent = res.last_mut().unwrap(); + ent.push_str(", "); + ent.push_str(v.to_str().unwrap()) + } else { + pref = Some(k); + res.push(v.to_str().unwrap().to_owned()); + } + } + + res + } +} + +impl Headers { + pub fn from_map(map: HeaderMap) -> Self { + Self { + inner: map, + } + } + + pub fn new_empty() -> Self { + Self::from_map(HeaderMap::new()) + } + + pub fn new_inner<'js>(ctx: &Ctx<'js>, val: Value<'js>) -> Result<Self> { + static INVALID_ERROR: &str = "Headers constructor: init was neither sequence<sequence<ByteString>> or record<ByteString, ByteString>"; + let mut res = Self::new_empty(); + + // TODO Set and Map, + if let Some(array) = val.as_array() { + // a sequence<sequence<String>>; + for v in array.iter::<Array>() { + let v = match v { + Ok(x) => x, + Err(e) => { + if e.is_from_js() { + return Err(Exception::throw_type(ctx, INVALID_ERROR)); + } + return Err(e); + } + }; + let key = match v.get::<Coerced<String>>(0) { + Ok(x) => x, + Err(e) => { + if e.is_from_js() { + return Err(Exception::throw_type(ctx, INVALID_ERROR)); + } + return Err(e); + } + }; + let value = match v.get::<Coerced<String>>(1) { + Ok(x) => x, + Err(e) => { + if e.is_from_js() { + return Err(Exception::throw_type(ctx, INVALID_ERROR)); + } + return Err(e); + } + }; + res.append_inner(ctx, &key, &value)?; + } + } else if let Some(obj) = val.as_object() { + // a record<String,String>; + for prop in obj.props::<String, Coerced<String>>() { + let (key, value) = match prop { + Ok(x) => x, + Err(e) => { + if e.is_from_js() { + return Err(Exception::throw_type(ctx, INVALID_ERROR)); + } + return Err(e); + } + }; + res.append_inner(ctx, &key, &value.0)?; + } + } else { + return Err(Exception::throw_type(ctx, INVALID_ERROR)); + } + + Ok(res) + } + + fn append_inner(&mut self, ctx: &Ctx<'_>, key: &str, val: &str) -> Result<()> { + // Unsure what to do exactly here. + // Spec dictates normalizing string before adding it as a header value, i.e. removing + // any leading and trailing whitespace: + // [`https://fetch.spec.whatwg.org/#concept-header-value-normalize`] + // But non of the platforms I tested, normalize, instead they throw an error + // with `Invalid header value`. I'll chose to just do what the platforms do. + + let key = match HeaderName::from_bytes(key.as_bytes()) { + Ok(x) => x, + Err(e) => { + return Err(Exception::throw_type( + ctx, + &format!("invalid header name `{key}`: {e}"), + )) + } + }; + let val = match HeaderValue::from_bytes(val.as_bytes()) { + Ok(x) => x, + Err(e) => { + return Err(Exception::throw_type( + ctx, + &format!("invalid header value `{val}`: {e}"), + )) + } + }; + + self.inner.append(key, val); + + Ok(()) } } @@ -327,7 +313,7 @@ mod test { }); assert.seq(headers.get("f"), "g"); assert.seq(headers.get("h"), "j"); - "#).catch(ctx).unwrap(); + "#).catch(&ctx).unwrap(); }) .await } diff --git a/lib/src/fnc/script/fetch/classes/request.rs b/lib/src/fnc/script/fetch/classes/request.rs index f57c982e..aae64746 100644 --- a/lib/src/fnc/script/fetch/classes/request.rs +++ b/lib/src/fnc/script/fetch/classes/request.rs @@ -1,18 +1,9 @@ //! Request class implementation -//! -use js::{ - bind, - class::{HasRefs, RefsMarker}, - prelude::Coerced, - Class, Ctx, Exception, FromJs, Object, Persistent, Result, Value, -}; + +use js::{class::Trace, prelude::Coerced, Class, Ctx, Exception, FromJs, Object, Result, Value}; use reqwest::Method; -use crate::fnc::script::fetch::{ - body::Body, - classes::{BlobClass, HeadersClass}, - RequestError, -}; +use crate::fnc::script::fetch::{body::Body, RequestError}; #[derive(Clone, Copy, Eq, PartialEq)] pub enum RequestMode { @@ -23,7 +14,7 @@ pub enum RequestMode { } impl<'js> FromJs<'js> for RequestMode { - fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> { + fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? { match x.as_str() { "navigate" => RequestMode::Navigate, @@ -59,7 +50,7 @@ pub enum RequestCredentials { } impl<'js> FromJs<'js> for RequestCredentials { - fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> { + fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? { match x.as_str() { "omit" => RequestCredentials::Omit, @@ -96,7 +87,7 @@ pub enum RequestCache { } impl<'js> FromJs<'js> for RequestCache { - fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> { + fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? { match x.as_str() { "default" => RequestCache::Default, @@ -136,7 +127,7 @@ pub enum RequestRedirect { } impl<'js> FromJs<'js> for RequestRedirect { - fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> { + fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? { match x.as_str() { "follow" => RequestRedirect::Follow, @@ -176,7 +167,7 @@ pub enum ReferrerPolicy { } impl<'js> FromJs<'js> for ReferrerPolicy { - fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> { + fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? { match x.as_str() { "" => ReferrerPolicy::Empty, @@ -213,9 +204,9 @@ impl<'js> FromJs<'js> for ReferrerPolicy { } } -pub struct RequestInit { +pub struct RequestInit<'js> { pub method: Method, - pub headers: Persistent<Class<'static, HeadersClass>>, + pub headers: Class<'js, Headers>, pub body: Option<Body>, pub referrer: String, pub referrer_policy: ReferrerPolicy, @@ -227,15 +218,15 @@ pub struct RequestInit { pub keep_alive: bool, } -impl HasRefs for RequestInit { - fn mark_refs(&self, marker: &RefsMarker) { - self.headers.mark_refs(marker); +impl<'js> Trace<'js> for RequestInit<'js> { + fn trace<'a>(&self, tracer: js::class::Tracer<'a, 'js>) { + self.headers.trace(tracer); } } -impl RequestInit { - pub fn default(ctx: Ctx<'_>) -> Result<Self> { - let headers = Persistent::save(ctx, Class::instance(ctx, HeadersClass::new_empty())?); +impl<'js> RequestInit<'js> { + pub fn default(ctx: Ctx<'js>) -> Result<Self> { + let headers = Class::instance(ctx, Headers::new_empty())?; Ok(RequestInit { method: Method::GET, headers, @@ -251,9 +242,9 @@ impl RequestInit { }) } - pub fn clone_js(&self, ctx: Ctx<'_>) -> Result<Self> { - let headers = self.headers.clone().restore(ctx).unwrap(); - let headers = Persistent::save(ctx, Class::instance(ctx, headers.borrow().clone())?); + pub fn clone_js(&self, ctx: Ctx<'js>) -> Result<Self> { + let headers = self.headers.clone(); + let headers = Class::instance(ctx.clone(), headers.borrow().clone())?; let body = self.body.as_ref().map(|x| x.clone_js(ctx)); @@ -274,7 +265,7 @@ impl RequestInit { } // Normalize method string according to spec. -fn normalize_method(ctx: Ctx<'_>, m: String) -> Result<Method> { +fn normalize_method(ctx: &Ctx<'_>, m: String) -> Result<Method> { if m.as_bytes().eq_ignore_ascii_case(b"CONNECT") || m.as_bytes().eq_ignore_ascii_case(b"TRACE") || m.as_bytes().eq_ignore_ascii_case(b"TRACK") @@ -309,8 +300,8 @@ fn normalize_method(ctx: Ctx<'_>, m: String) -> Result<Method> { } } -impl<'js> FromJs<'js> for RequestInit { - fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> { +impl<'js> FromJs<'js> for RequestInit<'js> { + fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { let object = Object::from_js(ctx, value)?; let referrer = object @@ -346,15 +337,14 @@ impl<'js> FromJs<'js> for RequestInit { } let headers = if let Some(hdrs) = object.get::<_, Option<Object>>("headers")? { - if let Ok(cls) = Class::<HeadersClass>::from_object(hdrs.clone()) { + if let Some(cls) = Class::<Headers>::from_object(hdrs.clone()) { cls } else { - Class::instance(ctx, HeadersClass::new_inner(ctx, hdrs.into_value())?)? + Class::instance(ctx.clone(), Headers::new_inner(ctx, hdrs.into_value())?)? } } else { - Class::instance(ctx, HeadersClass::new_empty())? + Class::instance(ctx.clone(), Headers::new_empty())? }; - let headers = Persistent::save(ctx, headers); let body = object.get::<_, Option<Body>>("body")?; @@ -376,192 +366,173 @@ impl<'js> FromJs<'js> for RequestInit { pub use request::Request as RequestClass; -#[bind(object, public)] -#[quickjs(bare)] -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(clippy::module_inception)] -mod request { +pub use super::*; - pub use super::*; +use bytes::Bytes; +use js::function::Opt; +// TODO: change implementation based on features. +use reqwest::{header::HeaderName, Url}; - use bytes::Bytes; - use js::{ - function::{Opt, Rest}, - Class, Ctx, Exception, HasRefs, Result, Value, - }; - // TODO: change implementation based on features. - use reqwest::{header::HeaderName, Url}; +#[allow(dead_code)] +#[js::class] +#[derive(Trace)] +pub struct Request<'js> { + #[qjs(skip_trace)] + pub(crate) url: Url, + pub(crate) init: RequestInit<'js>, +} - #[allow(dead_code)] - #[derive(HasRefs)] - #[quickjs(has_refs)] - pub struct Request { - pub(crate) url: Url, - #[quickjs(has_refs)] - pub(crate) init: RequestInit, +#[js::methods] +impl<'js> Request<'js> { + // ------------------------------ + // Constructor + // ------------------------------ + + #[qjs(constructor)] + pub fn new(ctx: Ctx<'js>, input: Value<'js>, init: Opt<RequestInit<'js>>) -> Result<Self> { + if let Some(url) = input.as_string() { + // url string + let url_str = url.to_string()?; + let url = Url::parse(&url_str) + .map_err(|e| Exception::throw_type(&ctx, &format!("failed to parse url: {e}")))?; + if !url.username().is_empty() || !url.password().map(str::is_empty).unwrap_or(true) { + // url cannot contain non empty username and passwords + return Err(Exception::throw_type(&ctx, "Url contained credentials.")); + } + let init = init.into_inner().map_or_else(|| RequestInit::default(ctx.clone()), Ok)?; + // HEAD and GET methods can't have a body + if init.body.is_some() && init.method == Method::GET || init.method == Method::HEAD { + return Err(Exception::throw_type( + &ctx, + &format!("Request with method `{}` cannot have a body", init.method), + )); + } + + Ok(Self { + url, + init, + }) + } else if let Some(request) = input.into_object().and_then(Class::<Self>::from_object) { + // existing request object, just return it + request.try_borrow()?.clone_js(ctx.clone()) + } else { + Err(Exception::throw_type( + &ctx, + "request `init` paramater must either be a request object or a string", + )) + } } - impl Request { - // ------------------------------ - // Constructor - // ------------------------------ + /// Clone the response, teeing any possible underlying streams. + #[qjs(rename = "clone")] + pub fn clone_js(&self, ctx: Ctx<'js>) -> Result<Self> { + Ok(Self { + url: self.url.clone(), + init: self.init.clone_js(ctx)?, + }) + } - #[quickjs(constructor)] - pub fn new<'js>( - ctx: Ctx<'js>, - input: Value<'js>, - init: Opt<RequestInit>, - args: Rest<()>, - ) -> Result<Self> { - if let Some(url) = input.as_string() { - // url string - let url_str = url.to_string()?; - let url = Url::parse(&url_str).map_err(|e| { - Exception::throw_type(ctx, &format!("failed to parse url: {e}")) - })?; - if !url.username().is_empty() || !url.password().map(str::is_empty).unwrap_or(true) - { - // url cannot contain non empty username and passwords - return Err(Exception::throw_type(ctx, "Url contained credentials.")); + // ------------------------------ + // Instance properties + // ------------------------------ + #[qjs(get, rename = "body_used")] + pub fn body_used(&self) -> bool { + self.init.body.as_ref().map(Body::used).unwrap_or(true) + } + + #[qjs(get)] + pub fn method(&self) -> String { + self.init.method.to_string() + } + + #[qjs(get)] + pub fn url(&self) -> String { + self.url.to_string() + } + + #[qjs(get)] + pub fn headers(&self) -> Class<'js, Headers> { + self.init.headers.clone() + } + + #[qjs(get)] + pub fn referrer(&self) -> String { + self.init.referrer.clone() + } + // TODO + + // ------------------------------ + // Instance methods + // ------------------------------ + + // Convert the object to a string + #[qjs(rename = "toString")] + pub fn js_to_string(&self) -> String { + String::from("[object Request]") + } + + /// Takes the buffer from the body leaving it used. + #[qjs(skip)] + async fn take_buffer(&self, ctx: &Ctx<'js>) -> Result<Bytes> { + let Some(body) = self.init.body.as_ref() else { + return Ok(Bytes::new()); + }; + match body.to_buffer().await { + Ok(Some(x)) => Ok(x), + Ok(None) => Err(Exception::throw_type(ctx, "Body unusable")), + Err(e) => match e { + RequestError::Reqwest(e) => { + Err(Exception::throw_type(ctx, &format!("stream failed: {e}"))) } - let init = init.into_inner().map_or_else(|| RequestInit::default(ctx), Ok)?; - // HEAD and GET methods can't have a body - if init.body.is_some() && init.method == Method::GET || init.method == Method::HEAD - { - return Err(Exception::throw_type( - ctx, - &format!("Request with method `{}` cannot have a body", init.method), - )); - } - - Ok(Self { - url, - init, - }) - } else if let Some(request) = input - .into_object() - .and_then(|obj| Class::<Self>::from_object(obj).ok().map(|x| x.borrow())) - { - // existing request object, just return it - request.clone_js(ctx, Default::default()) - } else { - Err(Exception::throw_type( - ctx, - "request `init` paramater must either be a request object or a string", - )) - } + }, } + } - /// Clone the response, teeing any possible underlying streams. - #[quickjs(rename = "clone")] - pub fn clone_js(&self, ctx: Ctx<'_>, _rest: Rest<()>) -> Result<Self> { - Ok(Self { - url: self.url.clone(), - init: self.init.clone_js(ctx)?, - }) - } + // Returns a promise with the request body as a Blob + pub async fn blob(&self, ctx: Ctx<'js>) -> Result<Blob> { + let headers = self.init.headers.clone(); + let mime = { + let headers = headers.borrow(); + let headers = &headers.inner; + let key = HeaderName::from_static("content-type"); + let types = headers.get_all(key); + // TODO: This is not according to spec. + types + .iter() + .next() + .map(|x| x.to_str().unwrap_or("text/html")) + .unwrap_or("text/html") + .to_owned() + }; - // ------------------------------ - // Instance properties - // ------------------------------ - #[quickjs(get)] - pub fn bodyUsed(&self) -> bool { - self.init.body.as_ref().map(Body::used).unwrap_or(true) - } + let data = self.take_buffer(&ctx).await?; + Ok(Blob { + mime, + data, + }) + } - #[quickjs(get)] - pub fn method(&self) -> String { - self.init.method.to_string() - } + // Returns a promise with the request body as FormData + #[qjs(rename = "formData")] + pub async fn form_data(&self, ctx: Ctx<'js>) -> Result<Value<'js>> { + Err(Exception::throw_internal(&ctx, "Not yet implemented")) + } - #[quickjs(get)] - pub fn url(&self) -> String { - self.url.to_string() - } + // Returns a promise with the request body as JSON + pub async fn json(&self, ctx: Ctx<'js>) -> Result<Value<'js>> { + let text = self.text(ctx.clone()).await?; + ctx.json_parse(text) + } - #[quickjs(get)] - pub fn headers<'js>(&self, ctx: Ctx<'js>) -> Class<'js, HeadersClass> { - self.init.headers.clone().restore(ctx).unwrap() - } + // Returns a promise with the request body as text + pub async fn text(&self, ctx: Ctx<'js>) -> Result<String> { + let data = self.take_buffer(&ctx).await?; - #[quickjs(get)] - pub fn referrer(&self, ctx: Ctx<'_>) -> String { - self.init.referrer.clone() - } - // TODO - - // ------------------------------ - // Instance methods - // ------------------------------ - - // Convert the object to a string - pub fn toString(&self) -> String { - String::from("[object Request]") - } - - /// Takes the buffer from the body leaving it used. - #[quickjs(skip)] - async fn take_buffer<'js>(&self, ctx: Ctx<'js>) -> Result<Bytes> { - let Some(body) = self.init.body.as_ref() else { - return Ok(Bytes::new()) - }; - match body.to_buffer().await { - Ok(Some(x)) => Ok(x), - Ok(None) => Err(Exception::throw_type(ctx, "Body unusable")), - Err(e) => match e { - RequestError::Reqwest(e) => { - Err(Exception::throw_type(ctx, &format!("stream failed: {e}"))) - } - }, - } - } - - // Returns a promise with the request body as a Blob - pub async fn blob(&self, ctx: Ctx<'_>, args: Rest<()>) -> Result<BlobClass> { - let headers = self.init.headers.clone().restore(ctx).unwrap(); - let mime = { - let headers = headers.borrow(); - let headers = headers.inner.borrow(); - let key = HeaderName::from_static("content-type"); - let types = headers.get_all(key); - // TODO: This is not according to spec. - types - .iter() - .next() - .map(|x| x.to_str().unwrap_or("text/html")) - .unwrap_or("text/html") - .to_owned() - }; - - let data = self.take_buffer(ctx).await?; - Ok(BlobClass { - mime, - data, - }) - } - - // Returns a promise with the request body as FormData - pub async fn formData<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<Value<'js>> { - Err(Exception::throw_internal(ctx, "Not yet implemented")) - } - - // Returns a promise with the request body as JSON - pub async fn json<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<Value<'js>> { - let text = self.text(ctx, args).await?; - ctx.json_parse(text) - } - - // Returns a promise with the request body as text - pub async fn text<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<String> { - let data = self.take_buffer(ctx).await?; - - // Skip UTF-BOM - if data.starts_with(&[0xEF, 0xBB, 0xBF]) { - Ok(String::from_utf8_lossy(&data[3..]).into_owned()) - } else { - Ok(String::from_utf8_lossy(&data).into_owned()) - } + // Skip UTF-BOM + if data.starts_with(&[0xEF, 0xBB, 0xBF]) { + Ok(String::from_utf8_lossy(&data[3..]).into_owned()) + } else { + Ok(String::from_utf8_lossy(&data).into_owned()) } } } @@ -644,7 +615,7 @@ mod test { assert.seq(await req_2.text(),"some text"); })() - "#).catch(ctx).unwrap().await.catch(ctx).unwrap(); + "#).catch(&ctx).unwrap().await.catch(&ctx).unwrap(); }) .await; } diff --git a/lib/src/fnc/script/fetch/classes/response/init.rs b/lib/src/fnc/script/fetch/classes/response/init.rs index 53cbcb78..04c25394 100644 --- a/lib/src/fnc/script/fetch/classes/response/init.rs +++ b/lib/src/fnc/script/fetch/classes/response/init.rs @@ -1,34 +1,33 @@ use std::string::String as StdString; use js::{ - class::{HasRefs, RefsMarker}, + class::{Trace, Tracer}, prelude::*, - Class, Ctx, Exception, FromJs, Object, Persistent, Result, Value, + Class, Ctx, Exception, FromJs, Object, Result, Value, }; -use crate::fnc::script::fetch::{classes::HeadersClass, util}; +use crate::fnc::script::fetch::{classes::Headers, util}; /// Struct containing data from the init argument from the Response constructor. #[derive(Clone)] -pub struct ResponseInit { +pub struct ResponseInit<'js> { // u16 instead of reqwest::StatusCode since javascript allows non valid status codes in some // circumstances. pub status: u16, pub status_text: StdString, - pub headers: Persistent<Class<'static, HeadersClass>>, + pub headers: Class<'js, Headers>, } -impl HasRefs for ResponseInit { - fn mark_refs(&self, marker: &RefsMarker) { - self.headers.mark_refs(marker); +impl<'js> Trace<'js> for ResponseInit<'js> { + fn trace<'a>(&self, tracer: Tracer<'a, 'js>) { + self.headers.trace(tracer); } } -impl ResponseInit { +impl<'js> ResponseInit<'js> { /// Returns a ResponseInit object with all values as the default value. - pub fn default(ctx: Ctx<'_>) -> Result<ResponseInit> { - let headers = Class::instance(ctx, HeadersClass::new_empty())?; - let headers = Persistent::save(ctx, headers); + pub fn default(ctx: Ctx<'js>) -> Result<Self> { + let headers = Class::instance(ctx, Headers::new_empty())?; Ok(ResponseInit { status: 200, status_text: StdString::new(), @@ -37,8 +36,8 @@ impl ResponseInit { } } -impl<'js> FromJs<'js> for ResponseInit { - fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> { +impl<'js> FromJs<'js> for ResponseInit<'js> { + fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { let object = Object::from_js(ctx, value)?; // Extract status. @@ -66,12 +65,11 @@ impl<'js> FromJs<'js> for ResponseInit { // Extract headers. let headers = if let Some(headers) = object.get::<_, Option<Value>>("headers")? { - let headers = HeadersClass::new_inner(ctx, headers)?; - Class::instance(ctx, headers)? + let headers = Headers::new_inner(ctx, headers)?; + Class::instance(ctx.clone(), headers)? } else { - Class::instance(ctx, HeadersClass::new_empty())? + Class::instance(ctx.clone(), Headers::new_empty())? }; - let headers = Persistent::save(ctx, headers); Ok(ResponseInit { status, diff --git a/lib/src/fnc/script/fetch/classes/response/mod.rs b/lib/src/fnc/script/fetch/classes/response/mod.rs index 87a0223b..4791900c 100644 --- a/lib/src/fnc/script/fetch/classes/response/mod.rs +++ b/lib/src/fnc/script/fetch/classes/response/mod.rs @@ -1,11 +1,10 @@ //! Response class implementation -use js::bind; - mod init; +use bytes::Bytes; pub use init::ResponseInit; -pub use response::Response as ResponseClass; +use js::{class::Trace, prelude::Opt, ArrayBuffer, Class, Ctx, Exception, Result, Value}; #[allow(dead_code)] #[derive(Clone, Copy)] @@ -18,304 +17,284 @@ pub enum ResponseType { OpaqueRedirect, } -#[bind(object, public)] -#[quickjs(bare)] -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(clippy::module_inception)] -pub mod response { +use reqwest::Url; - use crate::fnc::script::fetch::{ - body::{Body, BodyKind}, - classes::{BlobClass, HeadersClass}, - util, RequestError, - }; +use crate::fnc::script::fetch::{ + body::{Body, BodyKind}, + util, RequestError, +}; - use super::{ResponseInit, ResponseType}; - use bytes::Bytes; - use js::{ - function::{Opt, Rest}, - ArrayBuffer, Class, Ctx, Exception, HasRefs, Persistent, Result, Value, - }; - use reqwest::Url; +use super::{Blob, Headers}; - #[derive(HasRefs)] - #[allow(dead_code)] - #[quickjs(has_refs)] - pub struct Response { - #[quickjs(has_refs)] - pub(crate) body: Body, - #[quickjs(has_refs)] - pub(crate) init: ResponseInit, - pub(crate) url: Option<Url>, - pub(crate) r#type: ResponseType, - pub(crate) was_redirected: bool, +#[allow(dead_code)] +#[derive(Trace)] +#[js::class] +pub struct Response<'js> { + #[qjs(skip_trace)] + pub(crate) body: Body, + pub(crate) init: ResponseInit<'js>, + #[qjs(skip_trace)] + pub(crate) url: Option<Url>, + #[qjs(skip_trace)] + pub(crate) r#type: ResponseType, + pub(crate) was_redirected: bool, +} + +#[js::methods] +impl<'js> Response<'js> { + // ------------------------------ + // Constructor + // ------------------------------ + + #[qjs(constructor)] + pub fn new( + ctx: Ctx<'js>, + body: Opt<Option<Body>>, + init: Opt<ResponseInit<'js>>, + ) -> Result<Self> { + let init = match init.into_inner() { + Some(x) => x, + None => ResponseInit::default(ctx.clone())?, + }; + let body = body.into_inner().and_then(|x| x); + if body.is_some() && util::is_null_body_status(init.status) { + // Null body statuses are not allowed to have a body. + return Err(Exception::throw_type( + &ctx, + &format!("Response with status `{}` is not allowed to have a body", init.status), + )); + } + let body = body.unwrap_or_default(); + + Ok(Response { + body, + init, + url: None, + r#type: ResponseType::Default, + was_redirected: false, + }) } - impl Response { - // ------------------------------ - // Constructor - // ------------------------------ + // ------------------------------ + // Instance properties + // ------------------------------ - #[quickjs(constructor)] - pub fn new( - ctx: Ctx<'_>, - body: Opt<Option<Body>>, - init: Opt<ResponseInit>, - args: Rest<()>, - ) -> Result<Self> { - let init = match init.into_inner() { - Some(x) => x, - None => ResponseInit::default(ctx)?, - }; - let body = body.into_inner().and_then(|x| x); - if body.is_some() && util::is_null_body_status(init.status) { - // Null body statuses are not allowed to have a body. - return Err(Exception::throw_type( - ctx, - &format!( - "Response with status `{}` is not allowed to have a body", - init.status - ), - )); + #[qjs(get, rename = "bodyUsed")] + pub fn body_used(&self) -> bool { + self.body.used() + } + + #[qjs(get)] + pub fn status(&self) -> u16 { + self.init.status + } + + #[qjs(get)] + pub fn ok(&self) -> bool { + util::is_ok_status(self.init.status) + } + + #[qjs(get)] + pub fn redirected(&self) -> bool { + self.was_redirected + } + + #[qjs(get, rename = "statusText")] + pub fn status_text(&self) -> String { + self.init.status_text.clone() + } + + #[qjs(get, rename = "type")] + pub fn r#type(&self) -> &'static str { + match self.r#type { + ResponseType::Basic => "basic", + ResponseType::Cors => "cors", + ResponseType::Default => "default", + ResponseType::Error => "error", + ResponseType::Opaque => "opaque", + ResponseType::OpaqueRedirect => "opaqueredirect", + } + } + + #[qjs(get)] + pub fn headers(&self) -> Class<'js, Headers> { + self.init.headers.clone() + } + + #[qjs(get)] + pub fn url(&self) -> Option<String> { + self.url.as_ref().map(|x| { + if x.fragment().is_some() { + let mut res = x.clone(); + res.set_fragment(None); + res.to_string() + } else { + x.to_string() } - let body = body.unwrap_or_default(); + }) + } - Ok(Response { - body, - init, - url: None, - r#type: ResponseType::Default, - was_redirected: false, - }) + // ------------------------------ + // Instance methods + // ------------------------------ + + // Convert the object to a string + #[qjs(rename = "toString")] + pub fn js_to_string(&self) -> String { + String::from("[object Response]") + } + + // Creates a copy of the request object + #[qjs(rename = "clone")] + pub fn clone_js(&self, ctx: Ctx<'js>) -> Self { + Response { + body: self.body.clone_js(ctx), + init: self.init.clone(), + url: self.url.clone(), + r#type: self.r#type, + was_redirected: self.was_redirected, } + } - // ------------------------------ - // Instance properties - // ------------------------------ - - #[quickjs(get)] - pub fn bodyUsed(&self) -> bool { - self.body.used() - } - - #[quickjs(get)] - pub fn status(&self) -> u16 { - self.init.status - } - - #[quickjs(get)] - pub fn ok(&self) -> bool { - util::is_ok_status(self.init.status) - } - - #[quickjs(get)] - pub fn redirected(&self) -> bool { - self.was_redirected - } - - #[quickjs(get)] - pub fn statusText(&self) -> String { - self.init.status_text.clone() - } - - #[quickjs(get)] - pub fn r#type(&self) -> &'static str { - match self.r#type { - ResponseType::Basic => "basic", - ResponseType::Cors => "cors", - ResponseType::Default => "default", - ResponseType::Error => "error", - ResponseType::Opaque => "opaque", - ResponseType::OpaqueRedirect => "opaqueredirect", - } - } - - #[quickjs(get)] - pub fn headers<'js>(&self, ctx: Ctx<'js>) -> Class<'js, HeadersClass> { - self.init.headers.clone().restore(ctx).unwrap() - } - - #[quickjs(get)] - pub fn url(&self) -> Option<String> { - self.url.as_ref().map(|x| { - if x.fragment().is_some() { - let mut res = x.clone(); - res.set_fragment(None); - res.to_string() - } else { - x.to_string() + #[qjs(skip)] + async fn take_buffer(&self, ctx: &Ctx<'js>) -> Result<Bytes> { + match self.body.to_buffer().await { + Ok(Some(x)) => Ok(x), + Ok(None) => Err(Exception::throw_type(ctx, "Body unusable")), + Err(e) => match e { + RequestError::Reqwest(e) => { + Err(Exception::throw_type(ctx, &format!("stream failed: {e}"))) } - }) + }, + } + } + + // Returns a promise with the response body as a Blob + pub async fn blob(&self, ctx: Ctx<'js>) -> Result<Blob> { + let headers = self.init.headers.clone(); + let mime = { + let headers = headers.borrow(); + let headers = &headers.inner; + let types = headers.get_all(reqwest::header::CONTENT_TYPE); + // TODO: This is not according to spec. + types + .iter() + .next() + .map(|x| x.to_str().unwrap_or("text/html")) + .unwrap_or("text/html") + .to_owned() + }; + + let data = self.take_buffer(&ctx).await?; + Ok(Blob { + mime, + data, + }) + } + + // Returns a promise with the response body as FormData + #[qjs(rename = "formData")] + pub async fn form_data(&self, ctx: Ctx<'js>) -> Result<Value<'js>> { + Err(Exception::throw_internal(&ctx, "Not yet implemented")) + } + + // Returns a promise with the response body as JSON + pub async fn json(&self, ctx: Ctx<'js>) -> Result<Value<'js>> { + let text = self.text(ctx.clone()).await?; + ctx.json_parse(text) + } + + // Returns a promise with the response body as text + pub async fn text(&self, ctx: Ctx<'js>) -> Result<String> { + let data = self.take_buffer(&ctx).await?; + + // Skip UTF-BOM + if data.starts_with(&[0xEF, 0xBB, 0xBF]) { + Ok(String::from_utf8_lossy(&data[3..]).into_owned()) + } else { + Ok(String::from_utf8_lossy(&data).into_owned()) + } + } + + // Returns a promise with the response body as text + #[qjs(rename = "arrayBuffer")] + pub async fn array_buffer(&self, ctx: Ctx<'js>) -> Result<ArrayBuffer<'js>> { + let data = self.take_buffer(&ctx).await?; + ArrayBuffer::new(ctx, data) + } + + // ------------------------------ + // Static methods + // ------------------------------ + + #[qjs(r#static, rename = "json")] + pub fn static_json( + ctx: Ctx<'js>, + data: Value<'js>, + init: Opt<ResponseInit<'js>>, + ) -> Result<Self> { + let json = ctx.json_stringify(data)?; + let json = + json.ok_or_else(|| Exception::throw_type(&ctx, "Value is not JSON serializable"))?; + let json = json.to_string()?; + + let init = if let Some(init) = init.into_inner() { + init + } else { + ResponseInit::default(ctx)? + }; + + Ok(Response { + url: None, + body: Body::buffer(BodyKind::Buffer, json), + init, + r#type: ResponseType::Default, + was_redirected: false, + }) + } + + // Returns a new response representing a network error + #[qjs(r#static)] + pub fn error(ctx: Ctx<'js>) -> Result<Self> { + let headers = Class::instance(ctx, Headers::new_empty())?; + Ok(Response { + url: None, + body: Body::new(), + init: ResponseInit { + status: 0, + status_text: String::new(), + headers, + }, + r#type: ResponseType::Error, + was_redirected: false, + }) + } + + // Creates a new response with a different URL + #[qjs(r#static)] + pub fn redirect(ctx: Ctx<'_>, url: String, status: Opt<u32>) -> Result<Response> { + let url = url + .parse::<Url>() + .map_err(|e| Exception::throw_type(&ctx, &format!("Invalid url: {e}")))?; + + let status = status.into_inner().unwrap_or(302) as u16; + if !util::is_redirect_status(status) { + return Err(Exception::throw_range(&ctx, "Status code is not a redirect status")); } - // ------------------------------ - // Instance methods - // ------------------------------ + let headers = Class::instance(ctx, Headers::new_empty())?; - // Convert the object to a string - pub fn toString(&self, args: Rest<()>) -> String { - String::from("[object Response]") - } - - // Creates a copy of the request object - #[quickjs(rename = "clone")] - pub fn clone_js(&self, ctx: Ctx<'_>, args: Rest<()>) -> Response { - Response { - body: self.body.clone_js(ctx), - init: self.init.clone(), - url: self.url.clone(), - r#type: self.r#type, - was_redirected: self.was_redirected, - } - } - - #[quickjs(skip)] - async fn take_buffer<'js>(&self, ctx: Ctx<'js>) -> Result<Bytes> { - match self.body.to_buffer().await { - Ok(Some(x)) => Ok(x), - Ok(None) => Err(Exception::throw_type(ctx, "Body unusable")), - Err(e) => match e { - RequestError::Reqwest(e) => { - Err(Exception::throw_type(ctx, &format!("stream failed: {e}"))) - } - }, - } - } - - // Returns a promise with the response body as a Blob - pub async fn blob<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<BlobClass> { - let headers = self.init.headers.clone().restore(ctx).unwrap(); - let mime = { - let headers = headers.borrow(); - let headers = headers.inner.borrow(); - let types = headers.get_all(reqwest::header::CONTENT_TYPE); - // TODO: This is not according to spec. - types - .iter() - .next() - .map(|x| x.to_str().unwrap_or("text/html")) - .unwrap_or("text/html") - .to_owned() - }; - - let data = self.take_buffer(ctx).await?; - Ok(BlobClass { - mime, - data, - }) - } - - // Returns a promise with the response body as FormData - pub async fn formData<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<Value<'js>> { - Err(Exception::throw_internal(ctx, "Not yet implemented")) - } - - // Returns a promise with the response body as JSON - pub async fn json<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<Value<'js>> { - let text = self.text(ctx, args).await?; - ctx.json_parse(text) - } - - // Returns a promise with the response body as text - pub async fn text<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<String> { - let data = self.take_buffer(ctx).await?; - - // Skip UTF-BOM - if data.starts_with(&[0xEF, 0xBB, 0xBF]) { - Ok(String::from_utf8_lossy(&data[3..]).into_owned()) - } else { - Ok(String::from_utf8_lossy(&data).into_owned()) - } - } - - // Returns a promise with the response body as text - pub async fn arrayBuffer<'js>( - &self, - ctx: Ctx<'js>, - args: Rest<()>, - ) -> Result<ArrayBuffer<'js>> { - let data = self.take_buffer(ctx).await?; - ArrayBuffer::new(ctx, data) - } - - // ------------------------------ - // Static methods - // ------------------------------ - - #[quickjs(rename = "json")] - pub fn static_json<'js>( - ctx: Ctx<'js>, - data: Value<'js>, - init: Opt<ResponseInit>, - args: Rest<()>, - ) -> Result<Self> { - let json = ctx.json_stringify(data)?; - let json = - json.ok_or_else(|| Exception::throw_type(ctx, "Value is not JSON serializable"))?; - let json = json.to_string()?; - - let init = if let Some(init) = init.into_inner() { - init - } else { - ResponseInit::default(ctx)? - }; - - Ok(Response { - url: None, - body: Body::buffer(BodyKind::Buffer, json), - init, - r#type: ResponseType::Default, - was_redirected: false, - }) - } - - // Returns a new response representing a network error - pub fn error(ctx: Ctx<'_>, args: Rest<()>) -> Result<Self> { - let headers = Persistent::save(ctx, Class::instance(ctx, HeadersClass::new_empty())?); - Ok(Response { - url: None, - body: Body::new(), - init: ResponseInit { - status: 0, - status_text: String::new(), - headers, - }, - r#type: ResponseType::Error, - was_redirected: false, - }) - } - - // Creates a new response with a different URL - pub fn redirect( - ctx: Ctx<'_>, - url: String, - status: Opt<u32>, - args: Rest<()>, - ) -> Result<Response> { - let url = url - .parse::<Url>() - .map_err(|e| Exception::throw_type(ctx, &format!("Invalid url: {e}")))?; - - let status = status.into_inner().unwrap_or(302) as u16; - if !util::is_redirect_status(status) { - return Err(Exception::throw_range(ctx, "Status code is not a redirect status")); - } - - let headers = Persistent::save(ctx, Class::instance(ctx, HeadersClass::new_empty())?); - - Ok(Response { - url: Some(url), - body: Body::new(), - init: ResponseInit { - status, - status_text: String::new(), - headers, - }, - r#type: ResponseType::Default, - was_redirected: false, - }) - } + Ok(Response { + url: Some(url), + body: Body::new(), + init: ResponseInit { + status, + status_text: String::new(), + headers, + }, + r#type: ResponseType::Default, + was_redirected: false, + }) } } @@ -388,7 +367,7 @@ mod test { })() - "#).catch(ctx).unwrap().await.catch(ctx).unwrap(); + "#).catch(&ctx).unwrap().await.catch(&ctx).unwrap(); }) .await; } diff --git a/lib/src/fnc/script/fetch/func.rs b/lib/src/fnc/script/fetch/func.rs index cc2f6241..40dc9fac 100644 --- a/lib/src/fnc/script/fetch/func.rs +++ b/lib/src/fnc/script/fetch/func.rs @@ -2,29 +2,28 @@ use crate::fnc::script::fetch::{ body::{Body, BodyData, BodyKind}, - classes::{ - self, HeadersClass, RequestClass, RequestInit, ResponseClass, ResponseInit, ResponseType, - }, + classes::{self, Request, RequestInit, Response, ResponseInit, ResponseType}, RequestError, }; use futures::TryStreamExt; -use js::{bind, function::Opt, prelude::*, Class, Ctx, Exception, Persistent, Result, Value}; +use js::{function::Opt, Class, Ctx, Exception, Result, Value}; use reqwest::{ header::{HeaderValue, CONTENT_TYPE}, redirect, Body as ReqBody, }; use std::sync::Arc; -#[bind(object, public)] +use super::classes::Headers; + +#[js::function] #[allow(unused_variables)] pub async fn fetch<'js>( ctx: Ctx<'js>, input: Value<'js>, - init: Opt<RequestInit>, - args: Rest<()>, -) -> Result<ResponseClass> { + init: Opt<RequestInit<'js>>, +) -> Result<Response<'js>> { // Create a request from the input. - let js_req = RequestClass::new(ctx, input, init, args)?; + let js_req = Request::new(ctx.clone(), input, init)?; let url = js_req.url; @@ -32,9 +31,9 @@ pub async fn fetch<'js>( // SurrealDB Implementation keeps all javascript parts inside the context::with scope so this // unwrap should never panic. - let headers = js_req.init.headers.restore(ctx).unwrap(); + let headers = js_req.init.headers; let headers = headers.borrow(); - let mut headers = headers.inner.borrow().clone(); + let mut headers = headers.inner.clone(); let redirect = js_req.init.request_redirect; @@ -55,7 +54,7 @@ pub async fn fetch<'js>( }); let client = reqwest::Client::builder().redirect(policy).build().map_err(|e| { - Exception::throw_internal(ctx, &format!("Could not initialize http client: {e}")) + Exception::throw_internal(&ctx, &format!("Could not initialize http client: {e}")) })?; // Set the body for the request. @@ -70,7 +69,7 @@ pub async fn fetch<'js>( let body = ReqBody::from(x); req_builder = req_builder.body(body); } - BodyData::Used => return Err(Exception::throw_type(ctx, "Body unusable")), + BodyData::Used => return Err(Exception::throw_type(&ctx, "Body unusable")), }; match body.kind { BodyKind::Buffer => {} @@ -94,12 +93,11 @@ pub async fn fetch<'js>( .headers(headers) .send() .await - .map_err(|e| Exception::throw_type(ctx, &e.to_string()))?; + .map_err(|e| Exception::throw_type(&ctx, &e.to_string()))?; // Extract the headers - let headers = HeadersClass::from_map(response.headers().clone()); + let headers = Headers::from_map(response.headers().clone()); let headers = Class::instance(ctx, headers)?; - let headers = Persistent::save(ctx, headers); let init = ResponseInit { headers, status: response.status().as_u16(), @@ -111,7 +109,7 @@ pub async fn fetch<'js>( BodyKind::Buffer, response.bytes_stream().map_err(Arc::new).map_err(RequestError::Reqwest), ); - let response = ResponseClass { + let response = Response { body, init, url: Some(url), diff --git a/lib/src/fnc/script/fetch/mod.rs b/lib/src/fnc/script/fetch/mod.rs index 5636df74..40f831e9 100644 --- a/lib/src/fnc/script/fetch/mod.rs +++ b/lib/src/fnc/script/fetch/mod.rs @@ -1,6 +1,6 @@ use std::{error::Error, fmt, sync::Arc}; -use js::{Ctx, Result}; +use js::{Class, Ctx, Result}; mod body; mod classes; @@ -9,7 +9,7 @@ mod stream; mod util; use classes::{Blob, FormData, Headers, Request, Response}; -use func::Fetch; +use func::js_fetch; // Anoyingly errors aren't clone, // But with how we implement streams RequestError must be clone. @@ -30,15 +30,14 @@ impl fmt::Display for RequestError { impl Error for RequestError {} /// Register the fetch types in the context. -pub fn register(ctx: Ctx<'_>) -> Result<()> { +pub fn register(ctx: &Ctx<'_>) -> Result<()> { let globals = ctx.globals(); - globals.init_def::<Fetch>()?; - - globals.init_def::<Response>()?; - globals.init_def::<Request>()?; - globals.init_def::<Blob>()?; - globals.init_def::<FormData>()?; - globals.init_def::<Headers>()?; + globals.set("fetch", js_fetch)?; + Class::<Response>::define(&globals)?; + Class::<Request>::define(&globals)?; + Class::<Blob>::define(&globals)?; + Class::<FormData>::define(&globals)?; + Class::<Headers>::define(&globals)?; Ok(()) } @@ -55,9 +54,9 @@ mod test { let ctx = js::AsyncContext::full(&rt).await.unwrap(); js::async_with!(ctx => |$ctx|{ - crate::fnc::script::fetch::register($ctx).unwrap(); + crate::fnc::script::fetch::register(&$ctx).unwrap(); - $ctx.eval::<(),_>(r#" + $ctx.eval::<(),_>(r" globalThis.assert = (...arg) => { arg.forEach(x => { if (!x) { @@ -91,7 +90,7 @@ mod test { } throw new Error(`Code which should throw, didnt: \n${cb}`) } - "#).unwrap(); + ").unwrap(); $($t)* }).await; diff --git a/lib/src/fnc/script/fetch_stub/mod.rs b/lib/src/fnc/script/fetch_stub/mod.rs index 9f050826..e497e09a 100644 --- a/lib/src/fnc/script/fetch_stub/mod.rs +++ b/lib/src/fnc/script/fetch_stub/mod.rs @@ -1,27 +1,25 @@ -/// The stub implementations for the fetch API when `http` is not enabled. -use js::{bind, prelude::*, Ctx, Exception, Result}; +//! stub implementations for the fetch API when `http` is not enabled. + +use js::{class::Trace, Class, Ctx, Exception, Result}; #[cfg(test)] mod test; /// Register the fetch types in the context. -pub fn register(ctx: Ctx<'_>) -> Result<()> { +pub fn register(ctx: &Ctx<'_>) -> Result<()> { let globals = ctx.globals(); - globals.init_def::<Fetch>()?; - - globals.init_def::<Response>()?; - globals.init_def::<Request>()?; - globals.init_def::<Blob>()?; - globals.init_def::<FormData>()?; - globals.init_def::<Headers>()?; - + Class::<response::Response>::define(&globals)?; + Class::<request::Request>::define(&globals)?; + Class::<blob::Blob>::define(&globals)?; + Class::<form_data::FormData>::define(&globals)?; + Class::<headers::Headers>::define(&globals)?; + globals.set("fetch", Function::new(ctx, js_fetch).with_name("fetch")); Ok(()) } -#[bind(object, public)] -#[allow(unused_variables)] -fn fetch<'js>(ctx: Ctx<'js>, args: Rest<()>) -> Result<()> { - Err(Exception::throw_internal(ctx,"The 'fetch' function is not available in this build of SurrealDB. In order to use 'fetch', enable the 'http' feature.")) +#[js::function] +fn fetch<'js>(ctx: Ctx<'js>) -> Result<()> { + Err(Exception::throw_internal(&ctx,"The 'fetch' function is not available in this build of SurrealDB. In order to use 'fetch', enable the 'http' feature.")) } macro_rules! impl_stub_class { @@ -29,31 +27,29 @@ macro_rules! impl_stub_class { $( - #[js::bind(object, public)] - #[quickjs(bare)] - #[allow(non_snake_case)] - #[allow(unused_variables)] - #[allow(clippy::module_inception)] - pub mod $module{ - use js::{function::Rest, Ctx, Exception, Result}; + mod $module{ + use super::*; + #[js::class] + #[derive(Trace)] pub struct $name; + + #[js::methods] impl $name { - #[quickjs(constructor)] - pub fn new(ctx: Ctx, _arg: Rest<()>) -> Result<Self> { + #[qjs(constructor)] + pub fn new(ctx: Ctx<'_>) -> Result<Self> { Err(Exception::throw_internal( - ctx, - concat!( - "The '", - stringify!($name), - "' class is not available in this build of SurrealDB. In order to use '", - stringify!($name), - "', enable the 'http' feature." - ), - )) + &ctx, + concat!( + "The '", + stringify!($name), + "' class is not available in this build of SurrealDB. In order to use '", + stringify!($name), + "', enable the 'http' feature." + ), + )) } } - } )* }; diff --git a/lib/src/fnc/script/from.rs b/lib/src/fnc/script/from.rs index 55a5363c..10fc6a00 100644 --- a/lib/src/fnc/script/from.rs +++ b/lib/src/fnc/script/from.rs @@ -5,6 +5,7 @@ use crate::sql::object::Object; use crate::sql::value::Value; use crate::sql::Id; use chrono::{TimeZone, Utc}; +use js::prelude::This; use js::Ctx; use js::Error; use js::Exception; @@ -20,7 +21,7 @@ fn check_nul(s: &str) -> Result<(), Error> { } impl<'js> FromJs<'js> for Value { - fn from_js(ctx: Ctx<'js>, val: js::Value<'js>) -> Result<Self, Error> { + fn from_js(ctx: &Ctx<'js>, val: js::Value<'js>) -> Result<Self, Error> { match val { val if val.type_name() == "null" => Ok(Value::Null), val if val.type_name() == "undefined" => Ok(Value::None), @@ -49,14 +50,15 @@ impl<'js> FromJs<'js> for Value { // Check to see if this object is an error if v.is_error() { let e: String = v.get("message")?; - let (Ok(e) | Err(e)) = Exception::from_message(ctx, &e).map(|x| x.throw()); + let (Ok(e) | Err(e)) = + Exception::from_message(ctx.clone(), &e).map(|x| x.throw()); return Err(e); } // Check to see if this object is a record - if (v).instance_of::<classes::record::record::Record>() { - let v = v.into_instance::<classes::record::record::Record>().unwrap(); + if (v).instance_of::<classes::record::Record>() { + let v = v.into_class::<classes::record::Record>().unwrap(); let borrow = v.borrow(); - let v: &classes::record::record::Record = &borrow; + let v: &classes::record::Record = &borrow; check_nul(&v.value.tb)?; if let Id::String(s) = &v.value.id { check_nul(s)?; @@ -64,20 +66,20 @@ impl<'js> FromJs<'js> for Value { return Ok(v.value.clone().into()); } // Check to see if this object is a duration - if (v).instance_of::<classes::duration::duration::Duration>() { - let v = v.into_instance::<classes::duration::duration::Duration>().unwrap(); + if (v).instance_of::<classes::duration::Duration>() { + let v = v.into_class::<classes::duration::Duration>().unwrap(); let borrow = v.borrow(); - let v: &classes::duration::duration::Duration = &borrow; + let v: &classes::duration::Duration = &borrow; return match &v.value { Some(v) => Ok(v.clone().into()), None => Ok(Value::None), }; } // Check to see if this object is a uuid - if (v).instance_of::<classes::uuid::uuid::Uuid>() { - let v = v.into_instance::<classes::uuid::uuid::Uuid>().unwrap(); + if (v).instance_of::<classes::uuid::Uuid>() { + let v = v.into_class::<classes::uuid::Uuid>().unwrap(); let borrow = v.borrow(); - let v: &classes::uuid::uuid::Uuid = &borrow; + let v: &classes::uuid::Uuid = &borrow; return match &v.value { Some(v) => Ok(v.clone().into()), None => Ok(Value::None), @@ -87,7 +89,7 @@ impl<'js> FromJs<'js> for Value { let date: js::Object = ctx.globals().get("Date")?; if (v).is_instance_of(&date) { let f: js::Function = v.get("getTime")?; - let m: i64 = f.call((js::prelude::This(v),))?; + let m: i64 = f.call((This(v),))?; let d = Utc.timestamp_millis_opt(m).unwrap(); return Ok(Datetime::from(d).into()); } diff --git a/lib/src/fnc/script/globals/console.rs b/lib/src/fnc/script/globals/console.rs index 7ec7fda5..b681ea78 100644 --- a/lib/src/fnc/script/globals/console.rs +++ b/lib/src/fnc/script/globals/console.rs @@ -1,32 +1,44 @@ -#[js::bind(object, public)] -#[quickjs(rename = "console")] -#[allow(clippy::module_inception)] -pub mod console { - // Specify the imports - use crate::sql::value::Value; - use js::prelude::Rest; - /// Log the input values as INFO - pub fn log(args: Rest<Value>) { - info!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); - } - /// Log the input values as INFO - pub fn info(args: Rest<Value>) { - info!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); - } - /// Log the input values as WARN - pub fn warn(args: Rest<Value>) { - warn!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); - } - /// Log the input values as ERROR - pub fn error(args: Rest<Value>) { - error!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); - } - /// Log the input values as DEBUG - pub fn debug(args: Rest<Value>) { - debug!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); - } - /// Log the input values as TRACE - pub fn trace(args: Rest<Value>) { - trace!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); - } +// Specify the imports +use crate::sql::value::Value; +use js::{prelude::Rest, Ctx, Object, Result}; +/// Log the input values as INFO +#[js::function] +pub fn log(args: Rest<Value>) { + info!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); +} +/// Log the input values as INFO +#[js::function] +pub fn info(args: Rest<Value>) { + info!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); +} +/// Log the input values as WARN +#[js::function] +pub fn warn(args: Rest<Value>) { + warn!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); +} +/// Log the input values as ERROR +#[js::function] +pub fn error(args: Rest<Value>) { + error!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); +} +/// Log the input values as DEBUG +#[js::function] +pub fn debug(args: Rest<Value>) { + debug!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); +} +/// Log the input values as TRACE +#[js::function] +pub fn trace(args: Rest<Value>) { + trace!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" ")); +} + +pub fn console<'js>(ctx: &Ctx<'js>) -> Result<Object<'js>> { + let console = Object::new(ctx.clone())?; + console.set("log", js_log)?; + console.set("info", js_info)?; + console.set("warn", js_warn)?; + console.set("error", js_error)?; + console.set("debug", js_debug)?; + console.set("trace", js_trace)?; + Ok(console) } diff --git a/lib/src/fnc/script/into.rs b/lib/src/fnc/script/into.rs index 1b999c0b..495df24f 100644 --- a/lib/src/fnc/script/into.rs +++ b/lib/src/fnc/script/into.rs @@ -12,58 +12,58 @@ use js::Object; use js::Undefined; impl<'js> IntoJs<'js> for Value { - fn into_js(self, ctx: Ctx<'js>) -> Result<js::Value<'js>, Error> { + fn into_js(self, ctx: &Ctx<'js>) -> Result<js::Value<'js>, Error> { (&self).into_js(ctx) } } impl<'js> IntoJs<'js> for &Value { - fn into_js(self, ctx: Ctx<'js>) -> Result<js::Value<'js>, Error> { + fn into_js(self, ctx: &Ctx<'js>) -> Result<js::Value<'js>, Error> { match self { Value::Null => Null.into_js(ctx), Value::None => Undefined.into_js(ctx), - Value::Bool(boolean) => Ok(js::Value::new_bool(ctx, *boolean)), - Value::Strand(v) => js::String::from_str(ctx, v)?.into_js(ctx), - Value::Number(Number::Int(v)) => Ok(js::Value::new_int(ctx, *v as i32)), - Value::Number(Number::Float(v)) => Ok(js::Value::new_float(ctx, *v)), + Value::Bool(boolean) => Ok(js::Value::new_bool(ctx.clone(), *boolean)), + Value::Strand(v) => js::String::from_str(ctx.clone(), v)?.into_js(ctx), + Value::Number(Number::Int(v)) => Ok(js::Value::new_int(ctx.clone(), *v as i32)), + Value::Number(Number::Float(v)) => Ok(js::Value::new_float(ctx.clone(), *v)), &Value::Number(Number::Decimal(v)) => match decimal_is_integer(&v) { - true => Ok(js::Value::new_int(ctx, v.try_into().unwrap_or_default())), - false => Ok(js::Value::new_float(ctx, v.try_into().unwrap_or_default())), + true => Ok(js::Value::new_int(ctx.clone(), v.try_into().unwrap_or_default())), + false => Ok(js::Value::new_float(ctx.clone(), v.try_into().unwrap_or_default())), }, Value::Datetime(v) => { - let date: js::Function = ctx.globals().get("Date")?; + let date: js::function::Constructor = ctx.globals().get("Date")?; date.construct((v.0.timestamp_millis(),)) } - Value::Thing(v) => Ok(Class::<classes::record::record::Record>::instance( - ctx, - classes::record::record::Record { + Value::Thing(v) => Ok(Class::<classes::record::Record>::instance( + ctx.clone(), + classes::record::Record { value: v.to_owned(), }, )? .into_value()), - Value::Duration(v) => Ok(Class::<classes::duration::duration::Duration>::instance( - ctx, - classes::duration::duration::Duration { + Value::Duration(v) => Ok(Class::<classes::duration::Duration>::instance( + ctx.clone(), + classes::duration::Duration { value: Some(v.to_owned()), }, )? .into_value()), - Value::Uuid(v) => Ok(Class::<classes::uuid::uuid::Uuid>::instance( - ctx, - classes::uuid::uuid::Uuid { + Value::Uuid(v) => Ok(Class::<classes::uuid::Uuid>::instance( + ctx.clone(), + classes::uuid::Uuid { value: Some(v.to_owned()), }, )? .into_value()), Value::Array(v) => { - let x = Array::new(ctx)?; + let x = Array::new(ctx.clone())?; for (i, v) in v.iter().enumerate() { x.set(i, v)?; } x.into_js(ctx) } Value::Object(v) => { - let x = Object::new(ctx)?; + let x = Object::new(ctx.clone())?; for (k, v) in v.iter() { x.set(k, v)?; } diff --git a/lib/src/fnc/script/main.rs b/lib/src/fnc/script/main.rs index 6f837d94..08ec697f 100644 --- a/lib/src/fnc/script/main.rs +++ b/lib/src/fnc/script/main.rs @@ -50,23 +50,24 @@ pub async fn run( // Attempt to execute the script async_with!(ctx => |ctx|{ - let res = async move { + let res = async{ // register all classes to the runtime. // Get the context global object let global = ctx.globals(); // Register the surrealdb module as a global object global.set( "surrealdb", - Module::evaluate_def::<modules::surrealdb::Package, _>(ctx, "surrealdb")? + Module::evaluate_def::<modules::surrealdb::Package, _>(ctx.clone(), "surrealdb")? .get::<_, js::Value>("default")?, )?; - fetch::register(ctx)?; + fetch::register(&ctx)?; + let console = globals::console::console(&ctx)?; // Register the console function to the globals - global.init_def::<globals::console::Console>()?; + global.set("console",console)?; // Register the special SurrealDB types as classes - classes::init(ctx)?; + classes::init(&ctx)?; // Attempt to compile the script - let res = ctx.compile("script", src)?; + let res = ctx.clone().compile("script", src)?; // Attempt to fetch the main export let fnc = res.get::<_, Function>("default")?; // Extract the doc if any @@ -76,7 +77,7 @@ pub async fn run( promise.await }.await; - res.catch(ctx).map_err(Error::from) + res.catch(&ctx).map_err(Error::from) }) .await } diff --git a/lib/src/fnc/script/modules/mod.rs b/lib/src/fnc/script/modules/mod.rs index 4e37c88a..879241f7 100644 --- a/lib/src/fnc/script/modules/mod.rs +++ b/lib/src/fnc/script/modules/mod.rs @@ -54,8 +54,8 @@ macro_rules! impl_module_def { Ok(()) } - fn evaluate<'js>(ctx: js::Ctx<'js>, exports: &mut js::module::Exports<'js>) -> js::Result<()> { - let default = js::Object::new(ctx)?; + fn evaluate<'js>(ctx: &js::Ctx<'js>, exports: &mut js::module::Exports<'js>) -> js::Result<()> { + let default = js::Object::new(ctx.clone())?; $( exports.export($name, crate::fnc::script::modules::impl_module_def!(ctx, $path, $name, $action, $($wrapper)?))?; default.set($name, crate::fnc::script::modules::impl_module_def!(ctx, $path, $name, $action, $($wrapper)?))?; diff --git a/lib/src/fnc/script/modules/os.rs b/lib/src/fnc/script/modules/os.rs index 49162f66..1e6b4b9b 100644 --- a/lib/src/fnc/script/modules/os.rs +++ b/lib/src/fnc/script/modules/os.rs @@ -1,13 +1,31 @@ -#[js::bind(module, public)] -#[quickjs(bare)] -#[allow(non_upper_case_globals)] -pub mod package { - /// Get the target system architecture - pub fn arch() -> &'static str { - crate::env::arch() +use js::{ + module::{Declarations, Exports, ModuleDef}, + Result, +}; + +/// Get the target system architecture +#[js::function] +pub fn arch() -> &'static str { + crate::env::arch() +} +/// Get the target operating system +#[js::function] +pub fn platform() -> &'static str { + crate::env::os() +} + +pub struct Package; + +impl ModuleDef for Package { + fn declare(declare: &mut Declarations) -> Result<()> { + declare.declare("arch")?; + declare.declare("platform")?; + Ok(()) } - /// Get the target operating system - pub fn platform() -> &'static str { - crate::env::os() + + fn evaluate<'js>(_ctx: &js::Ctx<'js>, exports: &mut Exports<'js>) -> Result<()> { + exports.export("arch", js_arch)?; + exports.export("platform", js_platform)?; + Ok(()) } } diff --git a/lib/src/fnc/script/modules/surrealdb/mod.rs b/lib/src/fnc/script/modules/surrealdb/mod.rs index 7e38b6b2..26fb2796 100644 --- a/lib/src/fnc/script/modules/surrealdb/mod.rs +++ b/lib/src/fnc/script/modules/surrealdb/mod.rs @@ -12,9 +12,9 @@ impl_module_def!( "version" => (env!("CARGO_PKG_VERSION")) ); -fn pkg<'js, D>(ctx: Ctx<'js>, name: &str) -> Result<Value<'js>> +fn pkg<'js, D>(ctx: &Ctx<'js>, name: &str) -> Result<Value<'js>> where D: ModuleDef, { - Module::evaluate_def::<D, _>(ctx, name)?.get::<_, js::Value>("default") + Module::evaluate_def::<D, _>(ctx.clone(), name)?.get::<_, js::Value>("default") }