diff --git a/.gitignore b/.gitignore index dd4cda9..1753aea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ **/target -**/Cargo.lock **/.DS_Store .docker-cargo/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..92853ba --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,753 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "diesel" +version = "2.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" +dependencies = [ + "bitflags", + "byteorder", + "diesel_derives", + "mysqlclient-sys", + "percent-encoding", + "url", +] + +[[package]] +name = "diesel_derives" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" +dependencies = [ + "diesel_table_macro_syntax", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +dependencies = [ + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dissimilar" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "lipsum" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8451846f1f337e44486666989fbce40be804da139d5a4477d6b88ece5dc69f4" +dependencies = [ + "rand", + "rand_chacha", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "mysqlclient-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61b381528ba293005c42a409dd73d034508e273bf90481f17ec2e964a6e969b" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "trybuild" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f" +dependencies = [ + "basic-toml", + "dissimilar", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "udf" +version = "0.5.4" +dependencies = [ + "cfg-if", + "chrono", + "udf-macros", + "udf-sys", +] + +[[package]] +name = "udf-examples" +version = "0.5.4" +dependencies = [ + "diesel", + "generic-array", + "lazy_static", + "lipsum", + "sha2", + "udf", + "uuid", +] + +[[package]] +name = "udf-macros" +version = "0.5.4" +dependencies = [ + "heck", + "lazy_static", + "proc-macro2", + "quote", + "syn", + "trybuild", + "udf", + "udf-sys", +] + +[[package]] +name = "udf-sys" +version = "0.5.4" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +dependencies = [ + "atomic", + "getrandom", + "md-5", + "rand", + "sha1_smol", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" diff --git a/udf-macros/src/register.rs b/udf-macros/src/register.rs index 42c4320..32420fa 100644 --- a/udf-macros/src/register.rs +++ b/udf-macros/src/register.rs @@ -1,5 +1,7 @@ #![allow(unused_imports)] +use std::iter; + use heck::AsSnakeCase; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; @@ -8,24 +10,17 @@ use syn::parse::{Parse, ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ parse_macro_input, parse_quote, DeriveInput, Error, Expr, ExprLit, Ident, ImplItem, - ImplItemType, Item, ItemImpl, Lit, Meta, Path, PathSegment, Token, Type, TypePath, + ImplItemType, Item, ItemImpl, Lit, LitStr, Meta, Path, PathSegment, Token, Type, TypePath, TypeReference, }; use crate::match_variant; use crate::types::{make_type_list, ImplType, RetType, TypeClass}; -/// Create an identifier from another identifier, changing the name to snake case -macro_rules! format_ident_str { - ($formatter:tt, $ident:ident) => { - Ident::new(format!($formatter, $ident).as_str(), Span::call_site()) - }; -} - /// Verify that an `ItemImpl` matches the end of any given path /// /// implements `BasicUdf` (in any of its pathing options) -fn impls_path(itemimpl: &ItemImpl, expected: ImplType) -> bool { +fn impl_type(itemimpl: &ItemImpl) -> Option { let implemented = &itemimpl.trait_.as_ref().unwrap().1.segments; let basic_paths: [Punctuated; 3] = [ @@ -39,9 +34,12 @@ fn impls_path(itemimpl: &ItemImpl, expected: ImplType) -> bool { parse_quote! {AggregateUdf}, ]; - match expected { - ImplType::Basic => basic_paths.contains(implemented), - ImplType::Aggregate => arg_paths.contains(implemented), + if basic_paths.contains(implemented) { + Some(ImplType::Basic) + } else if arg_paths.contains(implemented) { + Some(ImplType::Aggregate) + } else { + None } } @@ -57,14 +55,11 @@ fn impls_path(itemimpl: &ItemImpl, expected: ImplType) -> bool { pub fn register(args: &TokenStream, input: TokenStream) -> TokenStream { let parsed = parse_macro_input!(input as ItemImpl); - let impls_basic = impls_path(&parsed, ImplType::Basic); - let impls_agg = impls_path(&parsed, ImplType::Aggregate); - - if !(impls_basic || impls_agg) { + let Some(impl_ty) = impl_type(&parsed) else { return Error::new_spanned(&parsed, "Expected trait `BasicUdf` or `AggregateUdf`") .into_compile_error() .into(); - } + }; // Full type path of our data struct let Type::Path(dstruct_path) = parsed.self_ty.as_ref() else { @@ -73,7 +68,7 @@ pub fn register(args: &TokenStream, input: TokenStream) -> TokenStream { .into(); }; - let base_fn_names = match parse_args(args, dstruct_path) { + let parsed_meta = match ParsedMeta::parse(args, dstruct_path) { Ok(v) => v, Err(e) => return e.into_compile_error().into(), }; @@ -89,26 +84,24 @@ pub fn register(args: &TokenStream, input: TokenStream) -> TokenStream { Span::call_site(), ); - let (ret_ty, wrapper_def) = if impls_basic { - match get_rt_and_wrapper(&parsed, dstruct_path, &wrapper_ident) { + let (ret_ty, wrapper_def) = match impl_ty { + ImplType::Basic => match get_ret_ty_and_wrapper(&parsed, dstruct_path, &wrapper_ident) { Ok((r, w)) => (Some(r), w), Err(e) => return e.into_compile_error().into(), - } - } else { - (None, TokenStream2::new()) + }, + ImplType::Aggregate => (None, TokenStream2::new()), }; - let content_iter = base_fn_names.iter().map(|base_fn_name| { - if impls_basic { - make_basic_fns( - ret_ty.as_ref().unwrap(), - base_fn_name, - dstruct_path, - &wrapper_ident, - ) - } else { - make_agg_fns(&parsed, base_fn_name, dstruct_path, &wrapper_ident) - } + let helper_traits = make_helper_trait_impls(dstruct_path, &parsed_meta, impl_ty); + + let fn_items_iter = parsed_meta.all_names().map(|base_fn_name| match impl_ty { + ImplType::Basic => make_basic_fns( + ret_ty.as_ref().unwrap(), + base_fn_name, + dstruct_path, + &wrapper_ident, + ), + ImplType::Aggregate => make_agg_fns(&parsed, base_fn_name, dstruct_path, &wrapper_ident), }); quote! { @@ -116,64 +109,85 @@ pub fn register(args: &TokenStream, input: TokenStream) -> TokenStream { #wrapper_def - #( #content_iter )* + #helper_traits + + #( #fn_items_iter )* } .into() } -/// Parse attribute arguments. Returns an iterator of names -fn parse_args(args: &TokenStream, dstruct_path: &TypePath) -> syn::Result> { - let meta = Punctuated::::parse_terminated.parse(args.clone())?; - let mut base_fn_names: Vec = vec![]; - let mut primary_name_specified = false; - - for m in meta { - let Meta::NameValue(mval) = m else { - return Err(Error::new_spanned(m, "expected `a = b atributes`")); - }; +/// Arguments we parse from metadata or default to +struct ParsedMeta { + name: String, + aliases: Vec, + default_name_used: bool, +} - if !mval.path.segments.iter().count() == 1 { - return Err(Error::new_spanned(mval.path, "unexpected path")); - } +impl ParsedMeta { + /// Parse attribute arguments. Returns an iterator of names + fn parse(args: &TokenStream, dstruct_path: &TypePath) -> syn::Result { + let meta = Punctuated::::parse_terminated.parse(args.clone())?; + let mut name_from_attributes = None; + let mut aliases = Vec::new(); - let key = mval.path.segments.first().unwrap(); + for m in meta { + let Meta::NameValue(mval) = m else { + return Err(Error::new_spanned(m, "expected `a = b atributes`")); + }; - let Expr::Lit(ExprLit { - lit: Lit::Str(value), - .. - }) = mval.value - else { - return Err(Error::new_spanned(mval.value, "expected a literal string")); - }; + if !mval.path.segments.iter().count() == 1 { + return Err(Error::new_spanned(mval.path, "unexpected path")); + } - if key.ident == "name" { - if primary_name_specified { - return Err(Error::new_spanned(key, "`name` can only be specified once")); + let key = mval.path.segments.first().unwrap(); + + let Expr::Lit(ExprLit { + lit: Lit::Str(value), + .. + }) = mval.value + else { + return Err(Error::new_spanned(mval.value, "expected a literal string")); + }; + + if key.ident == "name" { + if name_from_attributes.is_some() { + return Err(Error::new_spanned(key, "`name` can only be specified once")); + } + name_from_attributes = Some(value.value()); + } else if key.ident == "alias" { + aliases.push(value.value()); + } else { + return Err(Error::new_spanned( + key, + "unexpected key (only `name` and `alias` are accepted)", + )); } - base_fn_names.push(value.value()); - primary_name_specified = true; - } else if key.ident == "alias" { - base_fn_names.push(value.value()); - } else { - return Err(Error::new_spanned( - key, - "unexpected key (only `name` and `alias` are accepted)", - )); } - } - if !primary_name_specified { - // If we don't have a name specified, use the type name as snake case - let ty_ident = &dstruct_path.path.segments.last().unwrap().ident; - let fn_name = AsSnakeCase(&ty_ident.to_string()).to_string(); - base_fn_names.push(fn_name); + let mut default_name_used = false; + let name = name_from_attributes.unwrap_or_else(|| { + // If we don't have a name specified, use the type name as snake case + let ty_ident = &dstruct_path.path.segments.last().unwrap().ident; + let fn_name = AsSnakeCase(&ty_ident.to_string()).to_string(); + default_name_used = true; + fn_name + }); + + Ok(Self { + name, + aliases, + default_name_used, + }) } - Ok(base_fn_names) + /// Iterate the basic name and all aliases + fn all_names(&self) -> impl Iterator { + iter::once(&self.name).chain(self.aliases.iter()) + } } /// Get the return type to use and a wrapper. Once per impl setup. -fn get_rt_and_wrapper( +fn get_ret_ty_and_wrapper( parsed: &ItemImpl, dstruct_path: &TypePath, wrapper_ident: &Ident, @@ -209,6 +223,40 @@ fn get_rt_and_wrapper( Ok((ret_ty, wrapper_struct)) } +/// Make implementations for our helper/metadata traits +fn make_helper_trait_impls( + dstruct_path: &TypePath, + meta: &ParsedMeta, + impl_ty: ImplType, +) -> TokenStream2 { + let name = LitStr::new(&meta.name, Span::call_site()); + let aliases = meta + .aliases + .iter() + .map(|alias| LitStr::new(alias.as_ref(), Span::call_site())); + let (trait_name, check_expr) = match impl_ty { + ImplType::Basic => ( + quote! { ::udf::wrapper::RegisteredBasicUdf }, + TokenStream2::new(), + ), + ImplType::Aggregate => ( + quote! { ::udf::wrapper::RegisteredAggregateUdf }, + quote! { const _: () = ::udf::wrapper::verify_aggregate_attributes::<#dstruct_path>(); }, + ), + }; + let default_name_used = meta.default_name_used; + + quote! { + impl #trait_name for #dstruct_path { + const NAME: &'static str = #name; + const ALIASES: &'static [&'static str] = &[#( #aliases ),*]; + const DEFAULT_NAME_USED: bool = #default_name_used; + } + + #check_expr + } +} + /// Create the basic function signatures (`xxx_init`, `xxx_deinit`, `xxx`) fn make_basic_fns( rt: &RetType, @@ -216,9 +264,9 @@ fn make_basic_fns( dstruct_path: &TypePath, wrapper_ident: &Ident, ) -> TokenStream2 { - let init_fn_name = format_ident_str!("{}_init", base_fn_name); - let deinit_fn_name = format_ident_str!("{}_deinit", base_fn_name); - let process_fn_name = format_ident_str!("{}", base_fn_name); + let init_fn_name = format_ident!("{}_init", base_fn_name); + let deinit_fn_name = format_ident!("{}_deinit", base_fn_name); + let process_fn_name = format_ident!("{}", base_fn_name); let init_fn = make_init_fn(dstruct_path, wrapper_ident, &init_fn_name); let deinit_fn = make_deinit_fn(dstruct_path, wrapper_ident, &deinit_fn_name); @@ -269,9 +317,9 @@ fn make_agg_fns( dstruct_path: &TypePath, // Name of the data structure wrapper_ident: &Ident, ) -> TokenStream2 { - let clear_fn_name = format_ident_str!("{}_clear", base_fn_name); - let add_fn_name = format_ident_str!("{}_add", base_fn_name); - let remove_fn_name = format_ident_str!("{}_remove", base_fn_name); + let clear_fn_name = format_ident!("{}_clear", base_fn_name); + let add_fn_name = format_ident!("{}_add", base_fn_name); + let remove_fn_name = format_ident!("{}_remove", base_fn_name); // Determine whether this re-implements `remove` let impls_remove = &parsed @@ -280,7 +328,6 @@ fn make_agg_fns( .filter_map(match_variant!(ImplItem::Fn)) .map(|m| &m.sig.ident) .any(|id| *id == "remove"); - let base_fn_ident = Ident::new(base_fn_name, Span::call_site()); let clear_fn = make_clear_fn(dstruct_path, wrapper_ident, &clear_fn_name); let add_fn = make_add_fn(dstruct_path, wrapper_ident, &add_fn_name); @@ -295,10 +342,6 @@ fn make_agg_fns( }; quote! { - // Sanity check that we implemented - #[allow(dead_code, non_upper_case_globals)] - const did_you_apply_the_same_aliases_to_the_BasicUdf_impl: *const () = #base_fn_ident as _; - #clear_fn #add_fn diff --git a/udf-macros/tests/fail/agg_missing_basic.rs b/udf-macros/tests/fail/agg_missing_basic.rs new file mode 100644 index 0000000..88bfc8a --- /dev/null +++ b/udf-macros/tests/fail/agg_missing_basic.rs @@ -0,0 +1,22 @@ +#![allow(unused)] + +use udf::prelude::*; + +struct MyUdf; + +impl AggregateUdf for MyUdf { + // Required methods + fn clear(&mut self, cfg: &UdfCfg, error: Option) -> Result<(), NonZeroU8> { + todo!() + } + fn add( + &mut self, + cfg: &UdfCfg, + args: &ArgList<'_, Process>, + error: Option, + ) -> Result<(), NonZeroU8> { + todo!() + } +} + +fn main() {} diff --git a/udf-macros/tests/fail/agg_missing_basic.stderr b/udf-macros/tests/fail/agg_missing_basic.stderr new file mode 100644 index 0000000..b944721 --- /dev/null +++ b/udf-macros/tests/fail/agg_missing_basic.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `MyUdf: BasicUdf` is not satisfied + --> tests/fail/agg_missing_basic.rs:7:23 + | +7 | impl AggregateUdf for MyUdf { + | ^^^^^ the trait `BasicUdf` is not implemented for `MyUdf` + | +note: required by a bound in `udf::AggregateUdf` + --> $WORKSPACE/udf/src/traits.rs + | + | pub trait AggregateUdf: BasicUdf { + | ^^^^^^^^ required by this bound in `AggregateUdf` diff --git a/udf-macros/tests/fail/missing_rename.stderr b/udf-macros/tests/fail/missing_rename.stderr index 36c4bd5..0e1a69f 100644 --- a/udf-macros/tests/fail/missing_rename.stderr +++ b/udf-macros/tests/fail/missing_rename.stderr @@ -1,7 +1,22 @@ -error[E0425]: cannot find value `my_udf` in this scope +error[E0080]: evaluation of constant value failed + --> $WORKSPACE/udf/src/wrapper.rs + | + | panic!("{}", msg); + | ^^^^^^^^^^^^^^^^^ the evaluated program panicked at '`#[register]` on `BasicUdf` and `AggregateUdf` must have the same `name` argument; got `foo` and `my_udf` (default from struct name)', $WORKSPACE/udf/src/wrapper.rs:83:5 + | +note: inside `wrapper::verify_aggregate_attributes_name::` + --> $WORKSPACE/udf/src/wrapper.rs + | + | panic!("{}", msg); + | ^^^^^^^^^^^^^^^^^ +note: inside `verify_aggregate_attributes::` + --> $WORKSPACE/udf/src/wrapper.rs + | + | verify_aggregate_attributes_name::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: inside `_` --> tests/fail/missing_rename.rs:25:1 | 25 | #[register] - | ^^^^^^^^^^^ not found in this scope - | - = note: this error originates in the attribute macro `register` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^^^^^^ + = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the attribute macro `register` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/udf-macros/tests/ok_agg_alias.rs b/udf-macros/tests/ok_agg_alias.rs new file mode 100644 index 0000000..da0026c --- /dev/null +++ b/udf-macros/tests/ok_agg_alias.rs @@ -0,0 +1,51 @@ +#![allow(unused)] + +use udf::prelude::*; + +struct MyUdf; + +#[register(name = "foo", alias = "bar")] +impl BasicUdf for MyUdf { + type Returns<'a> = Option; + + fn init(cfg: &UdfCfg, args: &ArgList) -> Result { + todo!(); + } + + fn process<'a>( + &'a mut self, + cfg: &UdfCfg, + args: &ArgList, + error: Option, + ) -> Result, ProcessError> { + todo!(); + } +} + +#[register(name = "foo", alias = "bar")] +impl AggregateUdf for MyUdf { + fn clear(&mut self, cfg: &UdfCfg, error: Option) -> Result<(), NonZeroU8> { + todo!() + } + fn add( + &mut self, + cfg: &UdfCfg, + args: &ArgList<'_, Process>, + error: Option, + ) -> Result<(), NonZeroU8> { + todo!() + } +} + +fn main() { + let _ = foo as *const (); + let _ = foo_init as *const (); + let _ = foo_deinit as *const (); + let _ = foo_add as *const (); + let _ = foo_clear as *const (); + let _ = bar as *const (); + let _ = bar_init as *const (); + let _ = bar_deinit as *const (); + let _ = bar_add as *const (); + let _ = bar_clear as *const (); +} diff --git a/udf-sys/src/lib.rs b/udf-sys/src/lib.rs index bbd9f0d..b9adbb1 100644 --- a/udf-sys/src/lib.rs +++ b/udf-sys/src/lib.rs @@ -77,7 +77,7 @@ pub struct UDF_ARGS { /// Number of arguments present pub arg_count: ::std::ffi::c_uint, - /// Buffer of item_result pointers that indicate argument type + /// Buffer of `item_result` pointers that indicate argument type /// /// Remains mutable because it can be set in `xxx_init` pub arg_types: *mut Item_result, diff --git a/udf/src/mock.rs b/udf/src/mock.rs index 06dcdbc..9f5262c 100644 --- a/udf/src/mock.rs +++ b/udf/src/mock.rs @@ -355,7 +355,7 @@ pub struct MockArgList { /// pinned! built_args: Option, - /// The resulting UDF_ARGS struct that points to `built_args` + /// The resulting `UDF_ARGS` struct that points to `built_args` udf_args: Option>, /// We use a phantom pin here because our `out_list` item will reference diff --git a/udf/src/types/arg_list.rs b/udf/src/types/arg_list.rs index f781778..e47a303 100644 --- a/udf/src/types/arg_list.rs +++ b/udf/src/types/arg_list.rs @@ -18,8 +18,8 @@ use crate::{Init, SqlArg, UdfState}; /// easily work with arguments. #[repr(transparent)] pub struct ArgList<'a, S: UdfState>( - /// UnsafeCell indicates to the compiler that this struct may have interior - /// mutability (i.e., cannot make som optimizations) + /// `UnsafeCell` indicates to the compiler that this struct may have interior + /// mutability (i.e., cannot make some optimizations) pub(super) UnsafeCell, /// We use this zero-sized marker to hold our state PhantomData<&'a S>, diff --git a/udf/src/types/sql_types.rs b/udf/src/types/sql_types.rs index 13a904e..e09d381 100644 --- a/udf/src/types/sql_types.rs +++ b/udf/src/types/sql_types.rs @@ -149,7 +149,7 @@ impl<'a> SqlResult<'a> { ptr: *const u8, tag: Item_result, len: usize, - ) -> Result, String> { + ) -> Result { // Handle nullptr right away here let marker = diff --git a/udf/src/wrapper.rs b/udf/src/wrapper.rs index 87799e0..01e69f4 100644 --- a/udf/src/wrapper.rs +++ b/udf/src/wrapper.rs @@ -3,11 +3,16 @@ //! Warning: This module should be considered unstable and generally not for //! public use +#[macro_use] +mod const_helpers; mod functions; mod helpers; mod modded_types; mod process; +use std::str; + +use const_helpers::{const_slice_eq, const_slice_to_str, const_str_eq}; pub use functions::{wrap_add, wrap_clear, wrap_deinit, wrap_init, wrap_remove, BufConverter}; pub(crate) use helpers::*; pub use modded_types::UDF_ARGSx; @@ -16,5 +21,107 @@ pub use process::{ wrap_process_buf_option_ref, }; +/// A trait implemented by the proc macro +// FIXME: on unimplemented +pub trait RegisteredBasicUdf { + /// The main function name + const NAME: &'static str; + /// Aliases, if any + const ALIASES: &'static [&'static str]; + /// True if `NAME` comes from the default value for the struct + const DEFAULT_NAME_USED: bool; +} + +/// Implemented by the proc macro. This is used to enforce that the basic UDF and aggregate +/// UDF have the same name and aliases. +pub trait RegisteredAggregateUdf: RegisteredBasicUdf { + /// The main function name + const NAME: &'static str; + /// Aliases, if any + const ALIASES: &'static [&'static str]; + /// True if `NAME` comes from the default value for the struct + const DEFAULT_NAME_USED: bool; +} + +const NAME_MSG: &str = "`#[register]` on `BasicUdf` and `AggregateUdf` must have the same "; + +/// Enforce that a struct has the same basic and aggregate UDF names. +pub const fn verify_aggregate_attributes() { + verify_aggregate_attributes_name::(); + verify_aggregate_attribute_aliases::(); +} + +const fn verify_aggregate_attributes_name() { + let basic_name = ::NAME; + let agg_name = ::NAME; + let basic_default_name = ::DEFAULT_NAME_USED; + let agg_default_name = ::DEFAULT_NAME_USED; + + if const_str_eq(basic_name, agg_name) { + return; + } + + let mut msg_buf = [0u8; 512]; + let mut curs = 0; + curs += const_write_all!( + msg_buf, + [NAME_MSG, "`name` argument; got `", basic_name, "`",], + curs + ); + + if basic_default_name { + curs += const_write_all!(msg_buf, [" (default from struct name)"], curs); + } + + curs += const_write_all!(msg_buf, [" and `", agg_name, "`"], curs); + + if agg_default_name { + curs += const_write_all!(msg_buf, [" (default from struct name)"], curs); + } + + let msg = const_slice_to_str(msg_buf.as_slice(), curs); + panic!("{}", msg); +} + +#[allow(clippy::cognitive_complexity)] +const fn verify_aggregate_attribute_aliases() { + let basic_aliases = ::ALIASES; + let agg_aliases = ::ALIASES; + + if const_slice_eq(basic_aliases, agg_aliases) { + return; + } + + let mut msg_buf = [0u8; 512]; + let mut curs = 0; + + curs += const_write_all!(msg_buf, [NAME_MSG, "`alias` arguments; got [",], 0); + + let mut i = 0; + while i < basic_aliases.len() { + if i > 0 { + curs += const_write_all!(msg_buf, [", "], curs); + } + curs += const_write_all!(msg_buf, ["`", basic_aliases[i], "`",], curs); + i += 1; + } + + curs += const_write_all!(msg_buf, ["] and ["], curs); + + let mut i = 0; + while i < agg_aliases.len() { + if i > 0 { + curs += const_write_all!(msg_buf, [", "], curs); + } + curs += const_write_all!(msg_buf, ["`", agg_aliases[i], "`",], curs); + i += 1; + } + + curs += const_write_all!(msg_buf, ["]"], curs); + + let msg = const_slice_to_str(msg_buf.as_slice(), curs); + panic!("{}", msg); +} + #[cfg(test)] mod tests; diff --git a/udf/src/wrapper/const_helpers.rs b/udf/src/wrapper/const_helpers.rs new file mode 100644 index 0000000..60e9942 --- /dev/null +++ b/udf/src/wrapper/const_helpers.rs @@ -0,0 +1,122 @@ +use std::str; + +/// Similar to `copy_from_slice` but works at comptime. +/// +/// Takes a `start` offset so we can index into an existing slice. +macro_rules! const_arr_copy { + ($dst:expr, $src:expr, $start:expr) => {{ + let max_idx = $dst.len() - $start; + let (to_write, add_ellipsis) = if $src.len() <= (max_idx.saturating_sub($start)) { + ($src.len(), false) + } else { + ($src.len().saturating_sub(4), true) + }; + + let mut i = 0; + while i < to_write { + $dst[i + $start] = $src[i]; + i += 1; + } + + if add_ellipsis { + while i < $dst.len() - $start { + $dst[i + $start] = b'.'; + i += 1; + } + } + + i + }}; +} + +macro_rules! const_write_all { + ($dst:expr, $src_arr:expr, $start:expr) => {{ + let mut offset = $start; + + let mut i = 0; + while i < $src_arr.len() && offset < $dst.len() { + offset += const_arr_copy!($dst, $src_arr[i].as_bytes(), offset); + i += 1; + } + + offset - $start + }}; +} + +pub const fn const_str_eq(a: &str, b: &str) -> bool { + let a = a.as_bytes(); + let b = b.as_bytes(); + if a.len() != b.len() { + return false; + } + + let mut i = 0; + while i < a.len() { + if a[i] != b[i] { + return false; + } + + i += 1; + } + + true +} + +pub const fn const_slice_eq(a: &[&str], b: &[&str]) -> bool { + if a.len() != b.len() { + return false; + } + + let mut i = 0; + while i < a.len() { + if !const_str_eq(a[i], b[i]) { + return false; + } + + i += 1; + } + + true +} + +pub const fn const_slice_to_str(s: &[u8], len: usize) -> &str { + match str::from_utf8(s.split_at(len).0) { + Ok(v) => v, + Err(_e) => panic!("utf8 error"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_arr_copy() { + let mut x = [0u8; 20]; + let w1 = const_arr_copy!(x, b"foobar", 0); + let s = const_slice_to_str(x.as_slice(), w1); + assert_eq!(s, "foobar"); + + let w2 = const_arr_copy!(x, b"foobar", w1); + let s = const_slice_to_str(x.as_slice(), w1 + w2); + assert_eq!(s, "foobarfoobar"); + + let mut x = [0u8; 6]; + let written = const_arr_copy!(x, b"foobar", 0); + let s = const_slice_to_str(x.as_slice(), written); + assert_eq!(s, "foobar"); + + let mut x = [0u8; 5]; + let written = const_arr_copy!(x, b"foobar", 0); + let s = const_slice_to_str(x.as_slice(), written); + assert_eq!(s, "fo..."); + } + + #[test] + fn test_const_write_all() { + let mut x = [0u8; 20]; + let w1 = const_write_all!(x, ["foo", "bar", "baz"], 0); + let s = const_slice_to_str(x.as_slice(), w1); + assert_eq!(s, "foobarbaz"); + } +} diff --git a/udf/src/wrapper/functions.rs b/udf/src/wrapper/functions.rs index edeb508..6c86966 100644 --- a/udf/src/wrapper/functions.rs +++ b/udf/src/wrapper/functions.rs @@ -94,7 +94,7 @@ impl UdfConverter for U { /// # Interface /// /// Based on the SQL UDF spec, we need to perform the following here: -/// - Verify the number of arguments to XXX() (handled by `U::init`) +/// - Verify the number of arguments to `XXX()` (handled by `U::init`) /// - Verify that the arguments are of a required type or, alternatively, to /// tell `MySQL` to coerce arguments to the required types when the main /// function is called. (handled by `U::init`) diff --git a/udf/src/wrapper/modded_types.rs b/udf/src/wrapper/modded_types.rs index c49b40b..e879014 100644 --- a/udf/src/wrapper/modded_types.rs +++ b/udf/src/wrapper/modded_types.rs @@ -10,7 +10,7 @@ pub struct UDF_ARGSx { /// Number of arguments present pub arg_count: ::std::ffi::c_uint, - /// Buffer of item_result pointers that indicate argument type + /// Buffer of `item_result` pointers that indicate argument type /// /// Remains mutable because it can be set in `xxx_init` pub arg_types: *mut ::std::ffi::c_int, diff --git a/udf/src/wrapper/tests.rs b/udf/src/wrapper/tests.rs index 02c121d..e99a9c0 100644 --- a/udf/src/wrapper/tests.rs +++ b/udf/src/wrapper/tests.rs @@ -159,3 +159,58 @@ fn test_wrapper_bufwrapper() { wrap_init::(todo!(), todo!(), todo!()); } } + +#[test] +fn test_verify_aggregate_attributes() { + struct Foo; + impl RegisteredBasicUdf for Foo { + const NAME: &'static str = "foo"; + const ALIASES: &'static [&'static str] = &["foo", "bar"]; + const DEFAULT_NAME_USED: bool = false; + } + impl RegisteredAggregateUdf for Foo { + const NAME: &'static str = "foo"; + const ALIASES: &'static [&'static str] = &["foo", "bar"]; + const DEFAULT_NAME_USED: bool = false; + } + + verify_aggregate_attributes::(); +} + +#[test] +#[should_panic = "#[register]` on `BasicUdf` and `AggregateUdf` must have the same `name` \ + argument; got `foo` and `bar`"] +fn test_verify_aggregate_attributes_mismatch_name() { + struct Foo; + impl RegisteredBasicUdf for Foo { + const NAME: &'static str = "foo"; + const ALIASES: &'static [&'static str] = &["foo", "bar"]; + const DEFAULT_NAME_USED: bool = false; + } + impl RegisteredAggregateUdf for Foo { + const NAME: &'static str = "bar"; + const ALIASES: &'static [&'static str] = &["foo", "bar"]; + const DEFAULT_NAME_USED: bool = false; + } + + verify_aggregate_attributes::(); +} + +#[test] +#[should_panic = "`#[register]` on `BasicUdf` and `AggregateUdf` must have the same `alias` \ + arguments; got [`foo`, `bar`, `baz`] and [`foo`, `bar`]"] +fn test_verify_aggregate_attributes_mismatch_aliases() { + struct Foo; + impl RegisteredBasicUdf for Foo { + const NAME: &'static str = "foo"; + const ALIASES: &'static [&'static str] = &["foo", "bar", "baz"]; + const DEFAULT_NAME_USED: bool = false; + } + impl RegisteredAggregateUdf for Foo { + const NAME: &'static str = "foo"; + const ALIASES: &'static [&'static str] = &["foo", "bar"]; + const DEFAULT_NAME_USED: bool = false; + } + + verify_aggregate_attributes::(); +}