Skip to content

Commit 04666c5

Browse files
committed
impl list
1 parent 0da454a commit 04666c5

File tree

8 files changed

+204
-78
lines changed

8 files changed

+204
-78
lines changed

.rustfmt.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
max_width = 80
2+
merge-imports = true

Cargo.lock

Lines changed: 59 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
[package]
2-
name = "mime"
2+
name = "handlr"
33
version = "0.1.0"
44
authors = ["greg <gregory.mkv@gmail.com>"]
55
edition = "2018"
66

7-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8-
97
[dependencies]
108
dirs = "2.0.2"
11-
anyhow = "1.0.28"
129
pest = "2.1.3"
1310
pest_derive = "2.1.0"
1411
rayon = "1.3.0"
@@ -18,3 +15,7 @@ url = "2.1.1"
1815
mime_guess = "2.0.3"
1916
itertools = "0.9.0"
2017
derive_more = {version = "0.99.5", default-features = false, features = ["display"] }
18+
json = "0.12.4"
19+
shlex = "0.1.1"
20+
thiserror = "1.0.14"
21+
ascii_table = "3.0.0"

src/common.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::Result;
1+
use crate::{Error, Result};
22
use pest::Parser;
33
use std::convert::TryFrom;
44
use std::path::PathBuf;
@@ -12,11 +12,13 @@ impl std::str::FromStr for Mime {
1212
Ok(Self(s.to_owned()))
1313
}
1414
}
15-
#[derive(Debug, derive_more::Display, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
15+
#[derive(
16+
Debug, derive_more::Display, Clone, Hash, PartialEq, Eq, PartialOrd, Ord,
17+
)]
1618
pub struct Handler(String);
1719

1820
impl std::str::FromStr for Handler {
19-
type Err = anyhow::Error;
21+
type Err = Error;
2022
fn from_str(s: &str) -> Result<Self, Self::Err> {
2123
Self::resolve(s.to_owned())
2224
}
@@ -41,21 +43,31 @@ impl Handler {
4143
locally.or(system)
4244
}
4345
pub fn resolve(name: String) -> Result<Self> {
44-
let path =
45-
Self::get_path(&name).ok_or_else(|| anyhow::Error::msg("Handler does not exist"))?;
46+
let path = Self::get_path(&name).ok_or(Error::NotFound)?;
4647
DesktopEntry::try_from(path)?;
4748
Ok(Self(name))
4849
}
4950
pub fn get_entry(&self) -> Result<DesktopEntry> {
5051
DesktopEntry::try_from(Self::get_path(&self.0).unwrap())
5152
}
52-
pub fn run(&self, arg: &str) -> Result<()> {
53-
std::process::Command::new("gtk-launch")
54-
.args(&[self.0.as_str(), arg])
53+
pub fn open(&self, arg: String) -> Result<()> {
54+
let (cmd, args) = self.get_entry()?.get_cmd(Some(arg))?;
55+
std::process::Command::new(cmd)
56+
.args(args)
5557
.stdout(std::process::Stdio::null())
5658
.spawn()?;
5759
Ok(())
5860
}
61+
pub fn launch(&self, args: Vec<String>) -> Result<()> {
62+
let (cmd, mut base_args) = self.get_entry()?.get_cmd(None)?;
63+
base_args.extend_from_slice(&args);
64+
std::process::Command::new(cmd)
65+
.args(base_args)
66+
.stdout(std::process::Stdio::null())
67+
.stderr(std::process::Stdio::null())
68+
.spawn()?;
69+
Ok(())
70+
}
5971
}
6072

6173
#[derive(Debug, Clone, pest_derive::Parser, Default, PartialEq, Eq)]
@@ -67,8 +79,22 @@ pub struct DesktopEntry {
6779
pub(crate) mimes: Vec<Mime>,
6880
}
6981

82+
impl DesktopEntry {
83+
pub fn get_cmd(
84+
&self,
85+
arg: Option<String>,
86+
) -> Result<(String, Vec<String>)> {
87+
let arg = arg.unwrap_or_default();
88+
let arg = shlex::quote(&arg);
89+
let replaced = self.exec.replace("%f", &arg).replace("%U", &arg);
90+
91+
let mut split = shlex::split(&replaced).ok_or(Error::BadCmd)?;
92+
Ok((split.remove(0), split))
93+
}
94+
}
95+
7096
impl TryFrom<PathBuf> for DesktopEntry {
71-
type Error = anyhow::Error;
97+
type Error = Error;
7298
fn try_from(p: PathBuf) -> Result<DesktopEntry> {
7399
let raw = std::fs::read_to_string(&p)?;
74100
let file = Self::parse(Rule::file, &raw)?.next().unwrap();
@@ -84,10 +110,12 @@ impl TryFrom<PathBuf> for DesktopEntry {
84110
let name = inner_rules.next().unwrap().as_str();
85111
match name {
86112
"Name" => {
87-
entry.name = inner_rules.next().unwrap().as_str().into();
113+
entry.name =
114+
inner_rules.next().unwrap().as_str().into();
88115
}
89116
"Exec" => {
90-
entry.exec = inner_rules.next().unwrap().as_str().into();
117+
entry.exec =
118+
inner_rules.next().unwrap().as_str().into();
91119
}
92120
"MimeType" => {
93121
let mut mimes = inner_rules
@@ -111,7 +139,7 @@ impl TryFrom<PathBuf> for DesktopEntry {
111139
if !entry.name.is_empty() && !entry.exec.is_empty() {
112140
Ok(entry)
113141
} else {
114-
Err(anyhow::Error::msg("Invalid desktop entry"))
142+
Err(Error::BadCmd)
115143
}
116144
}
117145
}

src/error.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#[derive(Debug, thiserror::Error)]
2+
pub enum Error {
3+
#[error(transparent)]
4+
Parse(#[from] pest::error::Error<crate::common::Rule>),
5+
#[error(transparent)]
6+
Io(#[from] std::io::Error),
7+
#[error("handler not found")]
8+
NotFound,
9+
#[error("Invalid desktop entry")]
10+
BadCmd,
11+
#[error("Could not find config dir")]
12+
NoConfigDir,
13+
#[error("could not guess mime type")]
14+
Ambiguous,
15+
}
16+
17+
pub type Result<T, E = Error> = std::result::Result<T, E>;

src/main.rs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,61 @@
1-
use anyhow::Result;
1+
use error::{Error, Result};
22
use structopt::StructOpt;
33

44
mod common;
5+
mod error;
56
mod mimeapps;
67

78
pub use common::{DesktopEntry, Handler, Mime};
89

910
#[derive(StructOpt)]
10-
enum Options {
11+
enum Cmd {
1112
List,
12-
Open { path: String },
13-
Get { mime: Mime },
14-
Set { mime: Mime, handler: Handler },
13+
Open {
14+
path: String,
15+
},
16+
Get {
17+
#[structopt(long)]
18+
json: bool,
19+
mime: Mime,
20+
},
21+
Launch {
22+
mime: Mime,
23+
args: Vec<String>,
24+
},
25+
Set {
26+
mime: Mime,
27+
handler: Handler,
28+
},
1529
}
1630

1731
fn main() -> Result<()> {
18-
let cmd = Options::from_args();
32+
let mut apps = mimeapps::MimeApps::read()?;
1933

20-
let mut user = mimeapps::MimeApps::read()?;
21-
22-
match cmd {
23-
Options::Set { mime, handler } => {
24-
user.set_handler(mime, handler)?;
34+
match Cmd::from_args() {
35+
Cmd::Set { mime, handler } => {
36+
apps.set_handler(mime, handler)?;
37+
}
38+
Cmd::Launch { mime, args } => {
39+
apps.get_handler(&mime)?.launch(args)?;
2540
}
26-
Options::Get { mime } => {
27-
println!("{}", user.get_handler(&mime)?);
41+
Cmd::Get { mime, json } => {
42+
apps.show_handler(&mime, json)?;
2843
}
29-
Options::Open { path } => match url::Url::parse(&path) {
44+
Cmd::Open { path } => match url::Url::parse(&path) {
3045
Ok(url) => {
3146
let mime = Mime(format!("x-scheme-handler/{}", url.scheme()));
32-
user.get_handler(&mime)?.run(&path)?;
47+
apps.get_handler(&mime)?.open(path)?;
3348
}
3449
Err(_) => {
3550
let guess = mime_guess::from_path(&path)
3651
.first_raw()
37-
.ok_or_else(|| anyhow::Error::msg("Could not determine mime type"))?;
38-
user.get_handler(&Mime(guess.to_owned()))?.run(&path)?;
52+
.ok_or(Error::Ambiguous)?;
53+
apps.get_handler(&Mime(guess.to_owned()))?.open(path)?;
3954
}
4055
},
41-
_ => {}
56+
Cmd::List => {
57+
apps.print()?;
58+
}
4259
};
4360

4461
Ok(())

0 commit comments

Comments
 (0)