Reorganize and partially rewrite

This commit is contained in:
Gregory 2020-06-06 00:24:32 -04:00
parent d45027a408
commit 935d1daa87
No known key found for this signature in database
GPG key ID: 2E44FAEEDC94B1E2
14 changed files with 749 additions and 424 deletions

397
Cargo.lock generated
View file

@ -50,6 +50,18 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "base64"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42"
[[package]]
name = "bitflags"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
[[package]]
name = "bitflags"
version = "1.2.1"
@ -67,6 +79,12 @@ dependencies = [
"constant_time_eq",
]
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.7.3"
@ -90,9 +108,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.3.0"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
[[package]]
name = "byte-tools"
@ -124,6 +142,17 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
[[package]]
name = "chrono"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
dependencies = [
"num-integer",
"num-traits",
"time",
]
[[package]]
name = "clap"
version = "3.0.0-beta.1"
@ -131,7 +160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "860643c53f980f0d38a5e25dfab6c3c93b2cb3aa1fe192643d17a293c6c41936"
dependencies = [
"atty",
"bitflags",
"bitflags 1.2.1",
"clap_derive",
"indexmap",
"lazy_static",
@ -152,8 +181,8 @@ dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
]
[[package]]
@ -189,6 +218,16 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "dbus"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b17a12ffaff26515889b006fc029493a3e340366a137c13cec2cdd545ea3b8"
dependencies = [
"libc",
"libdbus-sys",
]
[[package]]
name = "digest"
version = "0.8.1"
@ -198,6 +237,17 @@ dependencies = [
"generic-array",
]
[[package]]
name = "dirs"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
dependencies = [
"libc",
"redox_users",
"winapi 0.3.8",
]
[[package]]
name = "dirs"
version = "2.0.2"
@ -274,7 +324,7 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
"bitflags",
"bitflags 1.2.1",
"fuchsia-zircon-sys",
]
@ -373,11 +423,13 @@ name = "handlr"
version = "0.3.2"
dependencies = [
"ascii_table",
"atty",
"clap",
"itertools",
"json",
"mime",
"mime-db",
"notify-rust",
"once_cell",
"pest",
"pest_derive",
@ -436,9 +488,9 @@ checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
[[package]]
name = "hyper"
version = "0.13.5"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14"
checksum = "a6e7655b9594024ad0ee439f3b5a7299369dc2a3f459b47c696f9ff676f9aa1f"
dependencies = [
"bytes",
"futures-channel",
@ -450,8 +502,8 @@ dependencies = [
"httparse",
"itoa",
"log",
"net2",
"pin-project",
"socket2",
"time",
"tokio",
"tower-service",
@ -484,9 +536,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.3.2"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
dependencies = [
"autocfg",
]
@ -517,9 +569,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
[[package]]
name = "js-sys"
version = "0.3.39"
version = "0.3.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7"
checksum = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177"
dependencies = [
"wasm-bindgen",
]
@ -553,7 +605,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f86d66d380c9c5a685aaac7a11818bdfa1f733198dfd9ec09c70b762cd12ad6f"
dependencies = [
"arrayvec 0.4.12",
"bitflags",
"bitflags 1.2.1",
"cfg-if",
"rustc_version",
"ryu",
@ -562,9 +614,18 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.70"
version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
[[package]]
name = "libdbus-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0"
dependencies = [
"pkg-config",
]
[[package]]
name = "log"
@ -575,6 +636,27 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "mac-notification-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb6b71a9a89cd38b395d994214297447e8e63b1ba5708a9a2b0b1048ceda76"
dependencies = [
"cc",
"chrono",
"dirs 1.0.5",
"objc-foundation",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "maplit"
version = "1.0.2"
@ -698,6 +780,36 @@ dependencies = [
"version_check",
]
[[package]]
name = "notify-rust"
version = "4.0.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963d07a7dc24a56e2e0036d444b37caf4f0418cfd31c4b4b018ffc92cc06b56b"
dependencies = [
"dbus",
"mac-notification-sys",
"winrt-notification",
]
[[package]]
name = "num-integer"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
@ -708,6 +820,35 @@ dependencies = [
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]]
name = "once_cell"
version = "1.4.0"
@ -726,7 +867,7 @@ version = "0.10.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
dependencies = [
"bitflags",
"bitflags 1.2.1",
"cfg-if",
"foreign-types",
"lazy_static",
@ -793,8 +934,8 @@ dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
]
[[package]]
@ -810,29 +951,29 @@ dependencies = [
[[package]]
name = "pin-project"
version = "0.4.17"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791"
checksum = "ba3a1acf4a3e70849f8a673497ef984f043f95d2d8252dcdf74d54e6a1e47e8a"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "0.4.17"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40"
checksum = "194e88048b71a3e02eb4ee36a6995fed9b8236c11a7bb9f7247a9d9835b3f265"
dependencies = [
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
]
[[package]]
name = "pin-project-lite"
version = "0.1.5"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f"
checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715"
[[package]]
name = "pin-utils"
@ -860,8 +1001,8 @@ checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
"version_check",
]
@ -872,21 +1013,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
"syn-mid",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
dependencies = [
"unicode-xid",
"unicode-xid 0.2.0",
]
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
[[package]]
name = "quote"
version = "1.0.6"
@ -980,11 +1127,11 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.10.4"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2"
checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680"
dependencies = [
"base64",
"base64 0.12.1",
"bytes",
"encoding_rs",
"futures-core",
@ -1004,7 +1151,6 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
"time",
"tokio",
"tokio-tls",
"url",
@ -1020,7 +1166,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
dependencies = [
"base64",
"base64 0.11.0",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
@ -1037,9 +1183,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.4"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "schannel"
@ -1057,7 +1203,7 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535"
dependencies = [
"bitflags",
"bitflags 1.2.1",
"core-foundation",
"core-foundation-sys",
"libc",
@ -1091,22 +1237,22 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.110"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c"
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.110"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
dependencies = [
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
]
[[package]]
@ -1162,6 +1308,18 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "socket2"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"winapi 0.3.8",
]
[[package]]
name = "static_assertions"
version = "0.3.4"
@ -1175,14 +1333,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.23"
name = "strum"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269"
checksum = "4ca6e4730f517e041e547ffe23d29daab8de6b73af4b6ae2a002108169f5e7da"
[[package]]
name = "strum_macros"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3384590878eb0cab3b128e844412e2d010821e7e091211b9d87324173ada7db8"
dependencies = [
"quote 0.3.15",
"syn 0.11.11",
]
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
dependencies = [
"quote 0.3.15",
"synom",
"unicode-xid 0.0.4",
]
[[package]]
name = "syn"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"quote 1.0.6",
"unicode-xid 0.2.0",
]
[[package]]
@ -1192,8 +1377,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
dependencies = [
"unicode-xid 0.0.4",
]
[[package]]
@ -1244,8 +1438,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
dependencies = [
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
]
[[package]]
@ -1284,8 +1478,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
dependencies = [
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
]
[[package]]
@ -1375,6 +1569,12 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
[[package]]
name = "unicode-xid"
version = "0.2.0"
@ -1394,9 +1594,9 @@ dependencies = [
[[package]]
name = "vcpkg"
version = "0.2.8"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
checksum = "55d1e41d56121e07f1e223db0a4def204e45c85425f6a16d462fd07c8d10d74c"
[[package]]
name = "vec_map"
@ -1428,9 +1628,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasm-bindgen"
version = "0.2.62"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551"
checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0"
dependencies = [
"cfg-if",
"serde",
@ -1440,24 +1640,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.62"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94"
checksum = "ded84f06e0ed21499f6184df0e0cb3494727b0c5da89534e0fcc55c51d812101"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.12"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a369c5e1dfb7569e14d62af4da642a3cbc2f9a3652fe586e26ac22222aa4b04"
checksum = "64487204d863f109eb77e8462189d111f27cb5712cc9fdb3461297a76963a2f6"
dependencies = [
"cfg-if",
"js-sys",
@ -1467,38 +1667,38 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.62"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776"
checksum = "838e423688dac18d73e31edce74ddfac468e37b1506ad163ffaf0a46f703ffe3"
dependencies = [
"quote",
"quote 1.0.6",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.62"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92"
dependencies = [
"proc-macro2",
"quote",
"syn",
"quote 1.0.6",
"syn 1.0.30",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.62"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad"
checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd"
[[package]]
name = "web-sys"
version = "0.3.39"
version = "0.3.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642"
checksum = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -1549,13 +1749,35 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winreg"
version = "0.6.2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "winrt"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e30cba82e22b083dc5a422c2ee77e20dc7927271a0dc981360c57c1453cb48d"
dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "winrt-notification"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c31a65da50d792c6f9bd2e3216249566c4fb1d2d34f9b7d2d66d2e93f62a242"
dependencies = [
"strum",
"strum_macros",
"winapi 0.3.8",
"winrt",
"xml-rs",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
@ -1577,9 +1799,18 @@ name = "xdg-mime"
version = "0.3.0"
source = "git+https://github.com/ebassi/xdg-mime-rs#68f9130f316a67c6fbce440698788b94b45380c5"
dependencies = [
"dirs",
"dirs 2.0.2",
"glob",
"mime",
"nom",
"unicase",
]
[[package]]
name = "xml-rs"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1945e12e16b951721d7976520b0832496ef79c31602c7a29d950de79ba74621"
dependencies = [
"bitflags 0.9.1",
]

View file

@ -9,7 +9,7 @@ description = "Manage mimeapps.list and default applications with ease"
[dependencies]
pest = "2.1.3"
pest_derive = "2.1.0"
clap = { version = "3.0.0-beta.1" }
clap = "3.0.0-beta.1"
url = "2.1.1"
itertools = "0.9.0"
json = "0.12.4"
@ -22,6 +22,8 @@ once_cell = "1.4.0"
regex = { version = "1.3.9", default-features = false, features = ["std"] }
mime-db = "0.1.5"
xdg-mime = { git = "https://github.com/ebassi/xdg-mime-rs" }
atty = "0.2.14"
notify-rust = "4.0.0-rc.1"
[profile.release]
opt-level=3

5
src/apps/mod.rs Normal file
View file

@ -0,0 +1,5 @@
mod system;
mod user;
pub use system::SystemApps;
pub use user::MimeApps;

47
src/apps/system.rs Normal file
View file

@ -0,0 +1,47 @@
use crate::{
common::{DesktopEntry, Handler},
Result,
};
use mime::Mime;
use std::{collections::HashMap, convert::TryFrom, ffi::OsStr};
#[derive(Debug)]
pub struct SystemApps(pub HashMap<Mime, Vec<Handler>>);
impl SystemApps {
pub fn get_handlers(&self, mime: &Mime) -> Option<Vec<Handler>> {
Some(self.0.get(mime)?.clone())
}
pub fn get_handler(&self, mime: &Mime) -> Option<Handler> {
Some(self.get_handlers(mime)?.get(0).unwrap().clone())
}
pub fn populate() -> Result<Self> {
let mut map = HashMap::<Mime, Vec<Handler>>::with_capacity(50);
xdg::BaseDirectories::new()?
.get_data_dirs()
.into_iter()
.map(|mut data_dir| {
data_dir.push("applications");
data_dir
})
.filter_map(|data_dir| std::fs::read_dir(data_dir).ok())
.for_each(|dir| {
dir.filter_map(Result::ok)
.filter(|p| {
p.path().extension() == Some(OsStr::new("desktop"))
})
.filter_map(|p| DesktopEntry::try_from(p.path()).ok())
.for_each(|entry| {
let (file_name, mimes) = (entry.file_name, entry.mimes);
mimes.into_iter().for_each(|mime| {
map.entry(mime)
.or_default()
.push(Handler::assume_valid(file_name.clone()));
});
});
});
Ok(Self(map))
}
}

View file

@ -1,12 +1,14 @@
use crate::{DesktopEntry, Error, Handler, Mime, Result};
use crate::{apps::SystemApps, DesktopEntry, Error, Handler, Result};
use mime::Mime;
use pest::Parser;
use std::{
collections::{HashMap, VecDeque},
path::PathBuf,
str::FromStr,
};
#[derive(Debug, pest_derive::Parser)]
#[grammar = "ini.pest"]
#[grammar = "common/ini.pest"]
pub struct MimeApps {
added_associations: HashMap<Mime, VecDeque<Handler>>,
default_apps: HashMap<Mime, VecDeque<Handler>>,
@ -42,7 +44,7 @@ impl MimeApps {
.or_else(|| self.added_associations.get(mime))
.map(|hs| hs.get(0).unwrap().clone())
.or_else(|| self.system_apps.get_handler(mime))
.ok_or(Error::NotFound)
.ok_or(Error::NotFound(mime.to_string()))
}
pub fn show_handler(&self, mime: &Mime, output_json: bool) -> Result<()> {
let handler = self.get_handler(mime)?;
@ -90,7 +92,6 @@ impl MimeApps {
let name = inner_rules.next().unwrap().as_str();
let handlers = {
use itertools::Itertools;
use std::str::FromStr;
inner_rules
.next()
@ -104,13 +105,17 @@ impl MimeApps {
};
if !handlers.is_empty() {
match current_section_name.as_str() {
"Added Associations" => conf
.added_associations
.insert(Mime(name.to_owned()), handlers),
"Default Applications" => conf
.default_apps
.insert(Mime(name.to_owned()), handlers),
match (
Mime::from_str(name),
current_section_name.as_str(),
) {
(Ok(mime), "Added Associations") => {
conf.added_associations.insert(mime, handlers)
}
(Ok(mime), "Default Applications") => {
conf.default_apps.insert(mime, handlers)
}
_ => None,
};
}
@ -134,7 +139,7 @@ impl MimeApps {
writer.write_all(b"[Added Associations]\n")?;
for (k, v) in self.added_associations.iter().sorted() {
writer.write_all(k.0.as_ref())?;
writer.write_all(k.essence_str().as_ref())?;
writer.write_all(b"=")?;
writer.write_all(v.iter().join(";").as_ref())?;
writer.write_all(b";\n")?;
@ -142,7 +147,7 @@ impl MimeApps {
writer.write_all(b"\n[Default Applications]\n")?;
for (k, v) in self.default_apps.iter().sorted() {
writer.write_all(k.0.as_ref())?;
writer.write_all(k.essence_str().as_ref())?;
writer.write_all(b"=")?;
writer.write_all(v.iter().join(";").as_ref())?;
writer.write_all(b";\n")?;
@ -158,7 +163,7 @@ impl MimeApps {
.default_apps
.iter()
.sorted()
.map(|(k, v)| vec![k.0.clone(), v.iter().join(", ")])
.map(|(k, v)| vec![k.to_string(), v.iter().join(", ")])
.collect::<Vec<_>>();
ascii_table::AsciiTable::default().print(rows);
@ -166,7 +171,7 @@ impl MimeApps {
Ok(())
}
pub fn list_handlers(&self) -> Result<()> {
use std::{convert::TryFrom, io::Write};
use std::{convert::TryFrom, io::Write, os::unix::ffi::OsStrExt};
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
@ -188,50 +193,3 @@ impl MimeApps {
Ok(())
}
}
#[derive(Debug)]
pub struct SystemApps(pub HashMap<Mime, Vec<Handler>>);
impl SystemApps {
pub fn get_handlers(&self, mime: &Mime) -> Option<Vec<Handler>> {
Some(self.0.get(mime)?.clone())
}
pub fn get_handler(&self, mime: &Mime) -> Option<Handler> {
Some(self.get_handlers(mime)?.get(0).unwrap().clone())
}
pub fn populate() -> Result<Self> {
use std::convert::TryFrom;
let mut map = HashMap::<Mime, Vec<Handler>>::with_capacity(50);
xdg::BaseDirectories::new()?
.get_data_dirs()
.into_iter()
.map(|mut data_dir| {
data_dir.push("applications");
data_dir
})
.filter_map(|data_dir| std::fs::read_dir(data_dir).ok())
.for_each(|dir| {
dir.filter_map(Result::ok)
.filter(|p| {
p.path()
.extension()
.map(std::ffi::OsStr::to_str)
.flatten()
== Some("desktop")
})
.filter_map(|p| DesktopEntry::try_from(p.path()).ok())
.for_each(|entry| {
let (file_name, mimes) = (entry.file_name, entry.mimes);
mimes.into_iter().for_each(|mime| {
map.entry(mime)
.or_default()
.push(Handler::assume_valid(file_name.clone()));
});
});
});
Ok(Self(map))
}
}

View file

@ -1,188 +0,0 @@
use crate::{mime_types, Error, Result};
use std::{convert::TryFrom, path::PathBuf};
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Mime(pub String);
impl Mime {
pub fn try_from_path(path: &str) -> Result<Self> {
if let Ok(url) = url::Url::parse(path) {
return Ok(Mime(format!("x-scheme-handler/{}", url.scheme())));
}
mime_types::from_file(path)
}
}
impl std::str::FromStr for Mime {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with(".") {
mime_types::from_file(s)
} else {
Ok(Mime(mime_types::verify(&s)?.to_owned()))
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Handler(String);
impl std::fmt::Display for Handler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::str::FromStr for Handler {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::resolve(s.to_owned())
}
}
impl Handler {
pub fn assume_valid(name: String) -> Self {
Self(name)
}
pub fn get_path(name: &str) -> Option<PathBuf> {
xdg::BaseDirectories::new()
.ok()?
.find_data_file(&format!("applications/{}", name))
}
pub fn resolve(name: String) -> Result<Self> {
let path = Self::get_path(&name).ok_or(Error::NotFound)?;
DesktopEntry::try_from(path)?;
Ok(Self(name))
}
pub fn get_entry(&self) -> Result<DesktopEntry> {
DesktopEntry::try_from(Self::get_path(&self.0).unwrap())
}
pub fn open(&self, arg: String) -> Result<()> {
let (cmd, args) = self.get_entry()?.get_cmd(Some(arg))?;
if self.get_entry()?.term {
std::process::Command::new(cmd)
.args(args)
.status()?;
} else {
std::process::Command::new(cmd)
.args(args)
.stdout(std::process::Stdio::null())
.spawn()?;
}
Ok(())
}
pub fn launch(&self, args: Vec<String>) -> Result<()> {
let (cmd, mut base_args) = self.get_entry()?.get_cmd(None)?;
base_args.extend_from_slice(&args);
if self.get_entry()?.term {
std::process::Command::new(cmd)
.args(base_args)
.status()?;
} else {
std::process::Command::new(cmd)
.args(base_args)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()?;
}
Ok(())
}
}
#[derive(Debug, Clone, pest_derive::Parser, Default, PartialEq, Eq)]
#[grammar = "ini.pest"]
pub struct DesktopEntry {
pub(crate) name: String,
pub(crate) exec: String,
pub(crate) file_name: String,
pub(crate) term: bool,
pub(crate) mimes: Vec<Mime>,
}
impl DesktopEntry {
pub fn get_cmd(
&self,
arg: Option<String>,
) -> Result<(String, Vec<String>)> {
let special = regex::Regex::new("%(f|F|u|U)").unwrap();
let mut split = shlex::split(&self.exec)
.ok_or(Error::BadCmd)?
.into_iter()
.map(|s| match s.as_str() {
"%f" | "%F" | "%u" | "%U" => arg.clone(),
s if special.is_match(s) => Some(
special
.replace_all(s, arg.as_deref().unwrap_or_default())
.into(),
),
_ => Some(s),
})
.filter_map(std::convert::identity)
.collect::<Vec<_>>();
Ok((split.remove(0), split))
}
}
impl TryFrom<PathBuf> for DesktopEntry {
type Error = Error;
fn try_from(p: PathBuf) -> Result<DesktopEntry> {
use pest::Parser;
let raw = std::fs::read_to_string(&p)?;
let file = Self::parse(Rule::file, &raw)?.next().unwrap();
let mut section: &str = Default::default();
let mut entry = Self::default();
entry.file_name = p.file_name().unwrap().to_str().unwrap().to_owned();
for line in file.into_inner() {
match line.as_rule() {
Rule::section => {
section = line.into_inner().as_str();
}
Rule::property if section == "Desktop Entry" => {
let mut inner_rules = line.into_inner(); // { name ~ "=" ~ value }
let name = inner_rules.next().unwrap().as_str();
match name {
"Name" if entry.name.is_empty() => {
entry.name =
inner_rules.next().unwrap().as_str().into();
}
"Exec" => {
entry.exec =
inner_rules.next().unwrap().as_str().into();
}
"MimeType" => {
let mut mimes = inner_rules
.next()
.unwrap()
.as_str()
.split(";")
.map(ToOwned::to_owned)
.map(Mime)
.collect::<Vec<_>>();
mimes.pop();
entry.mimes = mimes;
}
"Terminal" => {
entry.term = match inner_rules.next().unwrap().as_str() {
"true" => true,
_ => false,
}
}
_ => {}
}
}
_ => {}
}
}
if !entry.name.is_empty() && !entry.exec.is_empty() {
Ok(entry)
} else {
Err(Error::BadCmd)
}
}
}

View file

@ -1,9 +1,9 @@
use crate::{Error, Mime, Result};
use crate::Result;
use once_cell::sync::Lazy;
use std::path::Path;
use xdg_mime::SharedMimeInfo;
static SHARED_MIME_DB: Lazy<SharedMimeInfo> = Lazy::new(SharedMimeInfo::new);
pub static SHARED_MIME_DB: Lazy<SharedMimeInfo> =
Lazy::new(SharedMimeInfo::new);
static CUSTOM_MIMES: &[&'static str] = &[
"inode/directory",
@ -11,29 +11,7 @@ static CUSTOM_MIMES: &[&'static str] = &[
"x-scheme-handler/https",
];
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Mime> {
let guess = SHARED_MIME_DB.guess_mime_type().path(path).guess();
match guess.mime_type().essence_str() {
"application/octet-stream" => Err(Error::Ambiguous),
mime => Ok(Mime(mime.to_owned())),
}
}
pub fn verify(mime: &str) -> Result<&str> {
if mime.starts_with("x-scheme-handler/") || CUSTOM_MIMES.contains(&mime) {
return Ok(mime);
}
mime_db::TYPES
.iter()
.find(|(m, _, _)| m == &mime)
.ok_or(Error::BadMime(mime.to_owned()))?;
Ok(mime)
}
pub fn list() -> Result<()> {
pub fn autocomplete() -> Result<()> {
use std::io::Write;
let stdout = std::io::stdout();

129
src/common/desktop_entry.rs Normal file
View file

@ -0,0 +1,129 @@
use crate::{Error, Result};
use mime::Mime;
use std::{
convert::TryFrom,
ffi::OsString,
path::PathBuf,
process::{Command, Stdio},
str::FromStr,
};
#[derive(Debug, Clone, pest_derive::Parser, Default, PartialEq, Eq)]
#[grammar = "common/ini.pest"]
pub struct DesktopEntry {
pub(crate) name: String,
pub(crate) exec: String,
pub(crate) file_name: OsString,
pub(crate) term: bool,
pub(crate) mimes: Vec<Mime>,
}
impl DesktopEntry {
pub fn exec(&self, arguments: Vec<String>) -> Result<()> {
let supports_multiple =
self.exec.contains("%F") || self.exec.contains("%U");
if arguments.is_empty() {
self.exec_inner(None)?
} else if supports_multiple {
self.exec_inner(Some(arguments.join(" ")))?;
} else {
for arg in arguments {
self.exec_inner(Some(arg))?;
}
};
Ok(())
}
fn exec_inner(&self, arg: Option<String>) -> Result<()> {
let (cmd, args) = self.get_cmd(arg)?;
let mut cmd = Command::new(cmd);
cmd.args(args);
if self.term {
cmd.status()?;
} else {
cmd.stdout(Stdio::null()).stderr(Stdio::null()).spawn()?;
};
Ok(())
}
pub fn get_cmd(
&self,
arg: Option<String>,
) -> Result<(String, Vec<String>)> {
let special = regex::Regex::new("%(f|F|u|U)").unwrap();
let mut split = shlex::split(&self.exec)
.unwrap()
.into_iter()
.filter_map(|s| match s.as_str() {
"%f" | "%F" | "%u" | "%U" => arg.clone(),
s if special.is_match(s) => Some(
special
.replace_all(s, arg.as_deref().unwrap_or_default())
.into(),
),
_ => Some(s),
})
.collect::<Vec<_>>();
Ok((split.remove(0), split))
}
}
impl TryFrom<PathBuf> for DesktopEntry {
type Error = Error;
fn try_from(p: PathBuf) -> Result<DesktopEntry> {
use pest::Parser;
let raw = std::fs::read_to_string(&p)?;
let file = Self::parse(Rule::file, &raw)?.next().unwrap();
let mut entry = Self::default();
entry.file_name = p.file_name().unwrap().to_owned();
let mut section = "";
for line in file.into_inner() {
match line.as_rule() {
Rule::section => {
section = line.into_inner().as_str();
}
Rule::property if section == "Desktop Entry" => {
let mut inner_rules = line.into_inner(); // { name ~ "=" ~ value }
let name = inner_rules.next().unwrap().as_str();
match name {
"Name" if entry.name.is_empty() => {
entry.name =
inner_rules.next().unwrap().as_str().into();
}
"Exec" => {
entry.exec =
inner_rules.next().unwrap().as_str().into();
}
"MimeType" => {
let mut mimes = inner_rules
.next()
.unwrap()
.as_str()
.split(";")
.filter_map(|m| Mime::from_str(m).ok())
.collect::<Vec<_>>();
mimes.pop();
entry.mimes = mimes;
}
"Terminal" => {
entry.term =
inner_rules.next().unwrap().as_str() == "true"
}
_ => {}
}
}
_ => {}
}
}
if !entry.name.is_empty() && !entry.exec.is_empty() {
Ok(entry)
} else {
Err(Error::BadEntry(p.clone()))
}
}
}

41
src/common/handler.rs Normal file
View file

@ -0,0 +1,41 @@
use crate::{common::DesktopEntry, Error, Result};
use std::{convert::TryFrom, ffi::OsString, path::PathBuf};
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Handler(OsString);
impl std::fmt::Display for Handler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0.to_string_lossy())
}
}
impl std::str::FromStr for Handler {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::resolve(s.into())
}
}
impl Handler {
pub fn assume_valid(name: OsString) -> Self {
Self(name)
}
pub fn get_path(name: &std::ffi::OsStr) -> Option<PathBuf> {
let mut path = PathBuf::from("applications");
path.push(name);
xdg::BaseDirectories::new().ok()?.find_data_file(path)
}
pub fn resolve(name: OsString) -> Result<Self> {
let path = Self::get_path(&name)
.ok_or(Error::NotFound(name.to_string_lossy().into()))?;
DesktopEntry::try_from(path)?;
Ok(Self(name))
}
pub fn get_entry(&self) -> Result<DesktopEntry> {
DesktopEntry::try_from(Self::get_path(&self.0).unwrap())
}
pub fn launch(&self, args: Vec<String>) -> Result<()> {
self.get_entry()?.exec(args)
}
}

80
src/common/mime_types.rs Normal file
View file

@ -0,0 +1,80 @@
use crate::{common::SHARED_MIME_DB, Error, Result};
use mime::Mime;
use std::{
convert::TryFrom,
path::{Path, PathBuf},
str::FromStr,
};
// A mime derived from a path or URL
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct FlexibleMime(pub Mime);
impl TryFrom<&str> for FlexibleMime {
type Error = Error;
fn try_from(arg: &str) -> Result<Self> {
if let Ok(url) = url::Url::parse(arg) {
Ok(Self(
format!("x-scheme-handler/{}", url.scheme())
.parse::<Mime>()
.unwrap(),
))
} else {
Self::try_from(&*PathBuf::from(arg))
}
}
}
impl TryFrom<&Path> for FlexibleMime {
type Error = Error;
fn try_from(path: &Path) -> Result<Self> {
let guess = SHARED_MIME_DB.guess_mime_type().path(path).guess();
let guess = guess.mime_type().clone();
if guess == mime::APPLICATION_OCTET_STREAM {
Err(Error::Ambiguous(path.to_string_lossy().into()))
} else {
Ok(Self(guess))
}
}
}
// Mime derived from user input: extension(.pdf) or type like image/jpg
#[derive(Debug)]
pub struct MimeOrExtension(pub Mime);
impl FromStr for MimeOrExtension {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
if s.starts_with(".") {
Ok(Self(FlexibleMime::try_from(s)?.0))
} else {
Ok(Self(Mime::from_str(s)?))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn user_input() {
"image/jpg".parse::<MimeOrExtension>().unwrap();
".jpg".parse::<MimeOrExtension>().unwrap();
"image//jpg".parse::<MimeOrExtension>().unwrap_err();
"image".parse::<MimeOrExtension>().unwrap_err();
}
#[test]
fn from_path_with_extension() {
assert_eq!(
FlexibleMime::try_from(".pdf").unwrap().0,
mime::APPLICATION_PDF
);
assert_eq!(
FlexibleMime::try_from(".").unwrap().0.essence_str(),
"inode/directory"
);
}
}

9
src/common/mod.rs Normal file
View file

@ -0,0 +1,9 @@
mod db;
mod desktop_entry;
mod handler;
mod mime_types;
pub use self::db::{autocomplete as db_autocomplete, SHARED_MIME_DB};
pub use desktop_entry::{DesktopEntry, Rule as PestRule};
pub use handler::Handler;
pub use mime_types::{FlexibleMime, MimeOrExtension};

View file

@ -1,25 +1,21 @@
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Parse(#[from] pest::error::Error<crate::common::Rule>),
Parse(#[from] pest::error::Error<crate::common::PestRule>),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("no handler defined for this mime/extension")]
NotFound,
#[error("badly-formatted desktop entry")]
BadCmd,
#[error("could not locate config dir")]
NoConfigDir,
#[error("could not figure out the mime type")]
Ambiguous,
#[error("could not figure out the mime type from extension .{0}")]
UnknownExtension(String),
#[error("invalid mime {0}")]
BadMime(String),
#[error("either mime (via -m) or extension (via -e) must be provided")]
MissingMimeOrExt,
#[error(transparent)]
Notify(#[from] notify_rust::error::Error),
#[error(transparent)]
Xdg(#[from] xdg::BaseDirectoriesError),
#[error("no handler defined for this mime/extension")]
NotFound(String),
#[error("could not figure out the mime type .{0}")]
Ambiguous(String),
#[error(transparent)]
BadMimeType(#[from] mime::FromStrError),
#[error("Malformed desktop entry")]
BadEntry(std::path::PathBuf),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;

View file

@ -1,39 +1,57 @@
use clap::Clap;
use error::{Error, Result};
use notify_rust::Notification;
use std::convert::TryFrom;
mod apps;
mod common;
mod error;
mod mime_types;
mod mimeapps;
pub use common::{DesktopEntry, Handler, Mime};
use common::{DesktopEntry, FlexibleMime, Handler, MimeOrExtension};
#[derive(Clap)]
#[clap(global_setting = clap::AppSettings::DeriveDisplayOrder)]
#[clap(global_setting = clap::AppSettings::DisableHelpSubcommand)]
#[clap(version = clap::crate_version!())]
enum Cmd {
/// List default apps and the associated handlers
List,
/// Open a path/URL with its default handler
Open {
path: String,
#[clap(required = true)]
path: Vec<String>,
},
/// Set the default handler for mime/extension
Set {
mime: MimeOrExtension,
handler: Handler,
},
/// Unset the default handler for mime/extension
Unset { mime: MimeOrExtension },
/// Launch the handler for specified extension/mime with optional arguments
Launch {
mime: MimeOrExtension,
args: Vec<String>,
},
/// Get handler for this mime/extension
Get {
#[clap(long)]
json: bool,
mime: Mime,
},
Launch {
mime: Mime,
args: Vec<String>,
mime: MimeOrExtension,
},
/// Add a handler for given mime/extension
/// Note that the first handler is the default
Add {
mime: Mime,
mime: MimeOrExtension,
handler: Handler,
},
Set {
mime: Mime,
handler: Handler,
},
Unset {
mime: Mime,
},
#[clap(setting = clap::AppSettings::Hidden)]
Autocomplete {
#[clap(short)]
@ -44,41 +62,60 @@ enum Cmd {
}
fn main() -> Result<()> {
let mut apps = mimeapps::MimeApps::read()?;
let mut apps = apps::MimeApps::read()?;
match Cmd::parse() {
Cmd::Set { mime, handler } => {
apps.set_handler(mime, handler)?;
}
Cmd::Add { mime, handler } => {
apps.add_handler(mime, handler)?;
}
Cmd::Launch { mime, args } => {
apps.get_handler(&mime)?.launch(args)?;
}
Cmd::Get { mime, json } => {
apps.show_handler(&mime, json)?;
}
Cmd::Open { path } => {
apps.get_handler(&Mime::try_from_path(&path)?)?.open(path)?;
}
Cmd::List => {
apps.print()?;
}
Cmd::Unset { mime } => {
apps.remove_handler(&mime)?;
}
Cmd::Autocomplete {
desktop_files,
mimes,
} => {
if desktop_files {
apps.list_handlers()?;
} else if mimes {
mime_types::list()?;
let res = || -> Result<()> {
match Cmd::parse() {
Cmd::Set { mime, handler } => {
apps.set_handler(mime.0, handler)?;
}
Cmd::Add { mime, handler } => {
apps.add_handler(mime.0, handler)?;
}
Cmd::Launch { mime, args } => {
apps.get_handler(&mime.0)?.launch(args)?;
}
Cmd::Get { mime, json } => {
apps.show_handler(&mime.0, json)?;
}
Cmd::Open { path } => {
std::process::Command::new("notify-send")
.arg(&format!("{:?}", path))
.spawn()?;
apps.get_handler(
&FlexibleMime::try_from(path.get(0).unwrap().as_str())?.0,
)?
.launch(path)?;
}
Cmd::List => {
apps.print()?;
}
Cmd::Unset { mime } => {
apps.remove_handler(&mime.0)?;
}
Cmd::Autocomplete {
desktop_files,
mimes,
} => {
if desktop_files {
apps.list_handlers()?;
} else if mimes {
common::db_autocomplete()?;
}
}
}
};
Ok(())
}();
match (res, atty::is(atty::Stream::Stdout)) {
(Err(e), true) => eprintln!("{}", e),
(Err(e), false) => {
Notification::new()
.summary("handlr error")
.body(&e.to_string())
.show()?;
}
_ => {}
};
Ok(())
}