initial ui scaffolding, refactor
This commit is contained in:
parent
f36ed6096b
commit
8332eecf17
437
Cargo.lock
generated
437
Cargo.lock
generated
@ -17,6 +17,12 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argh"
|
name = "argh"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@ -55,6 +61,12 @@ version = "1.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.75"
|
version = "0.3.75"
|
||||||
@ -94,6 +106,21 @@ version = "1.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
|
[[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.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.27"
|
version = "1.2.27"
|
||||||
@ -109,6 +136,29 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
|
[[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 = "convert_case"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@ -125,6 +175,105 @@ version = "0.8.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[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 0.38.44",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"derive_more",
|
||||||
|
"document-features",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"rustix 1.0.7",
|
||||||
|
"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 = "derive_more"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@ -136,6 +285,21 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "document-features"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
||||||
|
dependencies = [
|
||||||
|
"litrs",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@ -319,6 +483,8 @@ version = "0.15.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"equivalent",
|
||||||
"foldhash",
|
"foldhash",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -331,6 +497,12 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@ -535,6 +707,12 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@ -566,6 +744,25 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "2.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instability"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"indoc",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.11.0"
|
version = "2.11.0"
|
||||||
@ -582,6 +779,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@ -615,6 +821,12 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@ -627,12 +839,37 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litrs"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.27"
|
version = "0.4.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.12.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.5"
|
version = "2.7.5"
|
||||||
@ -661,6 +898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
@ -741,6 +979,35 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@ -798,6 +1065,36 @@ version = "5.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[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 0.28.1",
|
||||||
|
"indoc",
|
||||||
|
"instability",
|
||||||
|
"itertools",
|
||||||
|
"lru",
|
||||||
|
"paste",
|
||||||
|
"strum",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-truncate",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.20"
|
version = "0.12.20"
|
||||||
@ -880,6 +1177,19 @@ version = "0.1.25"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||||
|
|
||||||
|
[[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 0.4.15",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@ -889,7 +1199,7 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys 0.9.4",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -947,6 +1257,12 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.11.1"
|
version = "2.11.1"
|
||||||
@ -1020,6 +1336,36 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[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.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.10"
|
version = "0.4.10"
|
||||||
@ -1048,6 +1394,40 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@ -1115,7 +1495,7 @@ dependencies = [
|
|||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix 1.0.7",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1246,6 +1626,8 @@ name = "trs"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
|
"crossterm 0.29.0",
|
||||||
|
"ratatui",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"xml-rs",
|
"xml-rs",
|
||||||
@ -1263,6 +1645,35 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@ -1397,6 +1808,28 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -5,6 +5,8 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
argh = "0.1.13"
|
argh = "0.1.13"
|
||||||
|
crossterm = "0.29.0"
|
||||||
|
ratatui = "0.29.0"
|
||||||
reqwest = { version = "0.12.20", features = ["blocking"] }
|
reqwest = { version = "0.12.20", features = ["blocking"] }
|
||||||
rusqlite = { version = "0.36.0", features = ["bundled"] }
|
rusqlite = { version = "0.36.0", features = ["bundled"] }
|
||||||
xml-rs = "0.8.26"
|
xml-rs = "0.8.26"
|
||||||
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Terminal RSs
|
||||||
|
|
||||||
|
- Support both Atom and RSS
|
||||||
|
- https://www.rssboard.org/rss-specification
|
||||||
|
- https://www.rfc-editor.org/rfc/rfc4287
|
@ -1,24 +0,0 @@
|
|||||||
# Terminal RSs
|
|
||||||
|
|
||||||
|
|
||||||
Keep the goal simple.
|
|
||||||
|
|
||||||
- just list the unread articles
|
|
||||||
- Have a way to mark an article as read/unread
|
|
||||||
- https://www.rssboard.org/rss-specification
|
|
||||||
|
|
||||||
## Database
|
|
||||||
|
|
||||||
Channels = Id | FeedName | Link | Atom/RSS | checksum
|
|
||||||
|
|
||||||
Articles = Id | ChannelId | Title | Link (unique) | Description | Published | Read
|
|
||||||
|
|
||||||
https://sqlite.org/lang_upsert.html
|
|
||||||
|
|
||||||
INSERT INTO Articles(ChannelId, Title, Link, Description, Published, 0)
|
|
||||||
VALUES('Alice','704-555-1212','2018-05-08')
|
|
||||||
ON CONFLICT(name) DO UPDATE SET
|
|
||||||
Read=0,
|
|
||||||
Published=excluded.Published
|
|
||||||
WHERE excluded.Published>Articles.Published;
|
|
||||||
|
|
10
src/args.rs
10
src/args.rs
@ -12,6 +12,7 @@ pub struct TrsArgs {
|
|||||||
pub enum TrsSubCommand {
|
pub enum TrsSubCommand {
|
||||||
AddChannel(AddChannelArgs),
|
AddChannel(AddChannelArgs),
|
||||||
ListChannels(ListChannelArgs),
|
ListChannels(ListChannelArgs),
|
||||||
|
RemoveChannel(RemoveChannelArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new RSS channel
|
/// Add a new RSS channel
|
||||||
@ -32,6 +33,15 @@ pub struct ListChannelArgs {
|
|||||||
pub limit: Option<u32>,
|
pub limit: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete an RSS channel
|
||||||
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
#[argh(subcommand, name = "remove")]
|
||||||
|
pub struct RemoveChannelArgs {
|
||||||
|
/// delete the channel with this id
|
||||||
|
#[argh(option)]
|
||||||
|
pub id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn valid_url(url: &str) -> Result<String, String> {
|
pub fn valid_url(url: &str) -> Result<String, String> {
|
||||||
if url.starts_with("http://") || url.starts_with("https://") {
|
if url.starts_with("http://") || url.starts_with("https://") {
|
||||||
Ok(url.to_string())
|
Ok(url.to_string())
|
||||||
|
80
src/commands.rs
Normal file
80
src/commands.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use crate::{
|
||||||
|
args::{AddChannelArgs, ListChannelArgs, RemoveChannelArgs, TrsArgs, TrsSubCommand},
|
||||||
|
error::TrsError,
|
||||||
|
parser,
|
||||||
|
persistence::Db,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn execute(mut db: Db, args: &TrsArgs) -> Result<(), TrsError> {
|
||||||
|
let sub_command = &args.sub_command;
|
||||||
|
match sub_command {
|
||||||
|
TrsSubCommand::AddChannel(add_args) => add_channel(&mut db, add_args),
|
||||||
|
TrsSubCommand::ListChannels(list_args) => list_channels(&mut db, list_args),
|
||||||
|
TrsSubCommand::RemoveChannel(delete_args) => delete_channel(&mut db, delete_args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_channel(db: &mut Db, args: &AddChannelArgs) -> Result<(), TrsError> {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
let rss = client.get(&args.link).send().map_err(|e| {
|
||||||
|
TrsError::ReqwestError(
|
||||||
|
e,
|
||||||
|
"Unable to download provided RSS channel link".to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// TODO: Streaming read
|
||||||
|
let bytes = rss.bytes().map_err(|e| {
|
||||||
|
TrsError::ReqwestError(e, "Unable to read bytes from RSS response".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let xml_source_stream = xml::ParserConfig::new()
|
||||||
|
.ignore_invalid_encoding_declarations(true)
|
||||||
|
.create_reader(&bytes[..]);
|
||||||
|
let channel = parser::parse_rss_channel(xml_source_stream)?;
|
||||||
|
db.add_channel
|
||||||
|
.execute((channel.title, &args.link, channel.description))
|
||||||
|
.map_err(|e| TrsError::SqlError(e, "Failed to insert channel into database".to_string()))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_channels(conn: &mut Db, args: &ListChannelArgs) -> Result<(), TrsError> {
|
||||||
|
let channels_iter =
|
||||||
|
conn.list_channels
|
||||||
|
.query_map([args.limit.unwrap_or_else(|| 999)], |row| {
|
||||||
|
Ok((
|
||||||
|
row.get::<_, i64>(0)?,
|
||||||
|
row.get::<_, String>(1)?,
|
||||||
|
row.get::<_, String>(2)?,
|
||||||
|
row.get::<_, String>(3)?,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for row in channels_iter {
|
||||||
|
let (id, name, link, description) = row?;
|
||||||
|
println!(
|
||||||
|
"ID: {}, Name: {}, Link: {}, Description: {}",
|
||||||
|
id, name, link, description
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_channel(db: &mut Db, args: &RemoveChannelArgs) -> Result<(), TrsError> {
|
||||||
|
let rows_affected = db
|
||||||
|
.remove_channel
|
||||||
|
.execute([args.id])
|
||||||
|
.map_err(|e| TrsError::SqlError(e, "Failed to delete channel from database".to_string()))?;
|
||||||
|
|
||||||
|
if rows_affected == 0 {
|
||||||
|
return Err(TrsError::Error(format!(
|
||||||
|
"No channel found with ID: {}",
|
||||||
|
args.id
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Channel with ID {} deleted successfully.", args.id);
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,8 +1,11 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, TrsError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TrsError {
|
pub enum TrsError {
|
||||||
Error(String),
|
Error(String),
|
||||||
|
TuiError(std::io::Error),
|
||||||
XmlRsError(xml::reader::Error, String),
|
XmlRsError(xml::reader::Error, String),
|
||||||
SqlError(rusqlite::Error, String),
|
SqlError(rusqlite::Error, String),
|
||||||
ReqwestError(reqwest::Error, String),
|
ReqwestError(reqwest::Error, String),
|
||||||
@ -26,8 +29,9 @@ impl Display for TrsError {
|
|||||||
f,
|
f,
|
||||||
"{}",
|
"{}",
|
||||||
match self {
|
match self {
|
||||||
TrsError::Error(msg) => format!("XML Parse Error: {}", msg),
|
TrsError::Error(msg) => format!("{}", msg),
|
||||||
TrsError::XmlRsError(err, msg) => format!("{} XML Rs error {}", msg, err),
|
TrsError::TuiError(err) => format!("TUI Error: {}", err),
|
||||||
|
TrsError::XmlRsError(err, msg) => format!("XML Rs Error: {} {}", msg, err),
|
||||||
TrsError::SqlError(err, msg) => format!("SQL Error: {} - {}", err, msg),
|
TrsError::SqlError(err, msg) => format!("SQL Error: {} - {}", err, msg),
|
||||||
TrsError::ReqwestError(err, msg) => format!("Reqwest Error: {} - {}", err, msg),
|
TrsError::ReqwestError(err, msg) => format!("Reqwest Error: {} - {}", err, msg),
|
||||||
}
|
}
|
||||||
|
118
src/main.rs
118
src/main.rs
@ -1,107 +1,25 @@
|
|||||||
use std::env;
|
use args::TrsArgs;
|
||||||
|
use error::Result;
|
||||||
use args::{TrsArgs, TrsSubCommand};
|
|
||||||
use error::TrsError;
|
|
||||||
use rusqlite::Connection;
|
|
||||||
pub mod args;
|
pub mod args;
|
||||||
|
pub mod commands;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod persistence;
|
||||||
|
pub mod ui;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
if std::env::args().len() < 2 {
|
||||||
|
let terminal = ratatui::init();
|
||||||
|
ui::ui(terminal)?;
|
||||||
|
ratatui::restore();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), TrsError> {
|
|
||||||
let args = argh::from_env::<TrsArgs>();
|
let args = argh::from_env::<TrsArgs>();
|
||||||
|
let conn = persistence::init_connection()?;
|
||||||
let conn = init_db()?;
|
let db = persistence::init_db(&conn)?;
|
||||||
match args.sub_command {
|
commands::execute(db, &args).map_err(|e| {
|
||||||
TrsSubCommand::AddChannel(add_channel_args) => {
|
eprintln!("Error executing command: {}", e);
|
||||||
add_channel(&conn, &add_channel_args.link)?;
|
|
||||||
}
|
|
||||||
TrsSubCommand::ListChannels(list_channel_args) => {
|
|
||||||
list_channels(&conn, list_channel_args.limit)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_channel(conn: &Connection, link: &str) -> Result<(), TrsError> {
|
|
||||||
let client = reqwest::blocking::Client::new();
|
|
||||||
let rss = client.get(link).send().map_err(|e| {
|
|
||||||
TrsError::ReqwestError(
|
|
||||||
e,
|
|
||||||
"Unable to download provided RSS channel link".to_string(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// TODO: Streaming read
|
|
||||||
let bytes = rss.bytes().map_err(|e| {
|
|
||||||
TrsError::ReqwestError(e, "Unable to read bytes from RSS response".to_string())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let xml_source_stream = xml::ParserConfig::new()
|
|
||||||
.ignore_invalid_encoding_declarations(true)
|
|
||||||
.create_reader(&bytes[..]);
|
|
||||||
let channel = parser::parse_rss_channel(xml_source_stream)?;
|
|
||||||
let mut stmt =
|
|
||||||
conn.prepare(
|
|
||||||
"INSERT INTO Channels (name, link, description) VALUES (?1, ?2, ?3) ON CONFLICT(link) DO UPDATE SET name=?1, description=?3")?;
|
|
||||||
stmt.execute((channel.title, link, channel.description))
|
|
||||||
.map_err(|e| TrsError::SqlError(e, "Failed to insert channel into database".to_string()))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_channels(conn: &Connection, limit: Option<u32>) -> Result<(), TrsError> {
|
|
||||||
let mut stmt = conn.prepare("SELECT id, name, link, description FROM Channels")?;
|
|
||||||
let channels_iter = stmt.query_map([], |row| {
|
|
||||||
Ok((
|
|
||||||
row.get::<_, i64>(0)?,
|
|
||||||
row.get::<_, String>(1)?,
|
|
||||||
row.get::<_, String>(2)?,
|
|
||||||
row.get::<_, String>(3)?,
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
for row in channels_iter {
|
|
||||||
let (id, name, link, description) = row?;
|
|
||||||
println!(
|
|
||||||
"ID: {}, Name: {}, Link: {}, Description: {}",
|
|
||||||
id, name, link, description
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_db() -> Result<Connection, TrsError> {
|
|
||||||
let home_dir = env::home_dir();
|
|
||||||
let db_dir = home_dir
|
|
||||||
.map(|dir| dir.join(".config").join("trs"))
|
|
||||||
.ok_or(TrsError::Error(
|
|
||||||
"Unable to determine home directory".to_string(),
|
|
||||||
))?;
|
|
||||||
|
|
||||||
match std::fs::create_dir_all(&db_dir) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
return Err(TrsError::Error(format!(
|
|
||||||
"Failed to create database directory: {}",
|
|
||||||
e
|
e
|
||||||
)));
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let db_file = db_dir.join("test.db");
|
|
||||||
let conn = Connection::open(db_file)?;
|
|
||||||
conn.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS Channels (
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
link TEXT NOT NULL UNIQUE,
|
|
||||||
description TEXT
|
|
||||||
)",
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
.map_err(|e| TrsError::SqlError(e, "Failed to create Channels table".to_string()))?;
|
|
||||||
|
|
||||||
Ok(conn)
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use std::io::Read;
|
|||||||
|
|
||||||
use xml::{reader::XmlEvent, EventReader};
|
use xml::{reader::XmlEvent, EventReader};
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
use crate::error::TrsError;
|
use crate::error::TrsError;
|
||||||
|
|
||||||
pub struct RssChannel {
|
pub struct RssChannel {
|
||||||
@ -21,7 +22,7 @@ impl RssChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_channel_field(&mut self, field: &XmlTagField, value: String) -> Result<(), TrsError> {
|
fn update_channel_field(&mut self, field: &XmlTagField, value: String) -> Result<()> {
|
||||||
let last_article = self.articles.last_mut();
|
let last_article = self.articles.last_mut();
|
||||||
let no_item_error = || {
|
let no_item_error = || {
|
||||||
TrsError::Error(format!(
|
TrsError::Error(format!(
|
||||||
@ -103,9 +104,7 @@ const FIELD_TAG_MAPPINGS: [XmlTagField; 6] = [
|
|||||||
XmlTagField::mapping("item > pubDate", "pubDate", XmlField::ItemPubDate),
|
XmlTagField::mapping("item > pubDate", "pubDate", XmlField::ItemPubDate),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn parse_rss_channel<R: Read>(
|
pub fn parse_rss_channel<R: Read>(xml_source_stream: EventReader<R>) -> Result<RssChannel> {
|
||||||
xml_source_stream: EventReader<R>,
|
|
||||||
) -> Result<RssChannel, TrsError> {
|
|
||||||
let mut channel = RssChannel::new();
|
let mut channel = RssChannel::new();
|
||||||
let mut tag_prefix = "";
|
let mut tag_prefix = "";
|
||||||
let mut current_field: Option<&XmlTagField> = None;
|
let mut current_field: Option<&XmlTagField> = None;
|
||||||
|
78
src/persistence.rs
Normal file
78
src/persistence.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
use rusqlite::{Connection, Statement};
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
|
use crate::error::TrsError;
|
||||||
|
|
||||||
|
const CREATE_TABLE: &'static str = "CREATE TABLE IF NOT EXISTS Channels ( \
|
||||||
|
id INTEGER PRIMARY KEY, \
|
||||||
|
name TEXT NOT NULL, \
|
||||||
|
link TEXT NOT NULL UNIQUE, \
|
||||||
|
description TEXT \
|
||||||
|
)";
|
||||||
|
|
||||||
|
const ADD_CHANNEL: &'static str = "INSERT INTO Channels (name, link, description) \
|
||||||
|
VALUES (?1, ?2, ?3)\
|
||||||
|
ON CONFLICT(link) DO UPDATE SET name=?1, description=?3";
|
||||||
|
const REMOVE_CHANNEL: &'static str = "DELETE FROM Channels WHERE id = ?1";
|
||||||
|
const LIST_CHANNELS: &'static str = "SELECT id, name, link, description FROM Channels LIMIT ?1";
|
||||||
|
|
||||||
|
pub struct Db<'a> {
|
||||||
|
pub add_channel: Statement<'a>,
|
||||||
|
pub remove_channel: Statement<'a>,
|
||||||
|
pub list_channels: Statement<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! prepare_sql {
|
||||||
|
($conn:expr, $sql:expr) => {
|
||||||
|
$conn.prepare($sql).map_err(|e| {
|
||||||
|
TrsError::SqlError(e, format!("Failed to prepare SQL statement: {}", $sql))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Db<'a> {
|
||||||
|
fn create(conn: &'a Connection) -> Result<Self> {
|
||||||
|
let add_channel = prepare_sql!(conn, ADD_CHANNEL)?;
|
||||||
|
let remove_channel = prepare_sql!(conn, REMOVE_CHANNEL)?;
|
||||||
|
let list_channels = prepare_sql!(conn, LIST_CHANNELS)?;
|
||||||
|
Ok(Db {
|
||||||
|
add_channel,
|
||||||
|
remove_channel,
|
||||||
|
list_channels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_db(conn: &Connection) -> Result<Db> {
|
||||||
|
conn.execute(CREATE_TABLE, ())
|
||||||
|
.map_err(|e| TrsError::SqlError(e, "Failed to create Channels table".to_string()))?;
|
||||||
|
Db::create(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_connection() -> Result<Connection> {
|
||||||
|
let home_dir = env::home_dir();
|
||||||
|
let db_dir = home_dir
|
||||||
|
.map(|dir| dir.join(".config").join("trs"))
|
||||||
|
.ok_or(TrsError::Error(
|
||||||
|
"Unable to determine home directory".to_string(),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
match std::fs::create_dir_all(&db_dir) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(TrsError::Error(format!(
|
||||||
|
"Failed to create database directory: {}",
|
||||||
|
e
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let db_file = db_dir.join("test.db");
|
||||||
|
let conn = Connection::open(db_file)?;
|
||||||
|
conn.execute(CREATE_TABLE, ())
|
||||||
|
.map_err(|e| TrsError::SqlError(e, "Failed to create Channels table".to_string()))?;
|
||||||
|
|
||||||
|
Ok(conn)
|
||||||
|
}
|
38
src/ui.rs
Normal file
38
src/ui.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use std::io::Stdout;
|
||||||
|
|
||||||
|
use crate::error::{Result, TrsError};
|
||||||
|
use crossterm::event::{self, Event, KeyEventKind};
|
||||||
|
use ratatui::{prelude::CrosstermBackend, Terminal};
|
||||||
|
|
||||||
|
struct AppState {
|
||||||
|
exit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(mut terminal: Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
|
||||||
|
let mut app_state = AppState { exit: false };
|
||||||
|
loop {
|
||||||
|
handle_events(&mut app_state)?;
|
||||||
|
|
||||||
|
if app_state.exit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(&app_state, &mut terminal)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(app_state: &AppState, terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_events(state: &mut AppState) -> Result<()> {
|
||||||
|
let event = event::read().map_err(|e| TrsError::TuiError(e))?;
|
||||||
|
match event {
|
||||||
|
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
||||||
|
state.exit = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user