Compare commits

..

No commits in common. "main" and "1.6.18" have entirely different histories.
main ... 1.6.18

14 changed files with 397 additions and 581 deletions

269
Cargo.lock generated
View file

@ -105,13 +105,13 @@ checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.89" version = "0.1.88"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -137,9 +137,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "aws-lc-rs" name = "aws-lc-rs"
version = "1.13.3" version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" checksum = "08b5d4e069cbc868041a64bd68dc8cb39a0d79585cd6c5a24caa8c2d622121be"
dependencies = [ dependencies = [
"aws-lc-sys", "aws-lc-sys",
"zeroize", "zeroize",
@ -221,7 +221,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -286,15 +286,15 @@ dependencies = [
"regex", "regex",
"rustc-hash", "rustc-hash",
"shlex", "shlex",
"syn 2.0.106", "syn 2.0.104",
"which", "which",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.4" version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@ -313,9 +313,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.23.2" version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -346,11 +346,10 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.35" version = "1.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
dependencies = [ dependencies = [
"find-msvc-tools",
"jobserver", "jobserver",
"libc", "libc",
"shlex", "shlex",
@ -367,9 +366,9 @@ dependencies = [
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.3" 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 = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]] [[package]]
name = "chrono" name = "chrono"
@ -398,9 +397,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.47" version = "4.5.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -408,26 +407,26 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.47" version = "4.5.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"clap_lex", "clap_lex",
"strsim", "strsim 0.11.1",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.47" version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -546,8 +545,8 @@ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim 0.11.1",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -558,14 +557,14 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.5.3" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [ dependencies = [
"powerfmt", "powerfmt",
] ]
@ -609,7 +608,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -627,7 +626,7 @@ dependencies = [
"enum-ordinalize", "enum-ordinalize",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -662,7 +661,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -713,12 +712,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "find-msvc-tools"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -748,9 +741,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.2" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [ dependencies = [
"percent-encoding", "percent-encoding",
] ]
@ -827,7 +820,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -899,7 +892,7 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi",
"wasi 0.14.3+wasi-0.2.4", "wasi 0.14.2+wasi-0.2.4",
] ]
[[package]] [[package]]
@ -910,9 +903,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.3" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]] [[package]]
name = "h2" name = "h2"
@ -935,9 +928,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.5" 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 = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
@ -1013,14 +1006,13 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.7.0" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
dependencies = [ dependencies = [
"atomic-waker",
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-core", "futures-util",
"h2", "h2",
"http", "http",
"http-body", "http-body",
@ -1028,7 +1020,6 @@ dependencies = [
"httpdate", "httpdate",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"pin-utils",
"smallvec", "smallvec",
"tokio", "tokio",
"want", "want",
@ -1210,9 +1201,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "1.1.0" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
dependencies = [ dependencies = [
"idna_adapter", "idna_adapter",
"smallvec", "smallvec",
@ -1231,9 +1222,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.11.0" version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -1261,14 +1252,14 @@ dependencies = [
"indoc", "indoc",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
name = "io-uring" name = "io-uring"
version = "0.7.10" version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cfg-if", "cfg-if",
@ -1323,9 +1314,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
version = "0.1.34" version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
dependencies = [ dependencies = [
"getrandom 0.3.3", "getrandom 0.3.3",
"libc", "libc",
@ -1333,7 +1324,7 @@ dependencies = [
[[package]] [[package]]
name = "jocalsend" name = "jocalsend"
version = "1.6.1803398" version = "1.6.18"
dependencies = [ dependencies = [
"axum", "axum",
"axum-server", "axum-server",
@ -1402,9 +1393,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.175" version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -1468,9 +1459,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.28" 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 = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]] [[package]]
name = "lru" name = "lru"
@ -1580,9 +1571,9 @@ dependencies = [
[[package]] [[package]]
name = "network-interface" name = "network-interface"
version = "2.0.3" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07709a6d4eba90ab10ec170a0530b3aafc81cb8a2d380e4423ae41fc55fe5745" checksum = "862f41f1276e7148fb597fc55ed8666423bebe045199a1298c3515a73ec5cdd9"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -1660,7 +1651,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -1736,7 +1727,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"proc-macro2-diagnostics", "proc-macro2-diagnostics",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -1751,9 +1742,9 @@ dependencies = [
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.2" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
@ -1775,9 +1766,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "potential_utf" name = "potential_utf"
version = "0.1.3" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
dependencies = [ dependencies = [
"zerovec", "zerovec",
] ]
@ -1799,19 +1790,19 @@ dependencies = [
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.37" version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.101" version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -1824,7 +1815,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
"version_check", "version_check",
"yansi", "yansi",
] ]
@ -1940,9 +1931,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.2" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -1952,9 +1943,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.10" version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -1963,15 +1954,15 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.6" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.12.23" version = "0.12.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
@ -2106,9 +2097,9 @@ dependencies = [
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.22" version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]] [[package]]
name = "ryu" name = "ryu"
@ -2171,14 +2162,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.143" version = "1.0.142"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -2288,19 +2279,19 @@ dependencies = [
[[package]] [[package]]
name = "simsearch" name = "simsearch"
version = "0.3.0" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "629d21c4ebf25655995cda9eb93e85539fa68b0438acb85e9e5d10f6fe2404bc" checksum = "9c869b25830e4824ef7279015cfc298a0674aca6a54eeff2efce8d12bf3701fe"
dependencies = [ dependencies = [
"strsim", "strsim 0.10.0",
"triple_accel", "triple_accel",
] ]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.11" version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
@ -2330,6 +2321,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -2355,7 +2352,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2377,9 +2374,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.106" version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2403,7 +2400,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2429,42 +2426,42 @@ dependencies = [
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.21.0" version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"getrandom 0.3.3", "getrandom 0.3.3",
"once_cell", "once_cell",
"rustix 1.0.8", "rustix 1.0.8",
"windows-sys 0.60.2", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.16" version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.16" version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.43" version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [ dependencies = [
"deranged", "deranged",
"num-conv", "num-conv",
@ -2475,9 +2472,9 @@ dependencies = [
[[package]] [[package]]
name = "time-core" name = "time-core"
version = "0.1.6" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]] [[package]]
name = "tinystr" name = "tinystr"
@ -2515,7 +2512,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -2700,9 +2697,9 @@ dependencies = [
[[package]] [[package]]
name = "triple_accel" name = "triple_accel"
version = "0.4.0" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22048bc95dfb2ffd05b1ff9a756290a009224b60b2f0e7525faeee7603851e63" checksum = "622b09ce2fe2df4618636fb92176d205662f59803f39e70d1c333393082de96c"
[[package]] [[package]]
name = "try-lock" name = "try-lock"
@ -2800,14 +2797,13 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.7" version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
"percent-encoding", "percent-encoding",
"serde",
] ]
[[package]] [[package]]
@ -2851,11 +2847,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.14.3+wasi-0.2.4" version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [ dependencies = [
"wit-bindgen", "wit-bindgen-rt",
] ]
[[package]] [[package]]
@ -2880,7 +2876,7 @@ dependencies = [
"log", "log",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2915,7 +2911,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2994,7 +2990,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -3005,7 +3001,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -3201,18 +3197,21 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.13" version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen-rt"
version = "0.45.0" version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "writeable" name = "writeable"
@ -3255,7 +3254,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
"synstructure", "synstructure",
] ]
@ -3276,7 +3275,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]
[[package]] [[package]]
@ -3296,7 +3295,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
"synstructure", "synstructure",
] ]
@ -3336,5 +3335,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.104",
] ]

View file

@ -1,13 +1,12 @@
[package] [package]
name = "jocalsend" name = "jocalsend"
# 1.61803398874989484 # 1.61803398874989484
#----------^ #-----^
version = "1.6.1803398" version = "1.6.18"
edition = "2024" edition = "2024"
rust-version = "1.89"
authors = ["Joe Ardent <code@ardent.nebcorp.com>"] authors = ["Joe Ardent <code@ardent.nebcorp.com>"]
keywords = ["p2p", "localsend", "tui", "linux"] keywords = ["p2p", "localsend", "tui", "linux"]
description = "A TUI for LocalSend" description = "A terminal implementation of the LocalSend protocol"
readme = "README.md" readme = "README.md"
license-file = "LICENSE.md" license-file = "LICENSE.md"
repository = "https://git.kittencollective.com/nebkor/joecalsend" repository = "https://git.kittencollective.com/nebkor/joecalsend"
@ -35,7 +34,7 @@ rustix = { version = "1", default-features = false, features = ["system"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
sha256 = "1.6" sha256 = "1.6"
simsearch = "0.3" simsearch = "0.2"
thiserror = "2" thiserror = "2"
tokio = { version = "1", default-features = false, features = ["time", "macros", "rt-multi-thread"] } tokio = { version = "1", default-features = false, features = ["time", "macros", "rt-multi-thread"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["tls12", "logging"] } tokio-rustls = { version = "0.26", default-features = false, features = ["tls12", "logging"] }

View file

@ -10,12 +10,7 @@ that uses [Ratatui](https://github.com/ratatui/ratatui) to provide an interactiv
application, and is compatible with the official app. application, and is compatible with the official app.
Install with `cargo install jocalsend` (requires [Rust](https://rustup.rs/)); tested on Linux, it Install with `cargo install jocalsend` (requires [Rust](https://rustup.rs/)); tested on Linux, it
will probably work on Macs but if you're on a Mac, you probably have AirDrop. It's also available in will probably work on Macs but if you're on a Mac, you probably have AirDrop.
nixpkgs, and so if you're a NixOS user, `nix-shell -p jocalsend` will do what you expect.
## BLOG POSTS!
- [Announcement post](https://proclamations.nebcorp-hias.com/sundries/jocalsend/)
- [Design and development](https://proclamations.nebcorp-hias.com/rnd/jocalsend-development/)
## Capabilities and screenshots ## Capabilities and screenshots
@ -27,21 +22,18 @@ available:
- `S` -> go to the sending screen, defaulting to sending files - `S` -> go to the sending screen, defaulting to sending files
- `R` -> go to the receiving screen to approve or deny incoming transfers - `R` -> go to the receiving screen to approve or deny incoming transfers
- `L` -> go to the logging screen where you can adjust the log level - `L` -> go to the logging screen where you can adjust the log level
- `C` -> clear the list of local peers and re-discover them
- `H` or `?` -> go to help screen
- `ESC` -> go back to the previous screen - `ESC` -> go back to the previous screen
- `Q` -> exit the application - `Q` -> exit the application
When in the sending screen, the following are available Additionally, when in the sending screen, the following are available
- `TAB` -> switch between content selection and peer selection - `TAB` -> switch between content selection and peer selection
- `T` -> enter text directly to send, `ESC` to cancel - `P` -> switch to peer selection
- `/` -> fuzzy filename search, use `ESC` to stop inputting text - `T` -> switch to entering text to send
- `F` -> switch to selecting files to send (not available when entering text, use `ESC` to exit text entry)
When in the receiving screen, use `A` to approve the incoming transfer request, or `D` to deny it. In addition to the interactive commands, it will also accept commandline arguments to pre-select a
file or pre-populate text to send:
Finally, it will also accept commandline arguments to pre-select a file or pre-populate text to
send:
``` ```
$ jocalsend -h $ jocalsend -h

View file

@ -1 +1 @@
1.61803398 1.618

View file

@ -1,72 +0,0 @@
use std::path::{Path, PathBuf};
use crossterm::event::Event;
use jocalsend::error::Result;
use ratatui::widgets::WidgetRef;
use ratatui_explorer::FileExplorer;
use simsearch::{SearchOptions, SimSearch};
use tui_input::Input;
#[derive(Clone)]
pub(crate) struct FileFinder {
pub explorer: FileExplorer,
pub fuzzy: SimSearch<usize>,
pub working_dir: Option<PathBuf>,
pub input: Input,
}
impl FileFinder {
pub fn new() -> Result<Self> {
let fuzzy = SimSearch::new_with(
SearchOptions::new()
.stop_words(vec![std::path::MAIN_SEPARATOR_STR.to_string()])
.stop_whitespace(false)
.threshold(0.0),
);
Ok(Self {
explorer: FileExplorer::new()?,
fuzzy,
working_dir: None,
input: Default::default(),
})
}
pub fn handle(&mut self, event: &Event) -> Result<()> {
self.index();
Ok(self.explorer.handle(event)?)
}
pub fn cwd(&self) -> &Path {
self.explorer.cwd()
}
pub fn set_cwd(&mut self, cwd: &Path) -> Result<()> {
self.explorer.set_cwd(cwd)?;
self.index();
Ok(())
}
pub fn widget(&self) -> impl WidgetRef {
self.explorer.widget()
}
pub fn reset_fuzzy(&mut self) {
self.fuzzy.clear();
self.input.reset();
}
pub fn index(&mut self) {
if let Some(owd) = self.working_dir.as_ref()
&& owd == self.cwd()
{
return;
}
self.working_dir = Some(self.cwd().to_path_buf());
self.reset_fuzzy();
for (i, f) in self.explorer.files().iter().enumerate() {
self.fuzzy.insert(i, f.name());
}
}
}

View file

@ -1,4 +1,4 @@
use crossterm::event::{Event, KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use jocalsend::ReceiveDialog; use jocalsend::ReceiveDialog;
use log::{debug, error, warn}; use log::{debug, error, warn};
use tui_input::backend::crossterm::EventHandler; use tui_input::backend::crossterm::EventHandler;
@ -12,25 +12,28 @@ impl App {
event: crossterm::event::Event, event: crossterm::event::Event,
) { ) {
let code = key_event.code; let code = key_event.code;
let mode = self.screen(); let mode = self.screen.last_mut().unwrap();
match mode { match mode {
CurrentScreen::Main CurrentScreen::Main
| CurrentScreen::Help
| CurrentScreen::Logging | CurrentScreen::Logging
| CurrentScreen::Receiving => match code { | CurrentScreen::Receiving
| CurrentScreen::Sending(SendingScreen::Files(FileMode::Picking))
| CurrentScreen::Sending(SendingScreen::Peers) => match code {
KeyCode::Char('q') => self.exit().await, KeyCode::Char('q') => self.exit().await,
KeyCode::Char('s') => self.send(), KeyCode::Char('s') => self.send(),
KeyCode::Char('r') => self.recv(), KeyCode::Char('r') => self.recv(),
KeyCode::Char('l') => self.logs(), KeyCode::Char('l') => self.logs(),
KeyCode::Char('m') => self.main(), KeyCode::Char('m') => self.main(),
KeyCode::Char('h') | KeyCode::Char('?') => self.help(),
KeyCode::Char('c') => self.service.clear_peers().await,
KeyCode::Esc => self.pop(), KeyCode::Esc => self.pop(),
_ => match mode { _ => match mode {
CurrentScreen::Main | CurrentScreen::Help => {} CurrentScreen::Main => {
if let KeyCode::Char('d') = code {
self.service.refresh_peers().await
}
}
CurrentScreen::Logging => match code { CurrentScreen::Logging => match code {
KeyCode::Left => change_log_level(LogDelta::Down), KeyCode::Left => super::change_log_level(-1),
KeyCode::Right => change_log_level(LogDelta::Up), KeyCode::Right => super::change_log_level(1),
_ => {} _ => {}
}, },
CurrentScreen::Receiving => match code { CurrentScreen::Receiving => match code {
@ -40,98 +43,89 @@ impl App {
KeyCode::Char('d') => self.deny(), KeyCode::Char('d') => self.deny(),
_ => {} _ => {}
}, },
CurrentScreen::Stopping | CurrentScreen::Sending(_) => unreachable!(), CurrentScreen::Sending(sending_screen) => match sending_screen {
// we can only be in picking mode
SendingScreen::Files(fmode) => match code {
KeyCode::Char('t') => *sending_screen = SendingScreen::Text,
KeyCode::Tab => *sending_screen = SendingScreen::Peers,
KeyCode::Enter => self.chdir_or_send_file().await,
KeyCode::Char('/') => {
*fmode = FileMode::Fuzzy;
}
_ => self.file_finder.handle(&event).unwrap_or_default(),
},
SendingScreen::Peers => match code {
KeyCode::Tab => {
*sending_screen = SendingScreen::Files(FileMode::Picking)
}
KeyCode::Char('t') => *sending_screen = SendingScreen::Text,
KeyCode::Enter => self.send_content().await,
KeyCode::Up => self.peer_state.select_previous(),
KeyCode::Down => self.peer_state.select_next(),
_ => {}
},
SendingScreen::Text => unreachable!(),
},
CurrentScreen::Stopping => unreachable!(),
}, },
}, },
CurrentScreen::Sending(_) => self.sending_screen(key_event, event).await, // we only need to deal with sending text now or doing fuzzy matching
CurrentScreen::Stopping => {} CurrentScreen::Sending(sending_screen) => match sending_screen {
} SendingScreen::Text => match code {
} KeyCode::Tab => *sending_screen = SendingScreen::Peers,
KeyCode::Enter => self.send_text().await,
async fn sending_screen(&mut self, key_event: KeyEvent, event: Event) { KeyCode::Esc => {
let mode = self.screen(); self.text = None;
let CurrentScreen::Sending(mode) = mode else { self.input.reset();
return; *sending_screen = SendingScreen::Files(FileMode::Picking);
};
let code = key_event.code;
match mode {
SendingScreen::Peers => match code {
KeyCode::Char('q') => self.exit().await,
KeyCode::Char('s') => self.send(),
KeyCode::Char('r') => self.recv(),
KeyCode::Char('l') => self.logs(),
KeyCode::Char('m') => self.main(),
KeyCode::Char('h') | KeyCode::Char('?') => self.help(),
KeyCode::Char('c') => self.service.clear_peers().await,
KeyCode::Char('t') => self.sending_text(),
KeyCode::Tab => self.sending_files(),
KeyCode::Enter => self.send_content().await,
KeyCode::Esc => self.pop(),
_ => {}
},
SendingScreen::Files(FileMode::Picking) => match code {
KeyCode::Char('q') => self.exit().await,
KeyCode::Char('s') => self.send(),
KeyCode::Char('r') => self.recv(),
KeyCode::Char('l') => self.logs(),
KeyCode::Char('m') => self.main(),
KeyCode::Char('h') | KeyCode::Char('?') => self.help(),
KeyCode::Char('c') => self.service.clear_peers().await,
KeyCode::Char('t') => self.sending_text(),
KeyCode::Tab => self.sending_peers(),
KeyCode::Enter => self.chdir_or_send_file().await,
KeyCode::Esc => self.pop(),
KeyCode::Char('/') => self.sending_fuzzy(),
_ => self.file_finder.handle(&event).unwrap_or_default(),
},
SendingScreen::Files(FileMode::Fuzzy) => match code {
KeyCode::Tab => self.sending_peers(),
KeyCode::Enter => self.chdir_or_send_file().await,
KeyCode::Esc => {
self.file_finder.reset_fuzzy();
self.sending_files();
}
KeyCode::Up | KeyCode::Down => {
if let Err(e) = self.file_finder.handle(&event) {
log::error!("error selecting file: {e:?}");
} }
} _ => {
_ => { if let Some(changed) = self.input.handle_event(&event)
self.file_finder.index(); && changed.value
if let Some(changed) = self.file_finder.input.handle_event(&event) {
&& changed.value if self.input.value().is_empty() {
{ self.text = None;
let id = self } else {
.file_finder self.text = Some(self.input.to_string());
.fuzzy }
.search(self.file_finder.input.value()) }
.first()
.copied()
.unwrap_or(0);
self.file_finder.explorer.set_selected_idx(id);
} }
} },
}, SendingScreen::Files(fmode) => {
SendingScreen::Text => match code { if *fmode == FileMode::Fuzzy {
KeyCode::Tab => self.sending_peers(), match code {
KeyCode::Enter => self.send_text().await, KeyCode::Tab => *sending_screen = SendingScreen::Peers,
KeyCode::Esc => { KeyCode::Enter => self.chdir_or_send_file().await,
self.text = None; KeyCode::Esc => {
self.input.reset(); self.file_finder.reset_fuzzy();
self.sending_files(); *fmode = FileMode::Picking;
} }
_ => { KeyCode::Up | KeyCode::Down => {
if let Some(changed) = self.input.handle_event(&event) if let Err(e) = self.file_finder.handle(&event) {
&& changed.value log::error!("error selecting file: {e:?}");
{ }
if self.input.value().is_empty() { }
self.text = None; _ => {
} else { self.file_finder.index();
self.text = Some(self.input.to_string()); if let Some(changed) = self.file_finder.input.handle_event(&event)
&& changed.value
{
let id = self
.file_finder
.fuzzy
.search(self.file_finder.input.value())
.first()
.copied()
.unwrap_or(0);
self.file_finder.explorer.set_selected_idx(id);
}
}
} }
} }
} }
SendingScreen::Peers => unreachable!(),
}, },
CurrentScreen::Stopping => {}
} }
} }
@ -183,49 +177,6 @@ impl App {
} }
} }
pub fn help(&mut self) {
let last = self.screen.last();
match last {
Some(CurrentScreen::Help) => {}
_ => self.screen.push(CurrentScreen::Help),
}
}
fn sending_peers(&mut self) {
if let CurrentScreen::Sending(mode) = self.screen_mut() {
*mode = SendingScreen::Peers;
}
}
fn sending_text(&mut self) {
if let CurrentScreen::Sending(mode) = self.screen_mut() {
*mode = SendingScreen::Text;
}
}
fn sending_fuzzy(&mut self) {
if let CurrentScreen::Sending(mode) = self.screen_mut() {
*mode = SendingScreen::Files(FileMode::Fuzzy);
}
}
fn sending_files(&mut self) {
let doing_files = self.text.is_none();
let doing_picking = self.file_finder.input.value().is_empty();
let screen = self.screen_mut();
if let CurrentScreen::Sending(mode) = screen {
if doing_files {
if doing_picking {
*mode = SendingScreen::Files(FileMode::Picking);
} else {
*mode = SendingScreen::Files(FileMode::Fuzzy);
}
} else {
*mode = SendingScreen::Text;
}
}
}
// accept a content receive request // accept a content receive request
fn accept(&mut self) { fn accept(&mut self) {
let Some(idx) = self.receiving_state.selected() else { let Some(idx) = self.receiving_state.selected() else {
@ -273,7 +224,6 @@ impl App {
error!("could not list directory {file:?}: {e}"); error!("could not list directory {file:?}: {e}");
return; return;
} else if file.is_dir() { } else if file.is_dir() {
self.file_finder.input.reset();
return; return;
} }
@ -325,17 +275,3 @@ impl App {
} }
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum LogDelta {
Up,
Down,
}
fn change_log_level(delta: LogDelta) {
let level = match delta {
LogDelta::Up => log::max_level().increment_severity(),
LogDelta::Down => log::max_level().decrement_severity(),
};
log::set_max_level(level);
}

View file

@ -1,22 +1,25 @@
use std::{collections::BTreeMap, net::SocketAddr}; use std::{
collections::BTreeMap,
net::SocketAddr,
path::{Path, PathBuf},
};
use crossterm::event::{Event, EventStream, KeyEventKind}; use crossterm::event::{Event, EventStream, KeyEventKind};
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use jocalsend::{JocalEvent, JocalService, ReceiveRequest, error::Result}; use jocalsend::{JocalEvent, JocalService, ReceiveRequest, error::Result};
use julid::Julid; use julid::Julid;
use log::LevelFilter;
use ratatui::{ use ratatui::{
Frame, Frame,
widgets::{ListState, TableState}, widgets::{ListState, TableState, WidgetRef},
}; };
use ratatui_explorer::FileExplorer; use ratatui_explorer::FileExplorer;
use simsearch::{SearchOptions, SimSearch};
use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedReceiver;
use tui_input::Input; use tui_input::Input;
pub mod widgets; pub mod widgets;
mod file_finder;
use file_finder::FileFinder;
mod handle; mod handle;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -26,9 +29,25 @@ pub struct Peer {
pub addr: SocketAddr, pub addr: SocketAddr,
} }
#[derive(Clone)]
struct FileFinder {
explorer: FileExplorer,
fuzzy: SimSearch<usize>,
working_dir: Option<PathBuf>,
input: Input,
}
fn searcher() -> SimSearch<usize> {
SimSearch::new_with(
SearchOptions::new()
.stop_words(vec![std::path::MAIN_SEPARATOR_STR.to_string()])
.stop_whitespace(false),
)
}
pub struct App { pub struct App {
pub service: JocalService, pub service: JocalService,
pub terminal_events: EventStream, pub events: EventStream,
pub peers: Vec<Peer>, pub peers: Vec<Peer>,
pub peer_state: ListState, pub peer_state: ListState,
pub receive_requests: BTreeMap<Julid, ReceiveRequest>, pub receive_requests: BTreeMap<Julid, ReceiveRequest>,
@ -36,7 +55,7 @@ pub struct App {
receiving_state: TableState, receiving_state: TableState,
// for getting messages back from the web server or web client about things we've done; the // for getting messages back from the web server or web client about things we've done; the
// other end is held by the service // other end is held by the service
jocal_event_rx: UnboundedReceiver<JocalEvent>, event_listener: UnboundedReceiver<JocalEvent>,
file_finder: FileFinder, file_finder: FileFinder,
text: Option<String>, text: Option<String>,
input: Input, input: Input,
@ -49,7 +68,6 @@ pub enum CurrentScreen {
Receiving, Receiving,
Stopping, Stopping,
Logging, Logging,
Help,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -69,11 +87,11 @@ impl App {
pub fn new(service: JocalService, event_listener: UnboundedReceiver<JocalEvent>) -> Self { pub fn new(service: JocalService, event_listener: UnboundedReceiver<JocalEvent>) -> Self {
App { App {
service, service,
jocal_event_rx: event_listener, event_listener,
screen: vec![CurrentScreen::Main], screen: vec![CurrentScreen::Main],
file_finder: FileFinder::new().expect("could not create file explorer"), file_finder: FileFinder::new().expect("could not create file explorer"),
text: None, text: None,
terminal_events: Default::default(), events: Default::default(),
peers: Default::default(), peers: Default::default(),
peer_state: Default::default(), peer_state: Default::default(),
receive_requests: Default::default(), receive_requests: Default::default(),
@ -84,8 +102,20 @@ impl App {
pub async fn handle_events(&mut self) -> Result<()> { pub async fn handle_events(&mut self) -> Result<()> {
tokio::select! { tokio::select! {
jocal_event = self.jocal_event_rx.recv().fuse() => { event = self.events.next().fuse() => {
if let Some(event) = jocal_event { if let Some(Ok(evt)) = event {
match evt {
Event::Key(key)
if key.kind == KeyEventKind::Press
=> self.handle_key_event(key, evt).await,
Event::Mouse(_) => {}
Event::Resize(_, _) => {}
_ => {}
}
}
}
transfer_event = self.event_listener.recv().fuse() => {
if let Some(event) = transfer_event {
log::trace!("got JocalEvent {event:?}"); log::trace!("got JocalEvent {event:?}");
match event { match event {
JocalEvent::ReceiveRequest { id, request } => { JocalEvent::ReceiveRequest { id, request } => {
@ -102,19 +132,6 @@ impl App {
} }
} }
} }
terminal_event = self.terminal_events.next().fuse() => {
if let Some(Ok(evt)) = terminal_event {
match evt {
Event::Key(key)
if key.kind == KeyEventKind::Press
=> self.handle_key_event(key, evt).await,
Event::Mouse(_) => {}
Event::Resize(_, _) => {}
_ => {}
}
}
}
} }
Ok(()) Ok(())
@ -143,3 +160,66 @@ impl App {
frame.render_widget(self, frame.area()); frame.render_widget(self, frame.area());
} }
} }
impl FileFinder {
pub fn new() -> Result<Self> {
Ok(Self {
explorer: FileExplorer::new()?,
fuzzy: searcher(),
working_dir: None,
input: Default::default(),
})
}
pub fn handle(&mut self, event: &Event) -> Result<()> {
self.index();
Ok(self.explorer.handle(event)?)
}
pub fn cwd(&self) -> &Path {
self.explorer.cwd()
}
pub fn set_cwd(&mut self, cwd: &Path) -> Result<()> {
self.explorer.set_cwd(cwd)?;
self.index();
Ok(())
}
pub fn widget(&self) -> impl WidgetRef {
self.explorer.widget()
}
pub fn reset_fuzzy(&mut self) {
self.clear_fuzzy();
self.input.reset();
}
fn clear_fuzzy(&mut self) {
self.fuzzy = searcher();
}
fn index(&mut self) {
if let Some(owd) = self.working_dir.as_ref()
&& owd == self.cwd()
{
return;
}
self.working_dir = Some(self.cwd().to_path_buf());
self.clear_fuzzy();
for (i, f) in self.explorer.files().iter().enumerate() {
self.fuzzy.insert(i, f.name());
}
}
}
fn change_log_level(delta: isize) {
let level = log::max_level() as isize;
let max = log::LevelFilter::max() as isize;
let level = (level + delta).clamp(0, max) as usize;
// levelfilter is repr(usize) so this is safe
let level = unsafe { std::mem::transmute::<usize, LevelFilter>(level) };
log::set_max_level(level);
}

View file

@ -10,7 +10,7 @@ use ratatui::{
text::{Line, Text, ToLine}, text::{Line, Text, ToLine},
widgets::{ widgets::{
Block, Borders, Clear, List, ListItem, ListState, Padding, Paragraph, Row, Table, Block, Borders, Clear, List, ListItem, ListState, Padding, Paragraph, Row, Table,
TableState, Widget, Wrap, TableState, Widget,
}, },
}; };
use tui_logger::{TuiLoggerLevelOutput, TuiLoggerWidget, TuiWidgetState}; use tui_logger::{TuiLoggerLevelOutput, TuiLoggerWidget, TuiWidgetState};
@ -27,8 +27,6 @@ static MAIN_MENU: LazyLock<Line> = LazyLock::new(|| {
"<L>".blue().bold(), "<L>".blue().bold(),
" Previous Screen ".into(), " Previous Screen ".into(),
"<ESC>".blue().bold(), "<ESC>".blue().bold(),
" Help ".into(),
"<H|?>".blue().bold(),
" Quit ".into(), " Quit ".into(),
"<Q>".blue().bold(), "<Q>".blue().bold(),
]) ])
@ -130,11 +128,10 @@ impl Widget for &mut App {
let [header_left, header_right] = header_layout.areas(top); let [header_left, header_right] = header_layout.areas(top);
let header_margin = Margin::new(1, 2); let header_margin = Margin::new(1, 2);
let top_heavy = Layout::vertical([Constraint::Percentage(66), Constraint::Percentage(34)]);
let [heavy_top, _skinny_bottom] = top_heavy.areas(area);
let subscreen_margin = Margin::new(1, 2); let subscreen_margin = Margin::new(1, 2);
// it's safe to call `unwrap()` here because we ensure there's always at least
// one element in `self.screen`; see the `self.pop()` method
let current_screen = self.screen(); let current_screen = self.screen();
match current_screen { match current_screen {
CurrentScreen::Main => { CurrentScreen::Main => {
@ -159,10 +156,6 @@ impl Widget for &mut App {
buf, buf,
); );
} }
CurrentScreen::Help => {
outer_frame(&current_screen, &MAIN_MENU, area, buf);
help_screen(area.inner(subscreen_margin), buf);
}
CurrentScreen::Logging | CurrentScreen::Stopping => { CurrentScreen::Logging | CurrentScreen::Stopping => {
outer_frame(&current_screen, &LOGGING_MENU, area, buf); outer_frame(&current_screen, &LOGGING_MENU, area, buf);
logger(area.inner(subscreen_margin), buf); logger(area.inner(subscreen_margin), buf);
@ -192,31 +185,18 @@ impl Widget for &mut App {
} }
let file_area = header_left.inner(header_margin); let file_area = header_left.inner(header_margin);
let cwd = self
.file_finder
.cwd()
.as_os_str()
.to_string_lossy()
.into_owned();
match sending_screen { match sending_screen {
SendingScreen::Files(FileMode::Picking) SendingScreen::Files(FileMode::Picking) => {
| SendingScreen::Peers
| SendingScreen::Text => {
let layout = Layout::vertical([Constraint::Length(3), Constraint::Min(5)]);
let [cwd_area, file_area] = layout.areas(file_area);
let cwd: Line = cwd.into();
Paragraph::new(cwd)
.centered()
.block(Block::bordered())
.render(cwd_area, buf);
self.file_finder.widget().render(file_area, buf); self.file_finder.widget().render(file_area, buf);
} }
SendingScreen::Files(FileMode::Fuzzy) => { SendingScreen::Files(FileMode::Fuzzy) => {
let layout = Layout::vertical([Constraint::Max(3), Constraint::Min(5)]); let layout = Layout::vertical([Constraint::Max(6), Constraint::Min(5)]);
let [input_area, files_area] = layout.areas(file_area); let [input, files] = layout.areas(file_area);
text_popup(self.file_finder.input.value(), &cwd, input_area, buf); text_popup(self.file_finder.input.value(), "fuzzy search", input, buf);
self.file_finder.widget().render(files_area, buf); self.file_finder.widget().render(files, buf);
} }
_ => {}
} }
logger(header_right.inner(header_margin), buf); logger(header_right.inner(header_margin), buf);
@ -229,8 +209,7 @@ impl Widget for &mut App {
); );
if sending_screen == SendingScreen::Text { if sending_screen == SendingScreen::Text {
let rect = let rect = centered_rect(area, Constraint::Percentage(80), Constraint::Max(10));
centered_rect(heavy_top, Constraint::Percentage(80), Constraint::Max(6));
let text = if let Some(text) = self.text.as_ref() { let text = if let Some(text) = self.text.as_ref() {
text text
} else { } else {
@ -264,96 +243,18 @@ fn outer_frame(screen: &CurrentScreen, menu: &Line, area: Rect, buf: &mut Buffer
.render(area, buf); .render(area, buf);
} }
fn help_screen(area: Rect, buf: &mut Buffer) { fn text_popup(text: &str, title: &str, area: Rect, buf: &mut Buffer) {
let spacer = "".to_line().centered(); let title = Line::from(title.bold());
let main_bindings = vec![ let block = Block::bordered().title(title.centered());
Row::new(vec!["".to_line(), spacer.clone(), "".to_line()]),
Row::new(vec![
// Sending
"Send data".bold().into_right_aligned_line(),
spacer.clone(),
"S".bold().into_left_aligned_line(),
]),
// Receiving
Row::new(vec![
"Manage incoming transfer requests"
.bold()
.into_right_aligned_line(),
spacer.clone(),
"R".bold().into_left_aligned_line(),
]),
// logging
Row::new(vec![
"View logs and change log level"
.bold()
.into_right_aligned_line(),
spacer.clone(),
"L".bold().into_left_aligned_line(),
]),
// misc: main menu
Row::new(vec![
"Go to the main screen".bold().into_right_aligned_line(),
spacer.clone(),
"M".bold().into_left_aligned_line(),
]),
// misc: clear peers
Row::new(vec![
"Clear peers and rediscover"
.bold()
.into_right_aligned_line(),
spacer.clone(),
"C".bold().into_left_aligned_line(),
]),
// misc: help
Row::new(vec![
"This help screen".bold().into_right_aligned_line(),
spacer.clone(),
"H or ?".bold().into_left_aligned_line(),
]),
// misc: pop
Row::new(vec![
"Go to previous screen".bold().into_right_aligned_line(),
spacer.clone(),
"ESC".bold().into_left_aligned_line(),
]),
// misc: quit
Row::new(vec![
"Quit the application".bold().into_right_aligned_line(),
spacer.clone(),
"Q".bold().into_left_aligned_line(),
]),
];
let layout = Layout::vertical(vec![
Constraint::Max(3),
Constraint::Max(12),
Constraint::Max(3),
])
.flex(Flex::SpaceAround);
let [intro_area, bindings_area, outro_area] = layout.areas(area);
let widths = vec![
Constraint::Percentage(50),
Constraint::Max(6),
Constraint::Percentage(50),
];
let main_bindings = Table::new(main_bindings, widths).header(Row::new(vec![
"Action".bold().into_right_aligned_line(),
spacer,
"Key input".bold().into_left_aligned_line(),
]));
Clear.render(area, buf); Clear.render(area, buf);
let intro = "JocalSend is a mode-based application that responds to key-presses. Most modes support the following key bindings:".to_line().centered(); block.render(area, buf);
let intro = Paragraph::new(intro).wrap(Wrap { trim: true });
let outro = "Additional key bindings are available when in the sending or receiving screens, and are displayed at the bottom of the screen there.".to_line().centered(); let (_, len) = unicode_segmentation::UnicodeSegmentation::graphemes(text, true).size_hint();
let outro = Paragraph::new(outro).wrap(Wrap { trim: true }); let len = len.unwrap_or(text.len()) as u16 + 2;
let area = centered_rect(area, Constraint::Length(len), Constraint::Length(1));
intro.render(intro_area, buf); Paragraph::new(text).centered().yellow().render(area, buf);
main_bindings.render(bindings_area, buf);
outro.render(outro_area, buf);
} }
fn logger(area: Rect, buf: &mut Buffer) { fn logger(area: Rect, buf: &mut Buffer) {
@ -509,17 +410,3 @@ fn centered_rect(area: Rect, horizontal: Constraint, vertical: Constraint) -> Re
let [area] = Layout::vertical([vertical]).flex(Flex::Center).areas(area); let [area] = Layout::vertical([vertical]).flex(Flex::Center).areas(area);
area area
} }
fn text_popup(text: &str, title: &str, area: Rect, buf: &mut Buffer) {
let title = Line::from(title.bold());
let block = Block::bordered().title(title.centered());
Clear.render(area, buf);
block.render(area, buf);
let (_, len) = unicode_segmentation::UnicodeSegmentation::graphemes(text, true).size_hint();
let len = len.unwrap_or(text.len()) as u16 + 2;
let area = centered_rect(area, Constraint::Length(len), Constraint::Length(1));
Paragraph::new(text).centered().yellow().render(area, buf);
}

View file

@ -88,7 +88,7 @@ impl Config {
.map_err(Box::new)? // boxed because the error size from figment is large .map_err(Box::new)? // boxed because the error size from figment is large
}; };
log::debug!("using config: {config:?}"); log::info!("using config: {config:?}");
Ok(config) Ok(config)
} }

View file

@ -32,8 +32,6 @@ impl JocalService {
log::info!("starting http server"); log::info!("starting http server");
// need to make a custom tls acceptor, see
// https://github.com/programatik29/axum-server/blob/master/examples/rustls_session.rs
axum_server::bind_rustls(addr, ssl_config) axum_server::bind_rustls(addr, ssl_config)
.handle(handle) .handle(handle)
.serve(app.into_make_service_with_connect_info::<SocketAddr>()) .serve(app.into_make_service_with_connect_info::<SocketAddr>())

View file

@ -30,7 +30,9 @@ use transfer::Session;
pub const DEFAULT_PORT: u16 = 53317; pub const DEFAULT_PORT: u16 = 53317;
pub const MULTICAST_IP: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 167); pub const MULTICAST_IP: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 167);
pub const DEFAULT_INTERVAL: Duration = Duration::from_millis(100); pub const LISTENING_SOCKET_ADDR: SocketAddrV4 =
SocketAddrV4::new(Ipv4Addr::from_bits(0), DEFAULT_PORT);
pub const DEFAULT_INTERVAL: Duration = Duration::from_micros(33333);
pub type Peers = Arc<Mutex<BTreeMap<String, (SocketAddr, Device)>>>; pub type Peers = Arc<Mutex<BTreeMap<String, (SocketAddr, Device)>>>;
pub type Sessions = Arc<Mutex<BTreeMap<String, Session>>>; // Session ID to Session pub type Sessions = Arc<Mutex<BTreeMap<String, Session>>>; // Session ID to Session
@ -76,7 +78,7 @@ pub struct ReceiveRequest {
impl PartialEq for ReceiveRequest { impl PartialEq for ReceiveRequest {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.alias == other.alias && self.files == other.files self.alias == other.alias
} }
} }
@ -111,11 +113,10 @@ impl JocalService {
config: Config, config: Config,
) -> crate::error::Result<(Self, UnboundedReceiver<JocalEvent>)> { ) -> crate::error::Result<(Self, UnboundedReceiver<JocalEvent>)> {
let (tx, rx) = mpsc::unbounded_channel(); let (tx, rx) = mpsc::unbounded_channel();
let addr = SocketAddrV4::new(config.local_ip_addr, DEFAULT_PORT); let socket = UdpSocket::bind(LISTENING_SOCKET_ADDR).await?;
let socket = UdpSocket::bind(addr).await?;
socket.set_multicast_loop_v4(false)?; socket.set_multicast_loop_v4(false)?;
socket.set_multicast_ttl_v4(1)?; // local subnet only socket.set_multicast_ttl_v4(8)?; // 8 hops out from localnet
socket.join_multicast_v4(MULTICAST_IP, config.local_ip_addr)?; socket.join_multicast_v4(MULTICAST_IP, Ipv4Addr::from_bits(0))?;
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
// localsend certs are self-signed // localsend certs are self-signed
@ -178,26 +179,22 @@ impl JocalService {
let service = self.clone(); let service = self.clone();
handles.spawn(async move { handles.spawn(async move {
loop { loop {
if let Err(e) = service.announce(None).await {
error!("Announcement error: {e}");
}
tokio::time::sleep(Duration::from_secs(2)).await;
let rstate = service.running_state.lock().await; let rstate = service.running_state.lock().await;
if *rstate == RunningState::Stopping { if *rstate == RunningState::Stopping {
break; break;
} }
if let Err(e) = service.announce(None).await {
error!("Announcement error: {e}");
}
tokio::time::sleep(Duration::from_secs(2)).await;
} }
JocalTasks::Udp JocalTasks::Udp
}); });
} }
pub async fn stop(&self) { pub async fn stop(&self) {
{ let mut rstate = self.running_state.lock().await;
let mut rstate = self.running_state.lock().await; *rstate = RunningState::Stopping;
*rstate = RunningState::Stopping;
}
log::info!("shutting down http server"); log::info!("shutting down http server");
self.http_handle self.http_handle
.get() .get()
@ -205,7 +202,7 @@ impl JocalService {
.graceful_shutdown(Some(Duration::from_secs(5))); .graceful_shutdown(Some(Duration::from_secs(5)));
} }
pub async fn clear_peers(&self) { pub async fn refresh_peers(&self) {
let mut peers = self.peers.lock().await; let mut peers = self.peers.lock().await;
peers.clear(); peers.clear();
} }

View file

@ -6,7 +6,7 @@ use log::{error, info};
use ratatui::DefaultTerminal; use ratatui::DefaultTerminal;
use ratatui_explorer::FileExplorer; use ratatui_explorer::FileExplorer;
use tokio::task::JoinSet; use tokio::task::JoinSet;
use tui_logger::{LevelFilter, init_logger}; use tui_logger::{LevelFilter, init_logger, set_env_filter_from_env};
mod app; mod app;
use app::{App, CurrentScreen, Peer}; use app::{App, CurrentScreen, Peer};
@ -18,11 +18,13 @@ fn main() -> Result<()> {
// just in case we need to display the help // just in case we need to display the help
let _ = Cli::parse(); let _ = Cli::parse();
if std::env::var("RUST_LOG").is_err() {
unsafe {
std::env::set_var("RUST_LOG", "jocalsend");
}
}
init_logger(LevelFilter::Info).map_err(|e| std::io::Error::other(format!("{e}")))?; init_logger(LevelFilter::Info).map_err(|e| std::io::Error::other(format!("{e}")))?;
set_env_filter_from_env(None);
tui_logger::set_env_filter_from_string(
&std::env::var("RUST_LOG").unwrap_or("jocalsend".to_string()),
);
let config = Config::new()?; let config = Config::new()?;

View file

@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use crate::error::LocalSendError; use crate::error::LocalSendError;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct FileMetadata { pub struct FileMetadata {
pub id: String, pub id: String,
@ -21,7 +21,7 @@ pub struct FileMetadata {
pub metadata: Option<FileMetadataExt>, pub metadata: Option<FileMetadataExt>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileMetadataExt { pub struct FileMetadataExt {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub modified: Option<String>, pub modified: Option<String>,

View file

@ -131,6 +131,7 @@ impl JocalService {
let token = match prepare_response.files.get(&metadata.id) { let token = match prepare_response.files.get(&metadata.id) {
Some(t) => t, Some(t) => t,
None => { None => {
log::warn!("");
send_tx( send_tx(
JocalEvent::SendFailed { JocalEvent::SendFailed {
error: "missing token in prepare response from remote".into(), error: "missing token in prepare response from remote".into(),
@ -143,15 +144,6 @@ impl JocalService {
let content_id = &metadata.id; let content_id = &metadata.id;
let session_id = prepare_response.session_id; let session_id = prepare_response.session_id;
log::info!(
"sending {content_id} to {}",
peers
.lock()
.await
.get(&peer)
.map(|(_, peer)| peer.alias.as_str())
.unwrap_or("unknown peer")
);
let resp = do_send_bytes(sessions, client, &session_id, content_id, token, bytes).await; let resp = do_send_bytes(sessions, client, &session_id, content_id, token, bytes).await;
match resp { match resp {
@ -281,7 +273,11 @@ pub async fn handle_receive_upload(
let file_metadata = match session.files.get(file_id) { let file_metadata = match session.files.get(file_id) {
Some(metadata) => metadata, Some(metadata) => metadata,
None => { None => {
return (StatusCode::BAD_REQUEST, "File not found".to_string()).into_response(); return (
StatusCode::INTERNAL_SERVER_ERROR,
"File not found".to_string(),
)
.into_response();
} }
}; };
@ -289,8 +285,11 @@ pub async fn handle_receive_upload(
// Create directory if it doesn't exist // Create directory if it doesn't exist
if let Err(e) = tokio::fs::create_dir_all(download_dir).await { if let Err(e) = tokio::fs::create_dir_all(download_dir).await {
log::error!("could not create download directory '{download_dir:?}', got {e}"); return (
return (StatusCode::INTERNAL_SERVER_ERROR, "could not save content").into_response(); StatusCode::INTERNAL_SERVER_ERROR,
format!("Failed to create directory: {e}"),
)
.into_response();
} }
// Create file path // Create file path
@ -298,14 +297,13 @@ pub async fn handle_receive_upload(
// Write file // Write file
if let Err(e) = tokio::fs::write(&file_path, body).await { if let Err(e) = tokio::fs::write(&file_path, body).await {
log::warn!("could not save content to {file_path:?}, got {e}"); return (
return (StatusCode::INTERNAL_SERVER_ERROR, "could not save content").into_response(); StatusCode::INTERNAL_SERVER_ERROR,
format!("Failed to write file: {e}"),
)
.into_response();
} }
log::info!(
"saved content from {} to {file_path:?}",
&session.sender.alias
);
if let Ok(id) = Julid::from_str(session_id) { if let Ok(id) = Julid::from_str(session_id) {
service.send_event(JocalEvent::ReceivedInbound(id)); service.send_event(JocalEvent::ReceivedInbound(id));
}; };