diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..2e07606 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.wasm32-unknown-unknown] +rustflags = ['--cfg', 'getrandom_backend="wasm_js"'] diff --git a/Cargo.lock b/Cargo.lock index 1691f66..a07fcda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,12 +41,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - [[package]] name = "autocfg" version = "1.5.0" @@ -84,9 +78,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.61" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "shlex", @@ -257,13 +251,13 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "dashmap" -version = "6.1.0" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" dependencies = [ "cfg-if", "crossbeam-utils", - "hashbrown 0.14.5", + "hashbrown", "lock_api", "once_cell", "parking_lot_core", @@ -275,24 +269,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "futures-core" version = "0.3.32" @@ -319,15 +301,16 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", - "wasip3", + "wasm-bindgen", ] [[package]] @@ -347,27 +330,6 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "iana-time-zone" version = "0.1.65" @@ -392,24 +354,6 @@ dependencies = [ "cc", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", -] - [[package]] name = "itertools" version = "0.13.0" @@ -437,12 +381,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" version = "0.2.186" @@ -451,17 +389,18 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libhaystack" -version = "3.1.6" +version = "3.2.0" dependencies = [ "chrono", "chrono-tz", "console_error_panic_hook", "criterion", "dashmap", + "getrandom", "regex", "serde", "serde_json", - "uuid", + "ulid", "wasm-bindgen", "web-sys", ] @@ -593,13 +532,12 @@ dependencies = [ ] [[package]] -name = "prettyplease" -version = "0.2.37" +name = "ppv-lite86" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "proc-macro2", - "syn", + "zerocopy", ] [[package]] @@ -622,9 +560,38 @@ dependencies = [ [[package]] name = "r-efi" -version = "6.0.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom", +] [[package]] name = "rayon" @@ -705,12 +672,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - [[package]] name = "serde" version = "1.0.228" @@ -800,16 +761,20 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.24" +name = "ulid" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" +dependencies = [ + "rand", + "web-time", +] [[package]] -name = "unicode-xid" -version = "0.2.6" +name = "unicode-ident" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unit-gen" @@ -819,17 +784,6 @@ dependencies = [ "pom", ] -[[package]] -name = "uuid" -version = "1.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" -dependencies = [ - "getrandom", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "walkdir" version = "2.5.0" @@ -846,16 +800,7 @@ version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] @@ -904,44 +849,20 @@ dependencies = [ ] [[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" +name = "web-sys" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "web-sys" -version = "0.3.98" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -1046,100 +967,12 @@ dependencies = [ "windows-link", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "zerocopy" version = "0.8.48" diff --git a/Cargo.toml b/Cargo.toml index 5588a2b..417d548 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libhaystack" -version = "3.1.6" +version = "3.2.0" description = "Rust implementation of the Haystack 4 data types, defs, filter, units, and encodings" authors = ["J2 Innovations", "Radu Racariu "] edition = "2024" @@ -73,7 +73,8 @@ trio-decoding = ["zinc-decoding"] [dependencies] serde = "1.0" serde_json = "1.0" -uuid = { version = "1.2", features = ["v4"] } +ulid = "1.2.1" +getrandom = "0.3" chrono = "0.4" chrono-tz = "0.10" regex = "1.12" @@ -84,6 +85,7 @@ criterion = "0.8" [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" -uuid = { version = "1.2", features = ["v4", "js"] } +ulid = "1.2.1" +getrandom = { version = "0.3", features = ["wasm_js"] } web-sys = { version = "0.3", features = ["console"] } console_error_panic_hook = "0.1" diff --git a/src/haystack/encoding/json/decode.rs b/src/haystack/encoding/json/decode.rs index 3417566..aabc3aa 100644 --- a/src/haystack/encoding/json/decode.rs +++ b/src/haystack/encoding/json/decode.rs @@ -42,7 +42,7 @@ impl<'de> Deserialize<'de> for Remove { } } -/// Hayson Remove deserializer +/// Hayson Na deserializer impl<'de> Deserialize<'de> for Na { fn deserialize>(deserializer: D) -> Result { let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; @@ -54,137 +54,36 @@ impl<'de> Deserialize<'de> for Na { } } -/// Hayson Number deserializer -impl<'de> Deserialize<'de> for Number { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Number(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Number")), - } - } -} - -/// Hayson Date deserializer -impl<'de> Deserialize<'de> for Date { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Date(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Date")), - } - } -} - -/// Hayson Time deserializer -impl<'de> Deserialize<'de> for Time { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Time(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Time")), - } - } -} - -/// Hayson DateTime deserializer -impl<'de> Deserialize<'de> for DateTime { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::DateTime(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson DateTime")), - } - } -} - -/// Hayson Ref deserializer -impl<'de> Deserialize<'de> for Ref { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Ref(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Ref")), - } - } -} - -/// Hayson Uri deserializer -impl<'de> Deserialize<'de> for Uri { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Uri(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Uri")), - } - } -} - -/// Hayson Symbol deserializer -impl<'de> Deserialize<'de> for Symbol { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Symbol(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Symbol")), - } - } -} - -/// Hayson Str deserializer -impl<'de> Deserialize<'de> for Str { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Str(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Str")), - } - } -} - -/// Hayson Coord deserializer -impl<'de> Deserialize<'de> for Coord { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Coord(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Coord")), - } - } -} - -/// Hayson XStr deserializer -impl<'de> Deserialize<'de> for XStr { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::XStr(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson XStr")), - } - } -} - -/// Hayson XStr deserializer -impl<'de> Deserialize<'de> for Dict { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Dict(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Dict")), +/// Generates a `Deserialize` impl for a Haystack value type whose JSON +/// representation is a Hayson object with a matching `_kind` field. +/// The macro avoids repeating the identical boilerplate for every typed +/// deserializer: deserialize via the universal `JsonValueDecoderVisitor`, +/// then destructure the expected `HVal` variant. +macro_rules! impl_hayson_deserialize { + ($type:ident, $err:literal) => { + impl<'de> Deserialize<'de> for $type { + fn deserialize>(deserializer: D) -> Result<$type, D::Error> { + match deserializer.deserialize_any(JsonValueDecoderVisitor)? { + HVal::$type(inner) => Ok(inner), + _ => Err(D::Error::custom($err)), + } + } } - } + }; } -/// Hayson Grid deserializer -impl<'de> Deserialize<'de> for Grid { - fn deserialize>(deserializer: D) -> Result { - let val = deserializer.deserialize_any(JsonValueDecoderVisitor)?; - match val { - HVal::Grid(val) => Ok(val), - _ => Err(D::Error::custom("Invalid Hayson Grid")), - } - } -} +impl_hayson_deserialize!(Number, "Invalid Hayson Number"); +impl_hayson_deserialize!(Date, "Invalid Hayson Date"); +impl_hayson_deserialize!(Time, "Invalid Hayson Time"); +impl_hayson_deserialize!(DateTime, "Invalid Hayson DateTime"); +impl_hayson_deserialize!(Ref, "Invalid Hayson Ref"); +impl_hayson_deserialize!(Uri, "Invalid Hayson Uri"); +impl_hayson_deserialize!(Symbol, "Invalid Hayson Symbol"); +impl_hayson_deserialize!(Str, "Invalid Hayson Str"); +impl_hayson_deserialize!(Coord, "Invalid Hayson Coord"); +impl_hayson_deserialize!(XStr, "Invalid Hayson XStr"); +impl_hayson_deserialize!(Dict, "Invalid Hayson Dict"); +impl_hayson_deserialize!(Grid, "Invalid Hayson Grid"); /// Hayson deserializer impl<'de> Deserialize<'de> for HVal { diff --git a/src/haystack/encoding/zinc/encode.rs b/src/haystack/encoding/zinc/encode.rs index 6ae9a3e..4758a44 100644 --- a/src/haystack/encoding/zinc/encode.rs +++ b/src/haystack/encoding/zinc/encode.rs @@ -75,14 +75,14 @@ impl Display for Error { impl std::error::Error for Error {} impl From for Error { - fn from(_: std::fmt::Error) -> Self { - Error::from("Format error.") + fn from(err: std::fmt::Error) -> Self { + Error::Message(err.to_string()) } } impl From for Error { - fn from(_: std::io::Error) -> Self { - Error::from("IO error.") + fn from(err: std::io::Error) -> Self { + Error::Message(err.to_string()) } } @@ -93,8 +93,8 @@ impl From<&str> for Error { } impl From for Error { - fn from(_: std::string::FromUtf8Error) -> Self { - Error::from("Utf8 encoding error.") + fn from(err: std::string::FromUtf8Error) -> Self { + Error::Message(err.to_string()) } } diff --git a/src/haystack/val.rs b/src/haystack/val.rs index 4d7cfd8..9676a5e 100644 --- a/src/haystack/val.rs +++ b/src/haystack/val.rs @@ -68,6 +68,12 @@ //! ``` //! +/// Error type returned by [`std::convert::TryFrom`] conversions on Haystack value types. +/// +/// This is a named alias for `&'static str` today; it can be evolved into a +/// structured type in a future release without touching every call-site. +pub type ConversionError = &'static str; + pub mod boolean; pub mod coord; pub mod date; diff --git a/src/haystack/val/boolean.rs b/src/haystack/val/boolean.rs index 833446e..2adf136 100644 --- a/src/haystack/val/boolean.rs +++ b/src/haystack/val/boolean.rs @@ -2,7 +2,7 @@ //! Haystack Bool -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; /// Haystack `Bool` /// @@ -63,7 +63,7 @@ impl From for Value { /// Tries to convert from `Bool` `Value` to a `bool` impl TryFrom<&Value> for bool { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Bool(v) => Ok(v.value), @@ -74,7 +74,7 @@ impl TryFrom<&Value> for bool { /// Tries to convert from `Bool` `Value` to a `Bool` impl TryFrom<&Value> for Bool { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Bool(v) => Ok(*v), diff --git a/src/haystack/val/coord.rs b/src/haystack/val/coord.rs index 716f03f..eab9955 100644 --- a/src/haystack/val/coord.rs +++ b/src/haystack/val/coord.rs @@ -2,7 +2,7 @@ //! Haystack Coord -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; use std::{ cmp::Ordering, convert::{From, TryFrom}, @@ -45,7 +45,7 @@ impl From for Value { /// Tries to convert from `Value` to a `Coord` impl TryFrom<&Value> for Coord { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Coord(v) => Ok(*v), diff --git a/src/haystack/val/date.rs b/src/haystack/val/date.rs index 81458f3..cba0238 100644 --- a/src/haystack/val/date.rs +++ b/src/haystack/val/date.rs @@ -2,7 +2,7 @@ //! Haystack Date -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; use chrono::NaiveDate; use std::cmp::Ordering; use std::fmt::{Debug, Display, Formatter}; @@ -103,7 +103,7 @@ impl From for Value { /// Tries to convert from `Value` to a `Date` impl TryFrom<&Value> for Date { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Date(v) => Ok(*v), diff --git a/src/haystack/val/datetime.rs b/src/haystack/val/datetime.rs index fb8c9fe..644d58d 100644 --- a/src/haystack/val/datetime.rs +++ b/src/haystack/val/datetime.rs @@ -2,7 +2,7 @@ //! Haystack DateTime -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; use crate::timezone::{ DateTimeType, is_utc, make_date_time, make_date_time_with_tz, timezone_short_name, utc_now, }; @@ -108,7 +108,12 @@ impl FromStr for DateTime { impl Display for DateTime { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - Debug::fmt(&self.value, f) + write!( + f, + "{}{}", + self.naive_local().format("%Y-%m-%dT%H:%M:%S"), + self.timezone_short_name() + ) } } @@ -171,7 +176,7 @@ impl From> for DateTime { /// Tries to convert from `Value` to a `DateTime` impl TryFrom<&Value> for DateTime { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::DateTime(v) => Ok(*v), diff --git a/src/haystack/val/dict.rs b/src/haystack/val/dict.rs index 34fd2ff..1f76f3d 100644 --- a/src/haystack/val/dict.rs +++ b/src/haystack/val/dict.rs @@ -58,8 +58,8 @@ pub trait HaystackDict { /// Get the optional `id` of this `Dict` fn id(&self) -> Option<&Ref>; - /// Get the `id` Ref of this `Dict`, or a default Ref if the id is not present - fn safe_id(&self) -> Ref; + /// Get the `id` Ref of this `Dict`, or a default empty `Ref` if the id is not present + fn safe_id(&self) -> &Ref; /// Get the optional `mod` of this `Dict`. /// On record `Dict`s this represents the last time this @@ -82,46 +82,46 @@ pub trait HaystackDict { fn has_remove(&self, key: &str) -> bool; /// Get optional Bool for the key - fn get_bool<'a>(&'a self, key: &str) -> Option<&'a Bool>; + fn get_bool(&self, key: &str) -> Option<&Bool>; /// Get optional Number for the key - fn get_num<'a>(&'a self, key: &str) -> Option<&'a Number>; + fn get_num(&self, key: &str) -> Option<&Number>; /// Get optional Ref for the key - fn get_ref<'a>(&'a self, key: &str) -> Option<&'a Ref>; + fn get_ref(&self, key: &str) -> Option<&Ref>; /// Get optional Str for the key - fn get_str<'a>(&'a self, key: &str) -> Option<&'a Str>; + fn get_str(&self, key: &str) -> Option<&Str>; /// Get optional XStr for the key - fn get_xstr<'a>(&'a self, key: &str) -> Option<&'a XStr>; + fn get_xstr(&self, key: &str) -> Option<&XStr>; /// Get optional Uri for the key - fn get_uri<'a>(&'a self, key: &str) -> Option<&'a Uri>; + fn get_uri(&self, key: &str) -> Option<&Uri>; /// Get optional Symbol for the key - fn get_symbol<'a>(&'a self, key: &str) -> Option<&'a Symbol>; + fn get_symbol(&self, key: &str) -> Option<&Symbol>; /// Get optional Date for the key - fn get_date<'a>(&'a self, key: &str) -> Option<&'a Date>; + fn get_date(&self, key: &str) -> Option<&Date>; /// Get optional Time for the key - fn get_time<'a>(&'a self, key: &str) -> Option<&'a Time>; + fn get_time(&self, key: &str) -> Option<&Time>; /// Get optional DateTime for the key - fn get_date_time<'a>(&'a self, key: &str) -> Option<&'a DateTime>; + fn get_date_time(&self, key: &str) -> Option<&DateTime>; /// Get optional Coord for the key - fn get_coord<'a>(&'a self, key: &str) -> Option<&'a Coord>; + fn get_coord(&self, key: &str) -> Option<&Coord>; /// Get optional Dict for the key - fn get_dict<'a>(&'a self, key: &str) -> Option<&'a Dict>; + fn get_dict(&self, key: &str) -> Option<&Dict>; /// Get optional List for the key - fn get_list<'a>(&'a self, key: &str) -> Option<&'a List>; + fn get_list(&self, key: &str) -> Option<&List>; /// Get optional Grid for the key - fn get_grid<'a>(&'a self, key: &str) -> Option<&'a Grid>; + fn get_grid(&self, key: &str) -> Option<&Grid>; /// Get a formatted display string for a dict. fn dis(&self) -> Cow<'_, str>; @@ -641,8 +641,8 @@ impl HaystackDict for Dict { self.get_ref("id") } - fn safe_id(&self) -> Ref { - self.get_ref("id").map_or(Ref::default(), |id| id.clone()) + fn safe_id(&self) -> &Ref { + self.get_ref("id").unwrap_or(&EMPTY_REF) } fn ts(&self) -> Option<&DateTime> { diff --git a/src/haystack/val/grid.rs b/src/haystack/val/grid.rs index 377c786..49f7510 100644 --- a/src/haystack/val/grid.rs +++ b/src/haystack/val/grid.rs @@ -3,9 +3,9 @@ //! Haystack Grid use crate::dict; -use crate::haystack::val::Value; use crate::haystack::val::dict::*; -use std::collections::HashSet; +use crate::haystack::val::{ConversionError, Value}; +use std::collections::BTreeSet; use std::iter::Iterator; use std::ops::Index; @@ -79,22 +79,12 @@ impl Grid { /// Constructs a Grid from a list of `Dict`s pub fn make_from_dicts(rows: Vec) -> Self { - let mut col_names = HashSet::::new(); - - rows.iter().for_each(|el| { - el.keys().for_each(|col_name| { - col_names.insert(col_name.clone()); - }) - }); - - let mut columns: Vec = col_names - .iter() - .map(|col_name| Column { - name: col_name.clone(), - meta: None, - }) + let col_names: BTreeSet = rows.iter().flat_map(|el| el.keys().cloned()).collect(); + + let columns: Vec = col_names + .into_iter() + .map(|name| Column { name, meta: None }) .collect(); - columns.sort_by(|a, b| a.name.cmp(&b.name)); Grid { meta: None, @@ -226,7 +216,7 @@ impl From for Value { /// Tries to convert from `Value` to a `Grid` impl TryFrom<&Value> for Grid { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Grid(v) => Ok(v.clone()), diff --git a/src/haystack/val/list.rs b/src/haystack/val/list.rs index 5487e84..f65b9dc 100644 --- a/src/haystack/val/list.rs +++ b/src/haystack/val/list.rs @@ -2,7 +2,7 @@ //! Haystack List -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; use std::vec::Vec; /// A Haystack List of `Value`s @@ -28,7 +28,7 @@ impl From for Value { /// Tries to convert from `Value` to a `List` impl TryFrom<&Value> for List { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::List(v) => Ok(v.clone()), diff --git a/src/haystack/val/marker.rs b/src/haystack/val/marker.rs index e4ab6d1..9c81fab 100644 --- a/src/haystack/val/marker.rs +++ b/src/haystack/val/marker.rs @@ -2,7 +2,7 @@ //! Haystack Marker -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; /// Haystack `Marker` /// @@ -28,7 +28,7 @@ impl From for Value { /// Tries to convert from `Marker` `Value` to a `Marker` impl TryFrom<&Value> for Marker { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Marker => Ok(Marker), diff --git a/src/haystack/val/na.rs b/src/haystack/val/na.rs index 5d66101..7ca6f10 100644 --- a/src/haystack/val/na.rs +++ b/src/haystack/val/na.rs @@ -2,7 +2,7 @@ //! Haystack NA -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; /// Haystack `NA` /// @@ -28,7 +28,7 @@ impl From for Value { /// Tries to convert from `Na` `Value` to a `Na` impl TryFrom<&Value> for Na { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Na => Ok(Na), diff --git a/src/haystack/val/number.rs b/src/haystack/val/number.rs index a980ae4..40d22b7 100644 --- a/src/haystack/val/number.rs +++ b/src/haystack/val/number.rs @@ -2,7 +2,11 @@ //! Haystack Number -use crate::{haystack::val::Value, units::DEFAULT_UNIT, units::Unit}; +use crate::{ + haystack::val::{ConversionError, Value}, + units::DEFAULT_UNIT, + units::Unit, +}; use std::{ cmp::Ordering, convert::{From, TryFrom}, @@ -97,7 +101,7 @@ impl From for Value { /// Tries to convert from `Number` `Value` to a `f64` impl TryFrom<&Value> for f64 { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Number(v) => Ok(v.value), @@ -108,7 +112,7 @@ impl TryFrom<&Value> for f64 { /// Tries to convert from `Number` `Value` to a `Number` impl TryFrom<&Value> for Number { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Number(v) => Ok(*v), diff --git a/src/haystack/val/reference.rs b/src/haystack/val/reference.rs index 49334f7..75b6b75 100644 --- a/src/haystack/val/reference.rs +++ b/src/haystack/val/reference.rs @@ -2,11 +2,17 @@ //! Haystack Ref -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; use std::cmp::{Eq, Ord, Ordering, PartialOrd}; use std::fmt; use std::hash::Hash; -use uuid::Uuid; +use ulid::Ulid; + +/// An empty `Ref` value +pub static EMPTY_REF: Ref = Ref { + value: String::new(), + dis: None, +}; /// Haystack `Ref` /// @@ -37,11 +43,10 @@ impl Ref { } } - /// Generate a new Ref based on a V4 UUID + /// Generate a new Ref based on a Ulid pub fn generate() -> Ref { - let uuid = Uuid::new_v4().as_simple().to_string(); Ref { - value: format!("{start}-{end}", start = &uuid[0..8], end = &uuid[26..]), + value: Ulid::new().to_string(), dis: None, } } @@ -106,7 +111,7 @@ impl From for Value { /// Tries to convert from `Value` to a `Ref` impl TryFrom<&Value> for Ref { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Ref(v) => Ok(v.clone()), diff --git a/src/haystack/val/remove.rs b/src/haystack/val/remove.rs index 0c2b7cb..9da56b0 100644 --- a/src/haystack/val/remove.rs +++ b/src/haystack/val/remove.rs @@ -2,7 +2,7 @@ //! Haystack Remove -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; /// Haystack `Remove` /// @@ -28,7 +28,7 @@ impl From for Value { /// Tries to convert from `Remove` `Value` to a `Remove` impl TryFrom<&Value> for Remove { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Remove => Ok(Remove), diff --git a/src/haystack/val/string.rs b/src/haystack/val/string.rs index a45a772..adc3350 100644 --- a/src/haystack/val/string.rs +++ b/src/haystack/val/string.rs @@ -5,7 +5,7 @@ use core::fmt; use std::ops::Deref; -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; /// Haystack `Str` /// @@ -76,7 +76,7 @@ impl From for Value { /// Tries to convert from `Str` `Value` to a `String` impl TryFrom<&Value> for String { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Str(v) => Ok(v.value.clone()), @@ -87,7 +87,7 @@ impl TryFrom<&Value> for String { /// Tries to convert from `Str` `Value` to a `Str` impl TryFrom<&Value> for Str { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Str(v) => Ok(v.clone()), diff --git a/src/haystack/val/symbol.rs b/src/haystack/val/symbol.rs index 51c3fd9..f14180b 100644 --- a/src/haystack/val/symbol.rs +++ b/src/haystack/val/symbol.rs @@ -4,7 +4,7 @@ use std::fmt; -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; /// Haystack `Symbol` /// @@ -55,7 +55,7 @@ impl From for Value { /// Tries to convert from `Value` to a `Symbol` impl TryFrom<&Value> for Symbol { - type Error = &'static str; + type Error = ConversionError; fn try_from(value: &Value) -> Result { match value { Value::Symbol(v) => Ok(v.clone()), diff --git a/src/haystack/val/time.rs b/src/haystack/val/time.rs index 7f8f89e..b75029d 100644 --- a/src/haystack/val/time.rs +++ b/src/haystack/val/time.rs @@ -2,7 +2,7 @@ //! Haystack Time -use crate::haystack::val::Value; +use crate::haystack::val::{ConversionError, Value}; use chrono::NaiveTime; use std::cmp::Ordering; use std::fmt::{Debug, Display, Formatter}; @@ -111,7 +111,7 @@ impl From