mirror of
https://github.com/chmln/handlr.git
synced 2024-11-14 21:49:27 +01:00
Implement open,get,set
This commit is contained in:
parent
b49441940e
commit
0da454a557
7 changed files with 465 additions and 105 deletions
0
.rustfmt.toml
Normal file
0
.rustfmt.toml
Normal file
258
Cargo.lock
generated
258
Cargo.lock
generated
|
@ -9,6 +9,15 @@ dependencies = [
|
||||||
"const-random",
|
"const-random",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.28"
|
version = "1.0.28"
|
||||||
|
@ -27,6 +36,17 @@ version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -39,6 +59,12 @@ version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2b_simd"
|
name = "blake2b_simd"
|
||||||
version = "0.5.10"
|
version = "0.5.10"
|
||||||
|
@ -89,6 +115,21 @@ version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.33.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"strsim",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-random"
|
name = "const-random"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
|
@ -173,6 +214,17 @@ dependencies = [
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "0.99.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
@ -236,6 +288,15 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
|
@ -245,6 +306,26 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||||
|
dependencies = [
|
||||||
|
"matches",
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-normalization",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -263,6 +344,12 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matches"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maybe-uninit"
|
name = "maybe-uninit"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -284,10 +371,31 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
"derive_more",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
"itertools",
|
||||||
|
"mime_guess",
|
||||||
"pest",
|
"pest",
|
||||||
"pest_derive",
|
"pest_derive",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"structopt",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime"
|
||||||
|
version = "0.3.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess"
|
||||||
|
version = "2.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
|
||||||
|
dependencies = [
|
||||||
|
"mime 0.3.16",
|
||||||
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -306,6 +414,12 @@ version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pest"
|
name = "pest"
|
||||||
version = "2.1.3"
|
version = "2.1.3"
|
||||||
|
@ -349,6 +463,32 @@ dependencies = [
|
||||||
"sha-1",
|
"sha-1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"syn-mid",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.15"
|
version = "0.5.15"
|
||||||
|
@ -444,6 +584,42 @@ dependencies = [
|
||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8faa2719539bbe9d77869bfb15d4ee769f99525e707931452c97b693b3f159d"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"lazy_static",
|
||||||
|
"structopt-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt-derive"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f88b8e18c69496aad6f9ddf4630dd7d585bcaf765786cb415b9aec2fe5a0430"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
@ -455,6 +631,26 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn-mid"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.11.2"
|
version = "1.11.2"
|
||||||
|
@ -467,12 +663,74 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||||
|
dependencies = [
|
||||||
|
"matches",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-normalization"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "url"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
|
||||||
|
dependencies = [
|
||||||
|
"idna",
|
||||||
|
"matches",
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.9.0+wasi-snapshot-preview1"
|
version = "0.9.0+wasi-snapshot-preview1"
|
||||||
|
|
|
@ -13,3 +13,8 @@ pest = "2.1.3"
|
||||||
pest_derive = "2.1.0"
|
pest_derive = "2.1.0"
|
||||||
rayon = "1.3.0"
|
rayon = "1.3.0"
|
||||||
dashmap = "3.10.0"
|
dashmap = "3.10.0"
|
||||||
|
structopt = "0.3.12"
|
||||||
|
url = "2.1.1"
|
||||||
|
mime_guess = "2.0.3"
|
||||||
|
itertools = "0.9.0"
|
||||||
|
derive_more = {version = "0.99.5", default-features = false, features = ["display"] }
|
||||||
|
|
|
@ -3,12 +3,58 @@ use pest::Parser;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Mime(pub String);
|
pub struct Mime(pub String);
|
||||||
|
|
||||||
impl std::fmt::Debug for Mime {
|
impl std::str::FromStr for Mime {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
type Err = std::convert::Infallible;
|
||||||
f.write_str(&self.0)
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self(s.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, derive_more::Display, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Handler(String);
|
||||||
|
|
||||||
|
impl std::str::FromStr for Handler {
|
||||||
|
type Err = anyhow::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> {
|
||||||
|
let locally = {
|
||||||
|
let mut local_dir = dirs::data_dir().unwrap();
|
||||||
|
local_dir.push("applications");
|
||||||
|
local_dir.push(name);
|
||||||
|
Some(local_dir).filter(|p| p.exists())
|
||||||
|
};
|
||||||
|
let system = {
|
||||||
|
let mut sys = std::path::PathBuf::from("/usr/share/applications");
|
||||||
|
sys.push(name);
|
||||||
|
Some(sys).filter(|p| p.exists())
|
||||||
|
};
|
||||||
|
locally.or(system)
|
||||||
|
}
|
||||||
|
pub fn resolve(name: String) -> Result<Self> {
|
||||||
|
let path =
|
||||||
|
Self::get_path(&name).ok_or_else(|| anyhow::Error::msg("Handler does not exist"))?;
|
||||||
|
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 run(&self, arg: &str) -> Result<()> {
|
||||||
|
std::process::Command::new("gtk-launch")
|
||||||
|
.args(&[self.0.as_str(), arg])
|
||||||
|
.stdout(std::process::Stdio::null())
|
||||||
|
.spawn()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,16 +63,18 @@ impl std::fmt::Debug for Mime {
|
||||||
pub struct DesktopEntry {
|
pub struct DesktopEntry {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) exec: String,
|
pub(crate) exec: String,
|
||||||
|
pub(crate) file_name: String,
|
||||||
pub(crate) mimes: Vec<Mime>,
|
pub(crate) mimes: Vec<Mime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<PathBuf> for DesktopEntry {
|
impl TryFrom<PathBuf> for DesktopEntry {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
fn try_from(p: PathBuf) -> Result<DesktopEntry> {
|
fn try_from(p: PathBuf) -> Result<DesktopEntry> {
|
||||||
let raw = std::fs::read_to_string(p)?;
|
let raw = std::fs::read_to_string(&p)?;
|
||||||
let file = Self::parse(Rule::file, &raw)?.next().unwrap();
|
let file = Self::parse(Rule::file, &raw)?.next().unwrap();
|
||||||
|
|
||||||
let mut entry = Self::default();
|
let mut entry = Self::default();
|
||||||
|
entry.file_name = p.file_name().unwrap().to_str().unwrap().to_owned();
|
||||||
|
|
||||||
for line in file.into_inner() {
|
for line in file.into_inner() {
|
||||||
match line.as_rule() {
|
match line.as_rule() {
|
||||||
|
|
44
src/main.rs
44
src/main.rs
|
@ -1,27 +1,45 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
mod mimeapps;
|
mod mimeapps;
|
||||||
mod systemapps;
|
|
||||||
|
|
||||||
pub use common::{DesktopEntry, Mime};
|
pub use common::{DesktopEntry, Handler, Mime};
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
enum Options {
|
||||||
|
List,
|
||||||
|
Open { path: String },
|
||||||
|
Get { mime: Mime },
|
||||||
|
Set { mime: Mime, handler: Handler },
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let mut args: Vec<_> = std::env::args().collect();
|
let cmd = Options::from_args();
|
||||||
let user = mimeapps::MimeApps::read()?;
|
|
||||||
let sys = systemapps::SystemApps::populate()?;
|
|
||||||
|
|
||||||
let mime = Mime(args.remove(1));
|
let mut user = mimeapps::MimeApps::read()?;
|
||||||
|
|
||||||
let user_handler = user.get_handler(&mime);
|
match cmd {
|
||||||
match user_handler {
|
Options::Set { mime, handler } => {
|
||||||
Some(h) => {
|
user.set_handler(mime, handler)?;
|
||||||
dbg!(&h);
|
|
||||||
}
|
}
|
||||||
None => {
|
Options::Get { mime } => {
|
||||||
let s = sys.get_handlers(&mime);
|
println!("{}", user.get_handler(&mime)?);
|
||||||
dbg!(&s);
|
|
||||||
}
|
}
|
||||||
|
Options::Open { path } => match url::Url::parse(&path) {
|
||||||
|
Ok(url) => {
|
||||||
|
let mime = Mime(format!("x-scheme-handler/{}", url.scheme()));
|
||||||
|
user.get_handler(&mime)?.run(&path)?;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let guess = mime_guess::from_path(&path)
|
||||||
|
.first_raw()
|
||||||
|
.ok_or_else(|| anyhow::Error::msg("Could not determine mime type"))?;
|
||||||
|
user.get_handler(&Mime(guess.to_owned()))?.run(&path)?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
168
src/mimeapps.rs
168
src/mimeapps.rs
|
@ -1,62 +1,55 @@
|
||||||
use crate::{DesktopEntry, Mime};
|
use crate::{DesktopEntry, Handler, Mime};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use dashmap::DashMap;
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Debug, pest_derive::Parser, Default)]
|
#[derive(Debug, pest_derive::Parser)]
|
||||||
#[grammar = "ini.pest"]
|
#[grammar = "ini.pest"]
|
||||||
pub struct MimeApps {
|
pub struct MimeApps {
|
||||||
added_associations: HashMap<Mime, Vec<PathBuf>>,
|
added_associations: HashMap<Mime, VecDeque<Handler>>,
|
||||||
default_apps: HashMap<Mime, Vec<PathBuf>>,
|
default_apps: HashMap<Mime, VecDeque<Handler>>,
|
||||||
}
|
system_apps: SystemApps,
|
||||||
|
|
||||||
fn handler_exists(name: &str) -> Option<std::path::PathBuf> {
|
|
||||||
let locally = {
|
|
||||||
let mut local_dir = dirs::data_dir().unwrap();
|
|
||||||
local_dir.push("applications");
|
|
||||||
local_dir.push(name);
|
|
||||||
Some(local_dir).filter(|p| p.exists())
|
|
||||||
};
|
|
||||||
let system = {
|
|
||||||
let mut sys = std::path::PathBuf::from("/usr/share/applications");
|
|
||||||
sys.push(name);
|
|
||||||
Some(sys).filter(|p| p.exists())
|
|
||||||
};
|
|
||||||
locally.or(system)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MimeApps {
|
impl MimeApps {
|
||||||
pub fn get_handler(&self, mime: &Mime) -> Option<DesktopEntry> {
|
pub fn set_handler(&mut self, mime: Mime, handler: Handler) -> Result<()> {
|
||||||
use std::convert::TryFrom;
|
let handlers = self.default_apps.entry(mime).or_default();
|
||||||
|
handlers.push_front(handler);
|
||||||
Some(
|
self.print()?;
|
||||||
self.default_apps
|
Ok(())
|
||||||
.get(mime)
|
|
||||||
.or_else(|| self.added_associations.get(mime))
|
|
||||||
.map(|hs| hs.get(0).unwrap().clone())
|
|
||||||
.map(DesktopEntry::try_from)
|
|
||||||
.map(Result::ok)
|
|
||||||
.flatten()?
|
|
||||||
.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
pub fn read() -> Result<Self> {
|
pub fn get_handler(&self, mime: &Mime) -> Result<Handler> {
|
||||||
let path = dirs::config_dir()
|
Ok(self
|
||||||
|
.default_apps
|
||||||
|
.get(mime)
|
||||||
|
.or_else(|| self.added_associations.get(mime))
|
||||||
|
.map(|hs| hs.get(0).unwrap().clone())
|
||||||
|
.or_else(|| self.system_apps.get_handler(mime))
|
||||||
|
.ok_or(anyhow::Error::msg("No handlers found"))?)
|
||||||
|
}
|
||||||
|
pub fn path() -> Result<PathBuf> {
|
||||||
|
dirs::config_dir()
|
||||||
.map(|mut data_dir| {
|
.map(|mut data_dir| {
|
||||||
data_dir.push("mimeapps.list");
|
data_dir.push("mimeapps.list");
|
||||||
data_dir
|
data_dir
|
||||||
})
|
})
|
||||||
.ok_or_else(|| anyhow::Error::msg("Could not determine xdg data dir"))?;
|
.ok_or_else(|| anyhow::Error::msg("Could not determine xdg data dir"))
|
||||||
|
}
|
||||||
let raw_conf = std::fs::read_to_string(path)?;
|
pub fn read() -> Result<Self> {
|
||||||
|
let raw_conf = std::fs::read_to_string(Self::path()?)?;
|
||||||
let file = Self::parse(Rule::file, &raw_conf)
|
let file = Self::parse(Rule::file, &raw_conf)
|
||||||
.expect("unsuccessful parse") // unwrap the parse result
|
.expect("unsuccessful parse") // unwrap the parse result
|
||||||
.next()
|
.next()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut current_section_name = "".to_string();
|
let mut current_section_name = "".to_string();
|
||||||
let mut conf = Self::default();
|
let mut conf = Self {
|
||||||
|
added_associations: HashMap::default(),
|
||||||
|
default_apps: HashMap::default(),
|
||||||
|
system_apps: SystemApps::populate()?,
|
||||||
|
};
|
||||||
|
|
||||||
file.into_inner().for_each(|line| {
|
file.into_inner().for_each(|line| {
|
||||||
match line.as_rule() {
|
match line.as_rule() {
|
||||||
|
@ -67,23 +60,29 @@ impl MimeApps {
|
||||||
let mut inner_rules = line.into_inner(); // { name ~ "=" ~ value }
|
let mut inner_rules = line.into_inner(); // { name ~ "=" ~ value }
|
||||||
|
|
||||||
let name = inner_rules.next().unwrap().as_str();
|
let name = inner_rules.next().unwrap().as_str();
|
||||||
let mut handlers = inner_rules
|
let handlers = {
|
||||||
.next()
|
use itertools::Itertools;
|
||||||
.unwrap()
|
use std::str::FromStr;
|
||||||
.as_str()
|
|
||||||
.split(";")
|
inner_rules
|
||||||
.filter_map(handler_exists)
|
.next()
|
||||||
.collect::<Vec<_>>();
|
.unwrap()
|
||||||
handlers.pop();
|
.as_str()
|
||||||
|
.split(";")
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.unique()
|
||||||
|
.filter_map(|s| Handler::from_str(s).ok())
|
||||||
|
.collect::<VecDeque<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
if !handlers.is_empty() {
|
if !handlers.is_empty() {
|
||||||
match current_section_name.as_str() {
|
match current_section_name.as_str() {
|
||||||
"Added Associations" => conf
|
"Added Associations" => conf
|
||||||
.added_associations
|
.added_associations
|
||||||
.insert(Mime(name.to_owned()), handlers.to_owned()),
|
.insert(Mime(name.to_owned()), handlers),
|
||||||
"Default Applications" => conf
|
"Default Applications" => {
|
||||||
.default_apps
|
conf.default_apps.insert(Mime(name.to_owned()), handlers)
|
||||||
.insert(Mime(name.to_owned()), handlers.to_owned()),
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -94,4 +93,71 @@ impl MimeApps {
|
||||||
|
|
||||||
Ok(conf)
|
Ok(conf)
|
||||||
}
|
}
|
||||||
|
pub fn print(&self) -> Result<()> {
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::io::{prelude::*, BufWriter};
|
||||||
|
|
||||||
|
let f = std::fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(Self::path()?)?;
|
||||||
|
let mut writer = BufWriter::new(f);
|
||||||
|
|
||||||
|
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(b"=")?;
|
||||||
|
writer.write_all(v.iter().join(";").as_ref())?;
|
||||||
|
writer.write_all(b";\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(b"=")?;
|
||||||
|
writer.write_all(v.iter().join(";").as_ref())?;
|
||||||
|
writer.write_all(b";\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SystemApps(pub DashMap<Mime, Vec<Handler>>);
|
||||||
|
|
||||||
|
impl SystemApps {
|
||||||
|
pub fn get_handlers(&self, mime: &Mime) -> Option<Vec<Handler>> {
|
||||||
|
Some(self.0.get(mime)?.value().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 rayon::iter::ParallelBridge;
|
||||||
|
use rayon::prelude::ParallelIterator;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
let map = DashMap::<Mime, Vec<Handler>>::with_capacity(50);
|
||||||
|
|
||||||
|
std::fs::read_dir("/usr/share/applications")?
|
||||||
|
.par_bridge()
|
||||||
|
.filter_map(|path| {
|
||||||
|
path.ok()
|
||||||
|
.map(|p| DesktopEntry::try_from(p.path()).ok())
|
||||||
|
.flatten()
|
||||||
|
})
|
||||||
|
.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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,4 @@
|
||||||
use crate::{DesktopEntry, Mime};
|
use crate::{DesktopEntry, Handler, Mime};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SystemApps(pub DashMap<Mime, Vec<DesktopEntry>>);
|
|
||||||
|
|
||||||
impl SystemApps {
|
|
||||||
pub fn get_handlers(&self, mime: &Mime) -> Option<Vec<DesktopEntry>> {
|
|
||||||
Some(self.0.get(mime)?.value().clone())
|
|
||||||
}
|
|
||||||
pub fn populate() -> Result<Self> {
|
|
||||||
use rayon::iter::ParallelBridge;
|
|
||||||
use rayon::prelude::ParallelIterator;
|
|
||||||
|
|
||||||
let map = DashMap::<Mime, Vec<DesktopEntry>>::with_capacity(50);
|
|
||||||
|
|
||||||
std::fs::read_dir("/usr/share/applications")?
|
|
||||||
.par_bridge()
|
|
||||||
.filter_map(|path| {
|
|
||||||
path.ok()
|
|
||||||
.map(|p| DesktopEntry::try_from(p.path()).ok())
|
|
||||||
.flatten()
|
|
||||||
})
|
|
||||||
.for_each(|entry| {
|
|
||||||
let (name, exec, mimes) = (entry.name, entry.exec, entry.mimes);
|
|
||||||
mimes.into_iter().for_each(|mime| {
|
|
||||||
map.entry(mime).or_default().push(DesktopEntry {
|
|
||||||
name: name.clone(),
|
|
||||||
exec: exec.clone(),
|
|
||||||
mimes: Vec::new(),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Self(map))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue