add ui
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/.vscode
|
||||||
591
Cargo.lock
generated
591
Cargo.lock
generated
@@ -2,13 +2,604 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cassowary"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "castaway"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "compact_str"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
|
||||||
|
dependencies = [
|
||||||
|
"castaway",
|
||||||
|
"cfg-if",
|
||||||
|
"itoa",
|
||||||
|
"rustversion",
|
||||||
|
"ryu",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.28.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"rustix",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm_winapi"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.20.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.20.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.20.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
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 = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "evalexpr"
|
name = "evalexpr"
|
||||||
version = "13.0.0"
|
version = "13.0.0"
|
||||||
|
source = "git+https://github.com/Rushmore75/evalexpr.git#cb0f504884ee4e70e682a90d6b231c9917b84f4c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"equivalent",
|
||||||
|
"foldhash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "2.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instability"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"indoc",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.177"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.12.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ratatui"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cassowary",
|
||||||
|
"compact_str",
|
||||||
|
"crossterm",
|
||||||
|
"indoc",
|
||||||
|
"instability",
|
||||||
|
"itertools",
|
||||||
|
"lru",
|
||||||
|
"paste",
|
||||||
|
"strum",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-truncate",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sc_rs"
|
name = "sc_rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"evalexpr",
|
"evalexpr",
|
||||||
|
"ratatui",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-mio"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.26.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.110"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-truncate"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width 0.1.14",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
|
[[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-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# evalexpr = "13.0.0"
|
# evalexpr = "13.0.0"
|
||||||
evalexpr = { path='../evalexpr' }
|
evalexpr = { git="https://github.com/Rushmore75/evalexpr.git" }
|
||||||
|
ratatui = "0.29.0"
|
||||||
|
|||||||
343
src/calc.rs
Normal file
343
src/calc.rs
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
use std::{fmt::Display, thread::yield_now};
|
||||||
|
|
||||||
|
use evalexpr::*;
|
||||||
|
use ratatui::{
|
||||||
|
layout::{Constraint, Layout, Rect}, style::{Style, palette::material::WHITE, *}, widgets::{Paragraph, Widget}
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::ctx;
|
||||||
|
|
||||||
|
// if this is very large at all it will overflow the stack
|
||||||
|
const LEN: usize = 100;
|
||||||
|
|
||||||
|
pub struct Grid {
|
||||||
|
// a b c ...
|
||||||
|
// 0
|
||||||
|
// 1
|
||||||
|
// 2
|
||||||
|
// ...
|
||||||
|
cells: [[Option<Box<dyn Cell>>; LEN]; LEN],
|
||||||
|
/// (X, Y)
|
||||||
|
pub selected_cell: (usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Grid {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Grid")
|
||||||
|
.field("cells", &"Too many to print")
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grid {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
// TODO this needs to be moved to the heap
|
||||||
|
let b: [[Option<Box<dyn Cell>>; LEN]; LEN] =
|
||||||
|
core::array::from_fn(|_| core::array::from_fn(|_| None));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
cells: b,
|
||||||
|
selected_cell: (0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, mut eq: &str) -> Option<f64> {
|
||||||
|
if eq.starts_with('=') {
|
||||||
|
eq = &eq[1..];
|
||||||
|
} else {
|
||||||
|
// Should be evaluating an equation
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctx = ctx::CallbackContext::new(&self);
|
||||||
|
// let mut ctx = HashMapContext::<DefaultNumericTypes>::new();
|
||||||
|
|
||||||
|
match eval_with_context(eq, &ctx) {
|
||||||
|
Ok(e) => {
|
||||||
|
let val = e.as_float().expect("Should be float");
|
||||||
|
return Some(val);
|
||||||
|
}
|
||||||
|
Err(e) => match e {
|
||||||
|
EvalexprError::VariableIdentifierNotFound(e) => {
|
||||||
|
// panic!("Will not be able to parse this equation, cell {e} not found")
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
_ => panic!("{}", e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_to_idx(i: &str) -> (usize, usize) {
|
||||||
|
let chars = i
|
||||||
|
.chars()
|
||||||
|
.take_while(|c| c.is_alphabetic())
|
||||||
|
.collect::<Vec<char>>();
|
||||||
|
let nums = i
|
||||||
|
.chars()
|
||||||
|
.skip(chars.len())
|
||||||
|
.take_while(|c| c.is_numeric())
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
// get the x index from the chars
|
||||||
|
let x_idx = chars
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(Self::char_to_idx)
|
||||||
|
.fold(0, |a, b| a + b);
|
||||||
|
|
||||||
|
// get the y index from the numbers
|
||||||
|
let y_idx = nums
|
||||||
|
.parse::<usize>()
|
||||||
|
.expect("Got non-number character after sorting for just numeric characters");
|
||||||
|
|
||||||
|
(x_idx, y_idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cell<T: Into<Box<dyn Cell>>>(&mut self, cell_id: &str, val: T) {
|
||||||
|
let loc = Self::parse_to_idx(cell_id);
|
||||||
|
self.set_cell_raw(loc, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cell_raw<T: Into<Box<dyn Cell>>>(&mut self, (x,y): (usize, usize), val: T) {
|
||||||
|
// TODO check oob
|
||||||
|
self.cells[x][y] = Some(val.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get cells via text like:
|
||||||
|
/// A6,
|
||||||
|
/// F0,
|
||||||
|
/// etc
|
||||||
|
pub fn get_cell(&self, cell_id: &str) -> &Option<Box<dyn Cell>> {
|
||||||
|
let (x, y) = Self::parse_to_idx(cell_id);
|
||||||
|
self.get_cell_raw(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cell_raw(&self, x: usize, y: usize) -> &Option<Box<dyn Cell>> {
|
||||||
|
// TODO check oob
|
||||||
|
&self.cells[x][y]
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function has unit tests
|
||||||
|
fn char_to_idx((idx, c): (usize, &char)) -> usize {
|
||||||
|
(c.to_ascii_lowercase() as usize - 97) + 26 * idx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_to_char(idx: usize) -> String {
|
||||||
|
/*
|
||||||
|
A = 0
|
||||||
|
AA = 26
|
||||||
|
AAA = Not going to worry about it yet
|
||||||
|
*/
|
||||||
|
|
||||||
|
let mut word: [char; 2] = [' '; 2];
|
||||||
|
|
||||||
|
if idx >= 26 {
|
||||||
|
word[0]= ((idx/26) + 65 -1) as u8 as char;
|
||||||
|
}
|
||||||
|
word[1]= ((idx%26) + 65) as u8 as char;
|
||||||
|
|
||||||
|
word.iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Grid {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Cell {
|
||||||
|
/// Important! This is IS NOT the return value of an equation.
|
||||||
|
/// This is the raw equation it's self.
|
||||||
|
fn as_raw_string(&self) -> String;
|
||||||
|
fn can_be_number(&self) -> bool;
|
||||||
|
fn as_num(&self) -> f64;
|
||||||
|
fn is_equation(&self) -> bool {
|
||||||
|
self.as_raw_string().starts_with('=')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for dyn Cell {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|
||||||
|
let disp = if self.can_be_number() {
|
||||||
|
self.as_num().to_string()
|
||||||
|
} else {
|
||||||
|
self.as_raw_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{disp}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cell for f64 {
|
||||||
|
fn as_raw_string(&self) -> String {
|
||||||
|
ToString::to_string(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_be_number(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_num(&self) -> f64 {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Box<dyn Cell>> for f64 {
|
||||||
|
fn into(self) -> Box<dyn Cell> {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Box<dyn Cell>> for String {
|
||||||
|
fn into(self) -> Box<dyn Cell> {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cell for String {
|
||||||
|
fn as_raw_string(&self) -> String {
|
||||||
|
ToString::to_string(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_be_number(&self) -> bool {
|
||||||
|
// checking if the string is an equation
|
||||||
|
self.starts_with('=')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_num(&self) -> f64 {
|
||||||
|
unimplemented!("&str cannot be used in a numeric context")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cells() {
|
||||||
|
let mut grid = Grid::new();
|
||||||
|
|
||||||
|
assert!(&grid.cells[0][0].is_none());
|
||||||
|
grid.set_cell("A0", "Hello".to_string());
|
||||||
|
assert!(grid.get_cell("A0").is_some());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
grid.get_cell("A0").as_ref().unwrap().as_raw_string(),
|
||||||
|
String::from("Hello")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn c_to_i() {
|
||||||
|
assert_eq!(Grid::char_to_idx((0, &'a')), 0);
|
||||||
|
assert_eq!(Grid::char_to_idx((0, &'A')), 0);
|
||||||
|
assert_eq!(Grid::char_to_idx((0, &'z')), 25);
|
||||||
|
assert_eq!(Grid::char_to_idx((0, &'Z')), 25);
|
||||||
|
assert_eq!(Grid::char_to_idx((1, &'a')), 26);
|
||||||
|
|
||||||
|
assert_eq!(Grid::parse_to_idx("A0"), (0, 0));
|
||||||
|
assert_eq!(Grid::parse_to_idx("AA0"), (26, 0));
|
||||||
|
assert_eq!(Grid::parse_to_idx("A1"), (0, 1));
|
||||||
|
assert_eq!(Grid::parse_to_idx("A10"), (0, 10));
|
||||||
|
assert_eq!(Grid::parse_to_idx("Aa10"), (26, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i_to_c() {
|
||||||
|
assert_eq!(Grid::num_to_char(0).trim(), "A");
|
||||||
|
assert_eq!(Grid::num_to_char(25).trim(), "Z");
|
||||||
|
assert_eq!(Grid::num_to_char(26), "AA");
|
||||||
|
assert_eq!(Grid::num_to_char(51), "AZ");
|
||||||
|
assert_eq!(Grid::num_to_char(701), "ZZ");
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &Grid {
|
||||||
|
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) {
|
||||||
|
let len = LEN as u16;
|
||||||
|
|
||||||
|
let cell_height = 1;
|
||||||
|
let cell_length = 5;
|
||||||
|
|
||||||
|
|
||||||
|
let x_max = if area.width / cell_length > len {
|
||||||
|
len - 1
|
||||||
|
} else {
|
||||||
|
area.width / cell_length
|
||||||
|
};
|
||||||
|
let y_max = if area.height / cell_height > len {
|
||||||
|
len - 1
|
||||||
|
} else {
|
||||||
|
area.height / cell_height
|
||||||
|
};
|
||||||
|
|
||||||
|
for x in 0..x_max {
|
||||||
|
for y in 0..y_max {
|
||||||
|
let mut display = String::new();
|
||||||
|
let mut style = Style::new().white();
|
||||||
|
|
||||||
|
const ORANGE1: Color = Color::Rgb(200, 160, 0);
|
||||||
|
const ORANGE2: Color = Color::Rgb(180, 130, 0);
|
||||||
|
|
||||||
|
match (x == 0, y == 0) {
|
||||||
|
(true, true) => {},
|
||||||
|
(true, false) => {
|
||||||
|
// row names
|
||||||
|
display = y.to_string();
|
||||||
|
|
||||||
|
let bg = if y%2==0 {
|
||||||
|
ORANGE1
|
||||||
|
} else {
|
||||||
|
ORANGE2
|
||||||
|
};
|
||||||
|
style = Style::new().fg(Color::White).bg(bg);
|
||||||
|
|
||||||
|
},
|
||||||
|
(false, true) => {
|
||||||
|
// column names
|
||||||
|
display = Grid::num_to_char(x as usize -1);
|
||||||
|
|
||||||
|
let bg = if x%2==0 {
|
||||||
|
ORANGE1
|
||||||
|
} else {
|
||||||
|
ORANGE2
|
||||||
|
};
|
||||||
|
|
||||||
|
style = Style::new().fg(Color::White).bg(bg)
|
||||||
|
},
|
||||||
|
(false, false) => {
|
||||||
|
// minus 1 because of header cells
|
||||||
|
let x_idx = x as usize -1;
|
||||||
|
let y_idx = y as usize -1;
|
||||||
|
|
||||||
|
if let Some(cell) = self.get_cell_raw(x_idx, y_idx) {
|
||||||
|
display = cell.as_raw_string();
|
||||||
|
|
||||||
|
if cell.can_be_number() {
|
||||||
|
if let Some(val) = self.evaluate(&cell.as_raw_string()) {
|
||||||
|
display = val.to_string();
|
||||||
|
} else {
|
||||||
|
// broken formulas
|
||||||
|
if cell.is_equation() {
|
||||||
|
style = Style::new().underline_color(Color::Red).add_modifier(Modifier::UNDERLINED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x_idx, y_idx) == self.selected_cell {
|
||||||
|
style = Style::new().fg(Color::Black).bg(Color::White);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let area = Rect::new(
|
||||||
|
area.x + (x * cell_length),
|
||||||
|
area.y + (y * cell_height),
|
||||||
|
cell_length,
|
||||||
|
cell_height,
|
||||||
|
);
|
||||||
|
|
||||||
|
Paragraph::new(display).style(style).render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/ctx.rs
113
src/ctx.rs
@@ -4,17 +4,16 @@ use evalexpr::{error::EvalexprResultValue, *};
|
|||||||
|
|
||||||
use crate::Grid;
|
use crate::Grid;
|
||||||
|
|
||||||
pub struct CallbackContext<'a, NumericTypes: EvalexprNumericTypes = DefaultNumericTypes> {
|
pub struct CallbackContext<'a, T: EvalexprNumericTypes = DefaultNumericTypes> {
|
||||||
variables: Rc<&'a Grid>,
|
variables: &'a Grid,
|
||||||
functions: HashMap<String, Function<NumericTypes>>,
|
functions: HashMap<String, Function<T>>,
|
||||||
|
|
||||||
/// True if builtin functions are disabled.
|
/// True if builtin functions are disabled.
|
||||||
without_builtin_functions: bool,
|
without_builtin_functions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, NumericTypes: EvalexprNumericTypes> CallbackContext<'a, NumericTypes> {
|
impl<'a, NumericTypes: EvalexprNumericTypes> CallbackContext<'a, NumericTypes> {
|
||||||
/// Constructs a `HashMapContext` with no mappings.
|
pub fn new(grid: &'a Grid) -> Self {
|
||||||
pub fn new(grid: Rc<&'a Grid>) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
variables: grid,
|
variables: grid,
|
||||||
functions: Default::default(),
|
functions: Default::default(),
|
||||||
@@ -22,55 +21,30 @@ impl<'a, NumericTypes: EvalexprNumericTypes> CallbackContext<'a, NumericTypes> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all variables from the context.
|
|
||||||
/// This allows to reuse the context without allocating a new HashMap.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use evalexpr::*;
|
|
||||||
///
|
|
||||||
/// let mut context = HashMapContext::<DefaultNumericTypes>::new();
|
|
||||||
/// context.set_value("abc".into(), "def".into()).unwrap();
|
|
||||||
/// assert_eq!(context.get_value("abc"), Some(&("def".into())));
|
|
||||||
/// context.clear_variables();
|
|
||||||
/// assert_eq!(context.get_value("abc"), None);
|
|
||||||
/// ```
|
|
||||||
pub fn clear_variables(&mut self) {
|
pub fn clear_variables(&mut self) {
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all functions from the context.
|
|
||||||
/// This allows to reuse the context without allocating a new HashMap.
|
|
||||||
pub fn clear_functions(&mut self) {
|
pub fn clear_functions(&mut self) {
|
||||||
self.functions.clear()
|
self.functions.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all variables and functions from the context.
|
|
||||||
/// This allows to reuse the context without allocating a new HashMap.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use evalexpr::*;
|
|
||||||
///
|
|
||||||
/// let mut context = HashMapContext::<DefaultNumericTypes>::new();
|
|
||||||
/// context.set_value("abc".into(), "def".into()).unwrap();
|
|
||||||
/// assert_eq!(context.get_value("abc"), Some(&("def".into())));
|
|
||||||
/// context.clear();
|
|
||||||
/// assert_eq!(context.get_value("abc"), None);
|
|
||||||
/// ```
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.clear_variables();
|
self.clear_variables();
|
||||||
self.clear_functions();
|
self.clear_functions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, NumericTypes: EvalexprNumericTypes> Context for CallbackContext<'a, NumericTypes> {
|
impl<'a> Context for CallbackContext<'a, DefaultNumericTypes> {
|
||||||
type NumericTypes = NumericTypes;
|
type NumericTypes = DefaultNumericTypes;
|
||||||
|
|
||||||
fn get_value(&self, identifier: &str) -> Option<&Value<Self::NumericTypes>> {
|
fn get_value(&self, identifier: &str) -> Option<Value<Self::NumericTypes>> {
|
||||||
return Some(4.);
|
if let Some(v) = self.variables.get_cell(identifier) {
|
||||||
|
if v.can_be_number() {
|
||||||
|
return Some(Value::Float(v.as_num()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_function(
|
fn call_function(
|
||||||
@@ -88,67 +62,8 @@ impl<'a, NumericTypes: EvalexprNumericTypes> Context for CallbackContext<'a, Num
|
|||||||
fn set_builtin_functions_disabled(
|
fn set_builtin_functions_disabled(
|
||||||
&mut self,
|
&mut self,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
) -> EvalexprResult<(), NumericTypes> {
|
) -> EvalexprResult<(), Self::NumericTypes> {
|
||||||
self.without_builtin_functions = disabled;
|
self.without_builtin_functions = disabled;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, NumericTypes: EvalexprNumericTypes> ContextWithMutableVariables
|
|
||||||
for CallbackContext<'a, NumericTypes>
|
|
||||||
{
|
|
||||||
fn set_value(
|
|
||||||
&mut self,
|
|
||||||
identifier: String,
|
|
||||||
value: Value<Self::NumericTypes>,
|
|
||||||
) -> EvalexprResult<(), NumericTypes> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_value(
|
|
||||||
&mut self,
|
|
||||||
identifier: &str,
|
|
||||||
) -> EvalexprResult<Option<Value<Self::NumericTypes>>, Self::NumericTypes> {
|
|
||||||
// Removes a value from the `self.variables`, returning the value at the key if the key was previously in the map.
|
|
||||||
// Ok(self.variables.remove(identifier))
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, NumericTypes: EvalexprNumericTypes> ContextWithMutableFunctions
|
|
||||||
for CallbackContext<'a, NumericTypes>
|
|
||||||
{
|
|
||||||
fn set_function(
|
|
||||||
&mut self,
|
|
||||||
identifier: String,
|
|
||||||
function: Function<NumericTypes>,
|
|
||||||
) -> EvalexprResult<(), Self::NumericTypes> {
|
|
||||||
self.functions.insert(identifier, function);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'b, NumericTypes: EvalexprNumericTypes> IterateVariablesContext for CallbackContext<'b, NumericTypes> {
|
|
||||||
type VariableIterator<'a>
|
|
||||||
= std::iter::Map<
|
|
||||||
std::collections::hash_map::Iter<'a, String, Value<NumericTypes>>,
|
|
||||||
fn((&String, &Value<NumericTypes>)) -> (String, Value<NumericTypes>),
|
|
||||||
>
|
|
||||||
where
|
|
||||||
Self: 'a;
|
|
||||||
type VariableNameIterator<'a>
|
|
||||||
= std::iter::Cloned<std::collections::hash_map::Keys<'a, String, Value<NumericTypes>>>
|
|
||||||
where
|
|
||||||
Self: 'a;
|
|
||||||
|
|
||||||
fn iter_variables(&self) -> Self::VariableIterator<'_> {
|
|
||||||
todo!()
|
|
||||||
// self.variables.iter().map(|(string, value)| (string.clone(), value.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter_variable_names(&self) -> Self::VariableNameIterator<'_> {
|
|
||||||
todo!()
|
|
||||||
// self.variables.keys().cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
357
src/main.rs
357
src/main.rs
@@ -1,205 +1,198 @@
|
|||||||
|
// #![feature(impl_trait_in_bindings)]
|
||||||
|
|
||||||
|
mod calc;
|
||||||
mod ctx;
|
mod ctx;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::io;
|
||||||
|
|
||||||
use evalexpr::*;
|
use ratatui::{
|
||||||
|
crossterm::event,
|
||||||
|
layout::{Constraint, Layout},
|
||||||
|
text::*,
|
||||||
|
widgets::{Paragraph, Widget},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
// if this is very large at all it will overflow the stack
|
use crate::calc::Grid;
|
||||||
const LEN: usize = 100;
|
|
||||||
|
|
||||||
struct Grid {
|
|
||||||
// a b c ...
|
|
||||||
// 0
|
|
||||||
// 1
|
|
||||||
// 2
|
|
||||||
// ...
|
|
||||||
cells: [[Option<Box<dyn Cell>>; LEN]; LEN],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for Grid {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("Grid").field("cells", &"Too many to print").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Grid {
|
|
||||||
fn new() -> Self {
|
|
||||||
let b: [[Option<Box<dyn Cell>>; LEN]; LEN] =
|
|
||||||
core::array::from_fn(|_| core::array::from_fn(|_| None));
|
|
||||||
|
|
||||||
Self { cells: b }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval(&self, mut eq: &str) -> f64 {
|
|
||||||
if eq.starts_with('=') {
|
|
||||||
eq = &eq[1..];
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ctx = ctx::CallbackContext::<DefaultNumericTypes>::new(Rc::new(self));
|
|
||||||
// let mut ctx = HashMapContext::<DefaultNumericTypes>::new();
|
|
||||||
|
|
||||||
let val;
|
|
||||||
loop {
|
|
||||||
match eval_with_context(eq, &ctx) {
|
|
||||||
Ok(e) => {
|
|
||||||
val = e.as_float().expect("Should be float");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
// TODO this is kinda a slow way to do this, the equation will get parsed
|
|
||||||
// multiple times. Might be good to modify the lib so that you can provide
|
|
||||||
// a callback for variables that are not found.
|
|
||||||
EvalexprError::VariableIdentifierNotFound(e) => {
|
|
||||||
panic!("Will not be able to parse this equation, cell {e} not found")
|
|
||||||
}
|
|
||||||
_ => panic!("{}", e),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_to_idx(i: &str) -> (usize, usize) {
|
|
||||||
let chars = i
|
|
||||||
.chars()
|
|
||||||
.take_while(|c| c.is_alphabetic())
|
|
||||||
.collect::<Vec<char>>();
|
|
||||||
let nums = i
|
|
||||||
.chars()
|
|
||||||
.skip(chars.len())
|
|
||||||
.take_while(|c| c.is_numeric())
|
|
||||||
.collect::<String>();
|
|
||||||
|
|
||||||
// get the x index from the chars
|
|
||||||
let x_idx = chars
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(Self::char_to_idx)
|
|
||||||
.fold(0, |a, b| a + b);
|
|
||||||
|
|
||||||
// get the y index from the numbers
|
|
||||||
let y_idx = nums
|
|
||||||
.parse::<usize>()
|
|
||||||
.expect("Got non-number character after sorting for just numeric characters");
|
|
||||||
|
|
||||||
(x_idx, y_idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_cell(&mut self, cell_id: &str, val: Box<dyn Cell>) {
|
|
||||||
let (x, y) = Self::parse_to_idx(cell_id);
|
|
||||||
// TODO check oob
|
|
||||||
self.cells[x][y] = Some(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get cells via text like:
|
|
||||||
/// A6
|
|
||||||
/// F0
|
|
||||||
fn get_cell(&self, cell_id: &str) -> &Option<Box<dyn Cell>> {
|
|
||||||
let (x, y) = Self::parse_to_idx(cell_id);
|
|
||||||
// TODO check oob
|
|
||||||
&self.cells[x][y]
|
|
||||||
}
|
|
||||||
|
|
||||||
// this function has unit tests
|
|
||||||
fn char_to_idx((idx, c): (usize, &char)) -> usize {
|
|
||||||
(c.to_ascii_lowercase() as usize - 97) + 26 * idx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Grid {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Cell {
|
|
||||||
fn to_string(&self) -> String;
|
|
||||||
fn can_be_number(&self) -> bool;
|
|
||||||
fn as_num(&self) -> f32;
|
|
||||||
fn is_eq(&self) -> bool {
|
|
||||||
self.to_string().starts_with('=')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cell for f32 {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
ToString::to_string(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_be_number(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_num(&self) -> f32 {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cell for &str {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
ToString::to_string(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_be_number(&self) -> bool {
|
|
||||||
// checking if the string is an equation
|
|
||||||
self.starts_with('=')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_num(&self) -> f32 {
|
|
||||||
unimplemented!("&str cannot be used in a numeric context")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_math() {
|
fn test_math() {
|
||||||
|
use evalexpr::*;
|
||||||
|
|
||||||
let mut grid = Grid::new();
|
let mut grid = Grid::new();
|
||||||
grid.set_cell("A0", Box::new(2.));
|
grid.set_cell("A0", 2.);
|
||||||
grid.set_cell("B0", Box::new(1.));
|
grid.set_cell("B0", 1.);
|
||||||
grid.set_cell("C0", Box::new("=A0+B0"));
|
grid.set_cell("C0", "=A0+B0".to_string());
|
||||||
|
|
||||||
assert_eq!(eval("1+2").unwrap(), Value::Int(3));
|
assert_eq!(eval("1+2").unwrap(), Value::Int(3));
|
||||||
|
|
||||||
let disp = &grid.get_cell("C0");
|
let cell_text = &grid.get_cell("C0");
|
||||||
if let Some(inner) = disp {
|
if let Some(text) = cell_text {
|
||||||
if inner.is_eq() {
|
if text.is_equation() {
|
||||||
println!("{}", inner.to_string());
|
println!("{}", text.as_raw_string());
|
||||||
let display = grid.eval(&inner.to_string());
|
let display = grid.evaluate(&text.as_raw_string());
|
||||||
assert_eq!(display, 3.);
|
assert_eq!(display, Some(3.));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic!("Should've found the value and returned");
|
panic!("Should've found the value and returned");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn main() -> Result<(), std::io::Error> {
|
||||||
fn test_cells() {
|
let term = ratatui::init();
|
||||||
let mut grid = Grid::new();
|
let mut app = App::new();
|
||||||
|
app.grid.set_cell("A0", 10.);
|
||||||
|
app.grid.set_cell("B1", 10.);
|
||||||
|
app.grid.set_cell("C2", "=A0+B1".to_string());
|
||||||
|
|
||||||
assert!(&grid.cells[0][0].is_none());
|
let res = app.run(term);
|
||||||
grid.set_cell("A0", Box::new("Hello"));
|
ratatui::restore();
|
||||||
assert!(grid.get_cell("A0").is_some());
|
return res;
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
grid.get_cell("A0").as_ref().unwrap().to_string(),
|
|
||||||
String::from("Hello")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
struct App {
|
||||||
fn c_to_i() {
|
exit: bool,
|
||||||
assert_eq!(Grid::char_to_idx((0, &'a')), 0);
|
grid: Grid,
|
||||||
assert_eq!(Grid::char_to_idx((0, &'A')), 0);
|
/// Buffer for key-chords
|
||||||
assert_eq!(Grid::char_to_idx((0, &'z')), 25);
|
chord_buf: String,
|
||||||
assert_eq!(Grid::char_to_idx((0, &'Z')), 25);
|
editor: Option<Editor>,
|
||||||
assert_eq!(Grid::char_to_idx((1, &'a')), 26);
|
|
||||||
|
|
||||||
assert_eq!(Grid::parse_to_idx("A0"), (0, 0));
|
|
||||||
assert_eq!(Grid::parse_to_idx("AA0"), (26, 0));
|
|
||||||
assert_eq!(Grid::parse_to_idx("A1"), (0, 1));
|
|
||||||
assert_eq!(Grid::parse_to_idx("A10"), (0, 10));
|
|
||||||
assert_eq!(Grid::parse_to_idx("Aa10"), (26, 10));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
impl Widget for &App {
|
||||||
println!("Only tests exist atm");
|
fn render(self, area: prelude::Rect, buf: &mut prelude::Buffer) {
|
||||||
|
Paragraph::new("Status").render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
exit: false,
|
||||||
|
grid: Grid::new(),
|
||||||
|
chord_buf: String::new(),
|
||||||
|
editor: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, mut term: DefaultTerminal) -> Result<(), std::io::Error> {
|
||||||
|
while !self.exit {
|
||||||
|
term.draw(|frame| self.draw(frame))?;
|
||||||
|
self.handle_events()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn draw(&self, frame: &mut Frame) {
|
||||||
|
|
||||||
|
let layout = Layout::default()
|
||||||
|
.direction(layout::Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Length(1),
|
||||||
|
Constraint::Min(1),
|
||||||
|
Constraint::Length(1),
|
||||||
|
])
|
||||||
|
.split(frame.area());
|
||||||
|
|
||||||
|
if let Some(editor) = &self.editor {
|
||||||
|
frame.render_widget(editor, layout[0]);
|
||||||
|
} else {
|
||||||
|
frame.render_widget(Paragraph::new("sc_rs"), layout[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.render_widget(&self.grid, layout[1]);
|
||||||
|
frame.render_widget(self, layout[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_events(&mut self) -> io::Result<()> {
|
||||||
|
match event::read()? {
|
||||||
|
event::Event::Key(key_event) => match key_event.code {
|
||||||
|
event::KeyCode::Enter => {
|
||||||
|
if let Some(editor) = &self.editor {
|
||||||
|
let loc= self.grid.selected_cell;
|
||||||
|
|
||||||
|
let val = editor.buf.trim().to_string();
|
||||||
|
|
||||||
|
// insert as number if at all possible
|
||||||
|
if let Ok(val) = val.parse::<f64>() {
|
||||||
|
self.grid.set_cell_raw(loc, val);
|
||||||
|
} else {
|
||||||
|
self.grid.set_cell_raw(loc, val);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.editor = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event::KeyCode::Backspace => {
|
||||||
|
if let Some(editor) = &mut self.editor {
|
||||||
|
editor.buf.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event::KeyCode::F(_) => todo!(),
|
||||||
|
event::KeyCode::Char(c) => {
|
||||||
|
|
||||||
|
if let Some(editor) = &mut self.editor {
|
||||||
|
editor.buf += &c.to_string();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.chord_buf.is_empty() {}
|
||||||
|
|
||||||
|
match c {
|
||||||
|
'q' => self.exit = true,
|
||||||
|
// <
|
||||||
|
'h' => self.grid.selected_cell.0 = self.grid.selected_cell.0.saturating_sub(1),
|
||||||
|
// v
|
||||||
|
'j' => self.grid.selected_cell.1 = self.grid.selected_cell.1.saturating_add(1),
|
||||||
|
// ^
|
||||||
|
'k' => self.grid.selected_cell.1 = self.grid.selected_cell.1.saturating_sub(1),
|
||||||
|
// >
|
||||||
|
'l' => self.grid.selected_cell.0 = self.grid.selected_cell.0.saturating_add(1),
|
||||||
|
// edit cell
|
||||||
|
'i' | 'a' => {
|
||||||
|
let (x,y) = self.grid.selected_cell;
|
||||||
|
let starting_val = if let Some(val) = self.grid.get_cell_raw(x, y) {
|
||||||
|
val.as_raw_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
self.editor = Some(Editor::from(starting_val))
|
||||||
|
},
|
||||||
|
'I' => {/* insert col before */}
|
||||||
|
'A' => {/* insert col after */}
|
||||||
|
'o' => {/* insert row below */}
|
||||||
|
'O' => {/* insert row above */}
|
||||||
|
':' => {/* enter command mode */}
|
||||||
|
c => {
|
||||||
|
// start entering c for words
|
||||||
|
self.chord_buf += &c.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
event::Event::Paste(_) => todo!(),
|
||||||
|
event::Event::Resize(_, _) => todo!(),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Editor {
|
||||||
|
buf: String,
|
||||||
|
cursor: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Editor {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self {
|
||||||
|
buf: value.to_string(),
|
||||||
|
cursor: value.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &Editor {
|
||||||
|
fn render(self, area: prelude::Rect, buf: &mut prelude::Buffer) {
|
||||||
|
Paragraph::new(self.buf.clone()).render(area, buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user