This commit is contained in:
Greg 2020-04-05 17:29:58 -04:00
commit b49441940e
No known key found for this signature in database
GPG key ID: 2E44FAEEDC94B1E2
8 changed files with 767 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

502
Cargo.lock generated Normal file
View file

@ -0,0 +1,502 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ahash"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0989268a37e128d4d7a8028f1c60099430113fdbc70419010601ce51a228e4fe"
dependencies = [
"const-random",
]
[[package]]
name = "anyhow"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "base64"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "blake2b_simd"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "const-random"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
dependencies = [
"const-random-macro",
"proc-macro-hack",
]
[[package]]
name = "const-random-macro"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
dependencies = [
"getrandom",
"proc-macro-hack",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if",
"lazy_static",
]
[[package]]
name = "dashmap"
version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0899c830f359a74834c84ed43b4c0cb6fd714a6fa64e20ab78c78f8cf86d8fc0"
dependencies = [
"ahash",
"cfg-if",
"num_cpus",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array",
]
[[package]]
name = "dirs"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
dependencies = [
"cfg-if",
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
dependencies = [
"cfg-if",
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "either"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
dependencies = [
"typenum",
]
[[package]]
name = "getrandom"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hermit-abi"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memoffset"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8"
dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.1.0"
dependencies = [
"anyhow",
"dashmap",
"dirs",
"pest",
"pest_derive",
"rayon",
]
[[package]]
name = "num_cpus"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
"sha-1",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
[[package]]
name = "proc-macro2"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
dependencies = [
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
dependencies = [
"crossbeam-deque",
"crossbeam-queue",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]]
name = "redox_users"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
dependencies = [
"getrandom",
"redox_syscall",
"rust-argon2",
]
[[package]]
name = "rust-argon2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
dependencies = [
"base64",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer",
"digest",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "syn"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "typenum"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

15
Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "mime"
version = "0.1.0"
authors = ["greg <gregory.mkv@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dirs = "2.0.2"
anyhow = "1.0.28"
pest = "2.1.3"
pest_derive = "2.1.0"
rayon = "1.3.0"
dashmap = "3.10.0"

69
src/common.rs Normal file
View file

@ -0,0 +1,69 @@
use anyhow::Result;
use pest::Parser;
use std::convert::TryFrom;
use std::path::PathBuf;
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct Mime(pub String);
impl std::fmt::Debug for Mime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[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) mimes: Vec<Mime>,
}
impl TryFrom<PathBuf> for DesktopEntry {
type Error = anyhow::Error;
fn try_from(p: PathBuf) -> Result<DesktopEntry> {
let raw = std::fs::read_to_string(p)?;
let file = Self::parse(Rule::file, &raw)?.next().unwrap();
let mut entry = Self::default();
for line in file.into_inner() {
match line.as_rule() {
Rule::property => {
let mut inner_rules = line.into_inner(); // { name ~ "=" ~ value }
let name = inner_rules.next().unwrap().as_str();
match name {
"Name" => {
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;
}
_ => {}
}
}
_ => {}
}
}
if !entry.name.is_empty() && !entry.exec.is_empty() {
Ok(entry)
} else {
Err(anyhow::Error::msg("Invalid desktop entry"))
}
}
}

17
src/ini.pest Normal file
View file

@ -0,0 +1,17 @@
section_char = { XID_CONTINUE | " " }
name_char = {XID_CONTINUE | PUNCTUATION | "/" | "+" | "." | "-" | "%" | " " | "[" | "]" }
value_char = { PUNCTUATION | FORMAT | NUMBER | MARK | GRAPHEME_BASE}
section = { "[" ~ (section_char)+ ~ "]" }
property = { name ~ "=" ~ value }
name = { name_char+ }
value = { value_char+ }
comment = { "#" ~ name_char* }
file = {
SOI ~
((section | property | comment)? ~ NEWLINE)* ~
EOI
}

27
src/main.rs Normal file
View file

@ -0,0 +1,27 @@
use anyhow::Result;
mod common;
mod mimeapps;
mod systemapps;
pub use common::{DesktopEntry, Mime};
fn main() -> Result<()> {
let mut args: Vec<_> = std::env::args().collect();
let user = mimeapps::MimeApps::read()?;
let sys = systemapps::SystemApps::populate()?;
let mime = Mime(args.remove(1));
let user_handler = user.get_handler(&mime);
match user_handler {
Some(h) => {
dbg!(&h);
}
None => {
let s = sys.get_handlers(&mime);
dbg!(&s);
}
};
Ok(())
}

97
src/mimeapps.rs Normal file
View file

@ -0,0 +1,97 @@
use crate::{DesktopEntry, Mime};
use anyhow::Result;
use pest::Parser;
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, pest_derive::Parser, Default)]
#[grammar = "ini.pest"]
pub struct MimeApps {
added_associations: HashMap<Mime, Vec<PathBuf>>,
default_apps: HashMap<Mime, Vec<PathBuf>>,
}
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 {
pub fn get_handler(&self, mime: &Mime) -> Option<DesktopEntry> {
use std::convert::TryFrom;
Some(
self.default_apps
.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> {
let path = dirs::config_dir()
.map(|mut data_dir| {
data_dir.push("mimeapps.list");
data_dir
})
.ok_or_else(|| anyhow::Error::msg("Could not determine xdg data dir"))?;
let raw_conf = std::fs::read_to_string(path)?;
let file = Self::parse(Rule::file, &raw_conf)
.expect("unsuccessful parse") // unwrap the parse result
.next()
.unwrap();
let mut current_section_name = "".to_string();
let mut conf = Self::default();
file.into_inner().for_each(|line| {
match line.as_rule() {
Rule::section => {
current_section_name = line.into_inner().concat();
}
Rule::property => {
let mut inner_rules = line.into_inner(); // { name ~ "=" ~ value }
let name = inner_rules.next().unwrap().as_str();
let mut handlers = inner_rules
.next()
.unwrap()
.as_str()
.split(";")
.filter_map(handler_exists)
.collect::<Vec<_>>();
handlers.pop();
if !handlers.is_empty() {
match current_section_name.as_str() {
"Added Associations" => conf
.added_associations
.insert(Mime(name.to_owned()), handlers.to_owned()),
"Default Applications" => conf
.default_apps
.insert(Mime(name.to_owned()), handlers.to_owned()),
_ => None,
};
}
}
_ => {}
}
});
Ok(conf)
}
}

39
src/systemapps.rs Normal file
View file

@ -0,0 +1,39 @@
use crate::{DesktopEntry, Mime};
use anyhow::Result;
use dashmap::DashMap;
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))
}
}