add currency
This commit is contained in:
parent
49b05d0876
commit
7e76fc4008
604
Cargo.lock
generated
604
Cargo.lock
generated
@ -2,6 +2,12 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
@ -17,6 +23,12 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bnum"
|
name = "bnum"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -29,6 +41,16 @@ version = "3.17.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.49"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -52,12 +74,36 @@ dependencies = [
|
|||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"fastnum",
|
"fastnum",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
|
"ureq",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-time",
|
"web-time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "displaydoc"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastnum"
|
name = "fastnum"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
@ -68,6 +114,150 @@ dependencies = [
|
|||||||
"bnum",
|
"bnum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "form_urlencoded"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_collections"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"potential_utf",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locale_core"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"litemap",
|
||||||
|
"tinystr",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
|
||||||
|
dependencies = [
|
||||||
|
"icu_collections",
|
||||||
|
"icu_normalizer_data",
|
||||||
|
"icu_properties",
|
||||||
|
"icu_provider",
|
||||||
|
"smallvec",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer_data"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_properties"
|
||||||
|
version = "2.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
|
||||||
|
dependencies = [
|
||||||
|
"icu_collections",
|
||||||
|
"icu_locale_core",
|
||||||
|
"icu_properties_data",
|
||||||
|
"icu_provider",
|
||||||
|
"zerotrie",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_properties_data"
|
||||||
|
version = "2.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_provider"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_locale_core",
|
||||||
|
"writeable",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerotrie",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||||
|
dependencies = [
|
||||||
|
"idna_adapter",
|
||||||
|
"smallvec",
|
||||||
|
"utf8_iter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna_adapter"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||||
|
dependencies = [
|
||||||
|
"icu_normalizer",
|
||||||
|
"icu_properties",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.77"
|
version = "0.3.77"
|
||||||
@ -78,6 +268,18 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.178"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litemap"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.27"
|
version = "0.4.27"
|
||||||
@ -90,12 +292,37 @@ version = "2.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.3"
|
version = "1.21.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "potential_utf"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
|
||||||
|
dependencies = [
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.95"
|
||||||
@ -143,12 +370,140 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom",
|
||||||
|
"libc",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.145"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.101"
|
version = "2.0.101"
|
||||||
@ -160,6 +515,27 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinystr"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@ -172,6 +548,54 @@ version = "1.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ureq"
|
||||||
|
version = "2.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"flate2",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"url",
|
||||||
|
"webpki-roots 0.26.11",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "url"
|
||||||
|
version = "2.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"idna",
|
||||||
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8_iter"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.100"
|
version = "0.2.100"
|
||||||
@ -239,3 +663,183 @@ dependencies = [
|
|||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.26.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||||
|
dependencies = [
|
||||||
|
"webpki-roots 1.0.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "writeable"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yoke"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
|
||||||
|
dependencies = [
|
||||||
|
"stable_deref_trait",
|
||||||
|
"yoke-derive",
|
||||||
|
"zerofrom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yoke-derive"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerofrom"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||||
|
dependencies = [
|
||||||
|
"zerofrom-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerofrom-derive"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerotrie"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec"
|
||||||
|
version = "0.11.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
|
||||||
|
dependencies = [
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec-derive"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|||||||
11
Cargo.toml
11
Cargo.toml
@ -10,6 +10,7 @@ homepage = "https://github.com/probablykasper/cpc#readme"
|
|||||||
repository = "https://github.com/probablykasper/cpc"
|
repository = "https://github.com/probablykasper/cpc"
|
||||||
documentation = "https://docs.rs/cpc"
|
documentation = "https://docs.rs/cpc"
|
||||||
keywords = ["math", "expression", "evaluate", "units", "convert"]
|
keywords = ["math", "expression", "evaluate", "units", "convert"]
|
||||||
|
autobins = false
|
||||||
categories = [
|
categories = [
|
||||||
"mathematics",
|
"mathematics",
|
||||||
"science",
|
"science",
|
||||||
@ -18,6 +19,10 @@ categories = [
|
|||||||
"value-formatting",
|
"value-formatting",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "cpc_cli"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
@ -25,6 +30,12 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
fastnum = "0.2"
|
fastnum = "0.2"
|
||||||
unicode-segmentation = "1.12"
|
unicode-segmentation = "1.12"
|
||||||
web-time = "1.1.0"
|
web-time = "1.1.0"
|
||||||
|
once_cell = "1.20"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
ureq = { version = "2.12", features = ["json"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
console_error_panic_hook = "0.1.7"
|
console_error_panic_hook = "0.1.7"
|
||||||
|
|||||||
145
src/currency.rs
Normal file
145
src/currency.rs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
use crate::extra_units;
|
||||||
|
use crate::units::UnitType;
|
||||||
|
use fastnum::decimal::Context;
|
||||||
|
use fastnum::D128;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
const EXCHANGE_RATE_API_USD_LATEST: &str = "https://api.exchangerate-api.com/v4/latest/USD";
|
||||||
|
|
||||||
|
fn parse_d128_from_json_number(v: &Value) -> Result<D128, String> {
|
||||||
|
let s = match v {
|
||||||
|
Value::Number(n) => n.to_string(),
|
||||||
|
_ => return Err("Expected JSON number".to_string()),
|
||||||
|
};
|
||||||
|
D128::from_str(&s, Context::default())
|
||||||
|
.map_err(|_| format!("Invalid decimal number: {s}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers/updates currencies from the ExchangeRate API JSON response (base USD).
|
||||||
|
///
|
||||||
|
/// The JSON is expected to look like `{"base":"USD","rates":{"EUR":0.852,...}}`.
|
||||||
|
///
|
||||||
|
/// Internally we store a "weight" as USD-per-unit, so conversions use the existing
|
||||||
|
/// `from.weight()/to.weight()` logic.
|
||||||
|
pub fn register_currencies_from_exchange_rate_api_json(json: &str) -> Result<usize, String> {
|
||||||
|
let v: Value = serde_json::from_str(json).map_err(|e| format!("Invalid JSON: {e}"))?;
|
||||||
|
|
||||||
|
let base = v
|
||||||
|
.get("base")
|
||||||
|
.and_then(|b| b.as_str())
|
||||||
|
.ok_or("Missing 'base'")?;
|
||||||
|
if base != "USD" {
|
||||||
|
return Err(format!("Expected base USD but got {base}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let rates = v
|
||||||
|
.get("rates")
|
||||||
|
.and_then(|r| r.as_object())
|
||||||
|
.ok_or("Missing 'rates' object")?;
|
||||||
|
|
||||||
|
let mut count = 0usize;
|
||||||
|
for (code, rate_value) in rates {
|
||||||
|
// Skip weird/empty keys defensively
|
||||||
|
if code.len() != 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let code_upper = code.to_ascii_uppercase();
|
||||||
|
let code_lower = code.to_ascii_lowercase();
|
||||||
|
|
||||||
|
let rate = parse_d128_from_json_number(rate_value)?;
|
||||||
|
if rate.is_zero() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// weight = USD per 1 {CODE} = 1 / (CODE per 1 USD)
|
||||||
|
let weight = D128::ONE / rate;
|
||||||
|
|
||||||
|
extra_units::upsert_unit(
|
||||||
|
&code_lower,
|
||||||
|
&code_upper,
|
||||||
|
&code_upper,
|
||||||
|
UnitType::Currency,
|
||||||
|
weight,
|
||||||
|
);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn cache_path() -> std::path::PathBuf {
|
||||||
|
let mut p = std::env::temp_dir();
|
||||||
|
p.push("cpc_currency_rates_usd.json");
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub fn init_currencies_blocking() -> Result<usize, String> {
|
||||||
|
// 1) Load cached (if any) so currencies work offline.
|
||||||
|
let mut cached_count = 0usize;
|
||||||
|
let cache_path = cache_path();
|
||||||
|
if let Ok(s) = std::fs::read_to_string(&cache_path) {
|
||||||
|
if let Ok(n) = register_currencies_from_exchange_rate_api_json(&s) {
|
||||||
|
cached_count = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Try refresh from network.
|
||||||
|
let resp = ureq::get(EXCHANGE_RATE_API_USD_LATEST)
|
||||||
|
.call()
|
||||||
|
.map_err(|e| format!("Currency fetch failed: {e}"))?;
|
||||||
|
let body = resp
|
||||||
|
.into_string()
|
||||||
|
.map_err(|e| format!("Currency fetch read failed: {e}"))?;
|
||||||
|
|
||||||
|
let n = register_currencies_from_exchange_rate_api_json(&body)?;
|
||||||
|
// Best-effort cache write
|
||||||
|
let _ = std::fs::write(&cache_path, body);
|
||||||
|
|
||||||
|
Ok(std::cmp::max(cached_count, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::extra_units;
|
||||||
|
use crate::units::{convert, Unit};
|
||||||
|
use crate::Number;
|
||||||
|
use fastnum::dec128 as d;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn register_and_convert_currency() {
|
||||||
|
extra_units::clear_for_tests();
|
||||||
|
|
||||||
|
let json = r#"{
|
||||||
|
"base": "USD",
|
||||||
|
"rates": {
|
||||||
|
"USD": 1,
|
||||||
|
"EUR": 0.5
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let n = register_currencies_from_exchange_rate_api_json(json).unwrap();
|
||||||
|
assert_eq!(n, 2);
|
||||||
|
|
||||||
|
let usd = extra_units::lookup_unit("usd").unwrap();
|
||||||
|
let eur = extra_units::lookup_unit("eur").unwrap();
|
||||||
|
|
||||||
|
let one_usd = Number::new(d!(1), usd);
|
||||||
|
let eur_amount = convert(one_usd, eur).unwrap();
|
||||||
|
assert_eq!(eur_amount.value, d!(0.5));
|
||||||
|
|
||||||
|
let one_eur = Number::new(d!(1), eur);
|
||||||
|
let usd_amount = convert(one_eur, usd).unwrap();
|
||||||
|
assert_eq!(usd_amount.value, d!(2));
|
||||||
|
|
||||||
|
// Display name should be currency code
|
||||||
|
assert_eq!(eur_amount.unit.singular(), "EUR");
|
||||||
|
assert_eq!(eur_amount.unit.plural(), "EUR");
|
||||||
|
|
||||||
|
// Sanity: it's still a Unit
|
||||||
|
let _ = Unit::NoUnit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
125
src/extra_units.rs
Normal file
125
src/extra_units.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
use crate::units::{Unit, UnitType};
|
||||||
|
use fastnum::D128;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub(crate) struct ExtraUnitDef {
|
||||||
|
pub(crate) category: UnitType,
|
||||||
|
pub(crate) weight: D128,
|
||||||
|
pub(crate) singular: &'static str,
|
||||||
|
pub(crate) plural: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Registry {
|
||||||
|
by_name: HashMap<&'static str, u16>,
|
||||||
|
units: Vec<ExtraUnitDef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static REGISTRY: Lazy<RwLock<Registry>> = Lazy::new(|| RwLock::new(Registry::default()));
|
||||||
|
|
||||||
|
fn leak_str(s: String) -> &'static str {
|
||||||
|
Box::leak(s.into_boxed_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn lookup_unit(word: &str) -> Option<Unit> {
|
||||||
|
let reg = REGISTRY.read().ok()?;
|
||||||
|
let id = *reg.by_name.get(word)?;
|
||||||
|
Some(Unit::Extra(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn upsert_unit(
|
||||||
|
name_lowercase: &str,
|
||||||
|
singular: &str,
|
||||||
|
plural: &str,
|
||||||
|
category: UnitType,
|
||||||
|
weight: D128,
|
||||||
|
) -> Unit {
|
||||||
|
let mut reg = REGISTRY
|
||||||
|
.write()
|
||||||
|
.expect("extra unit registry lock poisoned");
|
||||||
|
|
||||||
|
if let Some(existing_id) = reg.by_name.get(name_lowercase).copied() {
|
||||||
|
let idx = existing_id as usize;
|
||||||
|
reg.units[idx] = ExtraUnitDef {
|
||||||
|
category,
|
||||||
|
weight,
|
||||||
|
singular: reg.units[idx].singular,
|
||||||
|
plural: reg.units[idx].plural,
|
||||||
|
};
|
||||||
|
return Unit::Extra(existing_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let id: u16 = reg
|
||||||
|
.units
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.expect("too many extra units registered");
|
||||||
|
|
||||||
|
let key = leak_str(name_lowercase.to_string());
|
||||||
|
let singular = leak_str(singular.to_string());
|
||||||
|
let plural = leak_str(plural.to_string());
|
||||||
|
|
||||||
|
reg.by_name.insert(key, id);
|
||||||
|
reg.units.push(ExtraUnitDef {
|
||||||
|
category,
|
||||||
|
weight,
|
||||||
|
singular,
|
||||||
|
plural,
|
||||||
|
});
|
||||||
|
|
||||||
|
Unit::Extra(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_category(id: u16) -> UnitType {
|
||||||
|
let reg = REGISTRY
|
||||||
|
.read()
|
||||||
|
.expect("extra unit registry lock poisoned");
|
||||||
|
reg.units
|
||||||
|
.get(id as usize)
|
||||||
|
.map(|u| u.category)
|
||||||
|
.unwrap_or(UnitType::NoType)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_weight(id: u16) -> D128 {
|
||||||
|
let reg = REGISTRY
|
||||||
|
.read()
|
||||||
|
.expect("extra unit registry lock poisoned");
|
||||||
|
reg.units
|
||||||
|
.get(id as usize)
|
||||||
|
.map(|u| u.weight)
|
||||||
|
.unwrap_or(D128::NAN)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_singular(id: u16) -> &'static str {
|
||||||
|
let reg = REGISTRY
|
||||||
|
.read()
|
||||||
|
.expect("extra unit registry lock poisoned");
|
||||||
|
reg.units
|
||||||
|
.get(id as usize)
|
||||||
|
.map(|u| u.singular)
|
||||||
|
.unwrap_or("")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_plural(id: u16) -> &'static str {
|
||||||
|
let reg = REGISTRY
|
||||||
|
.read()
|
||||||
|
.expect("extra unit registry lock poisoned");
|
||||||
|
reg.units
|
||||||
|
.get(id as usize)
|
||||||
|
.map(|u| u.plural)
|
||||||
|
.unwrap_or("")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn clear_for_tests() {
|
||||||
|
let mut reg = REGISTRY
|
||||||
|
.write()
|
||||||
|
.expect("extra unit registry lock poisoned");
|
||||||
|
reg.by_name.clear();
|
||||||
|
reg.units.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
105
src/lexer.rs
105
src/lexer.rs
@ -10,6 +10,8 @@ use crate::Constant::{E, Pi};
|
|||||||
use crate::LexerKeyword::{In, PercentChar, Per, Mercury, Hg, PoundForce, Force, DoubleQuotes, Revolution};
|
use crate::LexerKeyword::{In, PercentChar, Per, Mercury, Hg, PoundForce, Force, DoubleQuotes, Revolution};
|
||||||
use crate::FunctionIdentifier::{Cbrt, Ceil, Cos, Exp, Abs, Floor, Ln, Log, Round, Sin, Sqrt, Tan};
|
use crate::FunctionIdentifier::{Cbrt, Ceil, Cos, Exp, Abs, Floor, Ln, Log, Round, Sin, Sqrt, Tan};
|
||||||
use crate::units::Unit::*;
|
use crate::units::Unit::*;
|
||||||
|
use crate::extra_units;
|
||||||
|
use crate::units::UnitType;
|
||||||
use unicode_segmentation::{Graphemes, UnicodeSegmentation};
|
use unicode_segmentation::{Graphemes, UnicodeSegmentation};
|
||||||
|
|
||||||
fn is_word_char_str(input: &str) -> bool {
|
fn is_word_char_str(input: &str) -> bool {
|
||||||
@ -591,7 +593,11 @@ fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
|
|||||||
"f" | "fahrenheit" | "fahrenheits" => Token::Unit(Fahrenheit),
|
"f" | "fahrenheit" | "fahrenheits" => Token::Unit(Fahrenheit),
|
||||||
|
|
||||||
string => {
|
string => {
|
||||||
return Err(format!("Invalid string: {}", string));
|
if let Some(unit) = extra_units::lookup_unit(string) {
|
||||||
|
Token::Unit(unit)
|
||||||
|
} else {
|
||||||
|
return Err(format!("Invalid string: {}", string));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
lexer.tokens.push(token);
|
lexer.tokens.push(token);
|
||||||
@ -903,6 +909,103 @@ pub fn lex(input: &str, remove_trailing_operator: bool) -> Result<Vec<Token>, St
|
|||||||
(Token::LexerKeyword(Revolution), Token::LexerKeyword(Per), Token::Unit(Minute)) => {
|
(Token::LexerKeyword(Revolution), Token::LexerKeyword(Per), Token::Unit(Minute)) => {
|
||||||
tokens[token_index-2] = Token::Unit(RevolutionsPerMinute);
|
tokens[token_index-2] = Token::Unit(RevolutionsPerMinute);
|
||||||
},
|
},
|
||||||
|
// currency per {mass|volume|length|area}, for example usd/lb, cny/kg, usd/l, usd/m, usd/m2
|
||||||
|
(Token::Unit(left_u), Token::LexerKeyword(Per), Token::Unit(right_u)) => {
|
||||||
|
let left_cat = left_u.category();
|
||||||
|
let right_cat = right_u.category();
|
||||||
|
if left_cat == UnitType::Currency
|
||||||
|
&& (right_cat == UnitType::Mass
|
||||||
|
|| right_cat == UnitType::Volume
|
||||||
|
|| right_cat == UnitType::Length
|
||||||
|
|| right_cat == UnitType::Area)
|
||||||
|
{
|
||||||
|
let cur_upper = left_u.singular().to_ascii_uppercase();
|
||||||
|
let cur_lower = cur_upper.to_ascii_lowercase();
|
||||||
|
let denom_abbrev: &str = match (*right_u, right_cat) {
|
||||||
|
// Mass
|
||||||
|
(Milligram, UnitType::Mass) => "mg",
|
||||||
|
(Gram, UnitType::Mass) => "g",
|
||||||
|
(Hectogram, UnitType::Mass) => "hg",
|
||||||
|
(Kilogram, UnitType::Mass) => "kg",
|
||||||
|
(MetricTon, UnitType::Mass) => "t",
|
||||||
|
(Ounce, UnitType::Mass) => "oz",
|
||||||
|
(Pound, UnitType::Mass) => "lb",
|
||||||
|
(Stone, UnitType::Mass) => "st",
|
||||||
|
(ShortTon, UnitType::Mass) => "ston",
|
||||||
|
(LongTon, UnitType::Mass) => "lton",
|
||||||
|
|
||||||
|
// Volume
|
||||||
|
(Milliliter, UnitType::Volume) => "ml",
|
||||||
|
(Centiliter, UnitType::Volume) => "cl",
|
||||||
|
(Deciliter, UnitType::Volume) => "dl",
|
||||||
|
(Liter, UnitType::Volume) => "l",
|
||||||
|
(Teaspoon, UnitType::Volume) => "tsp",
|
||||||
|
(Tablespoon, UnitType::Volume) => "tbsp",
|
||||||
|
(FluidOunce, UnitType::Volume) => "floz",
|
||||||
|
(Cup, UnitType::Volume) => "cup",
|
||||||
|
(Pint, UnitType::Volume) => "pt",
|
||||||
|
(Quart, UnitType::Volume) => "qt",
|
||||||
|
(Gallon, UnitType::Volume) => "gal",
|
||||||
|
(OilBarrel, UnitType::Volume) => "bbl",
|
||||||
|
|
||||||
|
// Length
|
||||||
|
(Millimeter, UnitType::Length) => "mm",
|
||||||
|
(Centimeter, UnitType::Length) => "cm",
|
||||||
|
(Decimeter, UnitType::Length) => "dm",
|
||||||
|
(Meter, UnitType::Length) => "m",
|
||||||
|
(Kilometer, UnitType::Length) => "km",
|
||||||
|
(Inch, UnitType::Length) => "in",
|
||||||
|
(Foot, UnitType::Length) => "ft",
|
||||||
|
(Yard, UnitType::Length) => "yd",
|
||||||
|
(Mile, UnitType::Length) => "mi",
|
||||||
|
(Marathon, UnitType::Length) => "marathon",
|
||||||
|
(NauticalMile, UnitType::Length) => "nmi",
|
||||||
|
(LightYear, UnitType::Length) => "ly",
|
||||||
|
(LightSecond, UnitType::Length) => "lightsec",
|
||||||
|
|
||||||
|
// Area
|
||||||
|
(SquareMillimeter, UnitType::Area) => "mm2",
|
||||||
|
(SquareCentimeter, UnitType::Area) => "cm2",
|
||||||
|
(SquareDecimeter, UnitType::Area) => "dm2",
|
||||||
|
(SquareMeter, UnitType::Area) => "m2",
|
||||||
|
(SquareKilometer, UnitType::Area) => "km2",
|
||||||
|
(SquareInch, UnitType::Area) => "in2",
|
||||||
|
(SquareFoot, UnitType::Area) => "ft2",
|
||||||
|
(SquareYard, UnitType::Area) => "yd2",
|
||||||
|
(SquareMile, UnitType::Area) => "mi2",
|
||||||
|
(Are, UnitType::Area) => "a",
|
||||||
|
(Decare, UnitType::Area) => "daa",
|
||||||
|
(Hectare, UnitType::Area) => "ha",
|
||||||
|
(Acre, UnitType::Area) => "acre",
|
||||||
|
|
||||||
|
_ => right_u.singular(),
|
||||||
|
};
|
||||||
|
let display = format!("{}/{}", cur_upper, denom_abbrev);
|
||||||
|
let key = format!("{cur_lower}_per_{denom_abbrev}");
|
||||||
|
// weight = (USD per currency unit) / (base volume-or-mass per denom unit)
|
||||||
|
let weight = left_u.weight() / right_u.weight();
|
||||||
|
let composite_type = match right_cat {
|
||||||
|
UnitType::Mass => UnitType::CurrencyPerMass,
|
||||||
|
UnitType::Volume => UnitType::CurrencyPerVolume,
|
||||||
|
UnitType::Length => UnitType::CurrencyPerLength,
|
||||||
|
UnitType::Area => UnitType::CurrencyPerArea,
|
||||||
|
_ => {
|
||||||
|
replaced = false;
|
||||||
|
UnitType::CurrencyPerMass
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let composite = extra_units::upsert_unit(
|
||||||
|
&key,
|
||||||
|
&display,
|
||||||
|
&display,
|
||||||
|
composite_type,
|
||||||
|
weight,
|
||||||
|
);
|
||||||
|
tokens[token_index-2] = Token::Unit(composite);
|
||||||
|
} else {
|
||||||
|
replaced = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
replaced = false;
|
replaced = false;
|
||||||
},
|
},
|
||||||
|
|||||||
14
src/lib.rs
14
src/lib.rs
@ -34,6 +34,10 @@ pub mod evaluator;
|
|||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
mod lookup;
|
mod lookup;
|
||||||
|
#[rustfmt::skip]
|
||||||
|
mod extra_units;
|
||||||
|
/// Currency rate loading / registration (USD base from exchangerate-api)
|
||||||
|
pub mod currency;
|
||||||
/// Turns [`Token`]s into an [`AstNode`](parser::AstNode)
|
/// Turns [`Token`]s into an [`AstNode`](parser::AstNode)
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
/// Units, and functions you can use with them
|
/// Units, and functions you can use with them
|
||||||
@ -299,6 +303,16 @@ pub fn wasm_eval(expression: &str) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn wasm_set_currency_rates(json: &str) -> String {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
match currency::register_currencies_from_exchange_rate_api_json(json) {
|
||||||
|
Ok(n) => format!("OK ({n} currencies)"),
|
||||||
|
Err(e) => format!("Error: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
use cpc::eval;
|
use cpc::eval;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use cpc::currency;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
@ -60,6 +62,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Best-effort: load currency rates so `usd`, `eur`, etc work as extra units.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
let _ = currency::init_currencies_blocking();
|
||||||
|
}
|
||||||
|
|
||||||
match eval(&expression, true, verbose) {
|
match eval(&expression, true, verbose) {
|
||||||
Ok(answer) => {
|
Ok(answer) => {
|
||||||
if !verbose {
|
if !verbose {
|
||||||
|
|||||||
27
src/units.rs
27
src/units.rs
@ -1,5 +1,6 @@
|
|||||||
use fastnum::{dec128 as d, D128};
|
use fastnum::{dec128 as d, D128};
|
||||||
use crate::Number;
|
use crate::Number;
|
||||||
|
use crate::extra_units;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
/// An enum of all possible unit types, like [`Length`], [`DigitalStorage`] etc.
|
/// An enum of all possible unit types, like [`Length`], [`DigitalStorage`] etc.
|
||||||
@ -39,6 +40,16 @@ pub enum UnitType {
|
|||||||
Speed,
|
Speed,
|
||||||
/// A unit of temperature, for example [`Kelvin`]
|
/// A unit of temperature, for example [`Kelvin`]
|
||||||
Temperature,
|
Temperature,
|
||||||
|
/// A currency unit, for example USD / EUR (loaded dynamically)
|
||||||
|
Currency,
|
||||||
|
/// A currency-per-mass unit, for example USD/lb or CNY/kg
|
||||||
|
CurrencyPerMass,
|
||||||
|
/// A currency-per-volume unit, for example USD/l or EUR/ml
|
||||||
|
CurrencyPerVolume,
|
||||||
|
/// A currency-per-length unit, for example USD/m or EUR/ft
|
||||||
|
CurrencyPerLength,
|
||||||
|
/// A currency-per-area unit, for example USD/m2 or EUR/ft2
|
||||||
|
CurrencyPerArea,
|
||||||
}
|
}
|
||||||
use UnitType::*;
|
use UnitType::*;
|
||||||
|
|
||||||
@ -50,7 +61,9 @@ macro_rules! create_units {
|
|||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
/// A Unit enum. Note that it can also be [`NoUnit`].
|
/// A Unit enum. Note that it can also be [`NoUnit`].
|
||||||
pub enum Unit {
|
pub enum Unit {
|
||||||
$($variant),*
|
$($variant),*,
|
||||||
|
/// Runtime-registered unit (currencies, custom units, etc)
|
||||||
|
Extra(u16)
|
||||||
}
|
}
|
||||||
use Unit::*;
|
use Unit::*;
|
||||||
|
|
||||||
@ -59,28 +72,32 @@ macro_rules! create_units {
|
|||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
Unit::$variant => $properties.0
|
Unit::$variant => $properties.0
|
||||||
),*
|
),*,
|
||||||
|
Unit::Extra(id) => extra_units::get_category(*id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn weight(&self) -> D128 {
|
pub fn weight(&self) -> D128 {
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
Unit::$variant => $properties.1
|
Unit::$variant => $properties.1
|
||||||
),*
|
),*,
|
||||||
|
Unit::Extra(id) => extra_units::get_weight(*id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn singular(&self) -> &str {
|
pub(crate) fn singular(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
Unit::$variant => $properties.2
|
Unit::$variant => $properties.2
|
||||||
),*
|
),*,
|
||||||
|
Unit::Extra(id) => extra_units::get_singular(*id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn plural(&self) -> &str {
|
pub(crate) fn plural(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
Unit::$variant => $properties.3
|
Unit::$variant => $properties.3
|
||||||
),*
|
),*,
|
||||||
|
Unit::Extra(id) => extra_units::get_plural(*id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user