mirror of
https://github.com/chmln/handlr.git
synced 2025-02-17 06:28:39 +01:00
Polish
This commit is contained in:
parent
5ce3e839e3
commit
6da612bee1
7 changed files with 941 additions and 14 deletions
815
Cargo.lock
generated
815
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -19,3 +19,4 @@ json = "0.12.4"
|
||||||
shlex = "0.1.1"
|
shlex = "0.1.1"
|
||||||
thiserror = "1.0.14"
|
thiserror = "1.0.14"
|
||||||
ascii_table = "3.0.0"
|
ascii_table = "3.0.0"
|
||||||
|
mime-db = "0.1.5"
|
||||||
|
|
31
completions/handlr.fish
Normal file
31
completions/handlr.fish
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
function __handlr_autocomplete
|
||||||
|
function subcommands
|
||||||
|
set -l handlr_commands 'get help launch list open set unset'
|
||||||
|
complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "get" -d "Show handler for mime"
|
||||||
|
complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "launch" -d "Launch given handler with path/args"
|
||||||
|
complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "list" -d "Show handlers (default applications)"
|
||||||
|
complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "open" -d "Open path/URL with default handler (like xdg-open)"
|
||||||
|
complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "set" -d "Set handler for extension (e.g. pdf) or mime type"
|
||||||
|
complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "unset" -d "Unset handler"
|
||||||
|
end
|
||||||
|
|
||||||
|
function _set
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from set' -s "em"
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from set; __fish_prev_arg_in set' -a '-e -m'
|
||||||
|
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from set; __fish_prev_arg_in "-e"' -a "(handlr autocomplete -e)"
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from set; __fish_prev_arg_in "-m"' -a '(handlr autocomplete -m)'
|
||||||
|
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from set; set -l last (commandline -pco)[-2]; [ "$last" = "-e" ]' -a '(handlr autocomplete -d)' -d "desc1 desc2"
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from set; set -l last (commandline -pco)[-2]; [ "$last" = "-m" ]' -a '(handlr autocomplete -d)'
|
||||||
|
end
|
||||||
|
|
||||||
|
subcommands
|
||||||
|
_set
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from get' -l 'json' -a '(handlr autocomplete -m)'
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from unset' -a '(handlr autocomplete -m)'
|
||||||
|
complete -f -c handlr -n '__fish_seen_subcommand_from launch; __fish_prev_arg_in launch' -a '(handlr autocomplete -m)'
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
__handlr_autocomplete
|
26
src/autocomplete.rs
Normal file
26
src/autocomplete.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::Result;
|
||||||
|
pub fn extensions() -> Result<()> {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let stdout = std::io::stdout();
|
||||||
|
let mut stdout = stdout.lock();
|
||||||
|
mime_db::EXTENSIONS.iter().for_each(|(ext, _)| {
|
||||||
|
stdout.write_all(ext.as_bytes()).unwrap();
|
||||||
|
stdout.write_all(b"\n").unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mimes() -> Result<()> {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let stdout = std::io::stdout();
|
||||||
|
let mut stdout = stdout.lock();
|
||||||
|
mime_db::TYPES.iter().for_each(|(mime, _, _)| {
|
||||||
|
stdout.write_all(mime.as_bytes()).unwrap();
|
||||||
|
stdout.write_all(b"\n").unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
10
src/error.rs
10
src/error.rs
|
@ -4,14 +4,16 @@ pub enum Error {
|
||||||
Parse(#[from] pest::error::Error<crate::common::Rule>),
|
Parse(#[from] pest::error::Error<crate::common::Rule>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
#[error("handler not found")]
|
#[error("no handler defined for this mime/extension")]
|
||||||
NotFound,
|
NotFound,
|
||||||
#[error("Invalid desktop entry")]
|
#[error("badly-formatted desktop entry")]
|
||||||
BadCmd,
|
BadCmd,
|
||||||
#[error("Could not find config dir")]
|
#[error("could not locate config dir")]
|
||||||
NoConfigDir,
|
NoConfigDir,
|
||||||
#[error("could not guess mime type")]
|
#[error("could not figure out the mime from extension. please provide the mime type directly")]
|
||||||
Ambiguous,
|
Ambiguous,
|
||||||
|
#[error("either mime (via -m) or extension (via -e) must be provided")]
|
||||||
|
MissingMimeOrExt,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
46
src/main.rs
46
src/main.rs
|
@ -1,6 +1,7 @@
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
mod autocomplete;
|
||||||
mod common;
|
mod common;
|
||||||
mod error;
|
mod error;
|
||||||
mod mimeapps;
|
mod mimeapps;
|
||||||
|
@ -23,19 +24,40 @@ enum Cmd {
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
},
|
},
|
||||||
Set {
|
Set {
|
||||||
mime: Mime,
|
#[structopt(long, short)]
|
||||||
|
mime: Option<Mime>,
|
||||||
|
#[structopt(long, short)]
|
||||||
|
ext: Option<String>,
|
||||||
handler: Handler,
|
handler: Handler,
|
||||||
},
|
},
|
||||||
Unset {
|
Unset {
|
||||||
mime: Mime,
|
mime: Mime,
|
||||||
},
|
},
|
||||||
|
#[structopt(setting = structopt::clap::AppSettings::Hidden)]
|
||||||
|
Autocomplete {
|
||||||
|
#[structopt(short)]
|
||||||
|
desktop_files: bool,
|
||||||
|
#[structopt(short)]
|
||||||
|
mimes: bool,
|
||||||
|
#[structopt(short)]
|
||||||
|
extensions: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let mut apps = mimeapps::MimeApps::read()?;
|
let mut apps = mimeapps::MimeApps::read()?;
|
||||||
|
|
||||||
match Cmd::from_args() {
|
match Cmd::from_args() {
|
||||||
Cmd::Set { mime, handler } => {
|
Cmd::Set { mime, ext, handler } => {
|
||||||
|
let mime = match ext {
|
||||||
|
Some(extension) => mime_guess::from_ext(&extension)
|
||||||
|
.first_raw()
|
||||||
|
.map(ToOwned::to_owned)
|
||||||
|
.map(Mime)
|
||||||
|
.ok_or(Error::Ambiguous)?,
|
||||||
|
None => mime.ok_or(Error::MissingMimeOrExt)?,
|
||||||
|
};
|
||||||
|
|
||||||
apps.set_handler(mime, handler)?;
|
apps.set_handler(mime, handler)?;
|
||||||
}
|
}
|
||||||
Cmd::Launch { mime, args } => {
|
Cmd::Launch { mime, args } => {
|
||||||
|
@ -47,13 +69,14 @@ fn main() -> Result<()> {
|
||||||
Cmd::Open { path } => match url::Url::parse(&path) {
|
Cmd::Open { path } => match url::Url::parse(&path) {
|
||||||
Ok(url) => {
|
Ok(url) => {
|
||||||
let mime = Mime(format!("x-scheme-handler/{}", url.scheme()));
|
let mime = Mime(format!("x-scheme-handler/{}", url.scheme()));
|
||||||
|
|
||||||
apps.get_handler(&mime)?.open(path)?;
|
apps.get_handler(&mime)?.open(path)?;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let guess = mime_guess::from_path(&path)
|
let guess = mime_guess::from_path(&path)
|
||||||
.first_raw()
|
.first_or_text_plain()
|
||||||
.ok_or(Error::Ambiguous)?;
|
.to_string();
|
||||||
apps.get_handler(&Mime(guess.to_owned()))?.open(path)?;
|
apps.get_handler(&Mime(guess))?.open(path)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Cmd::List => {
|
Cmd::List => {
|
||||||
|
@ -62,6 +85,19 @@ fn main() -> Result<()> {
|
||||||
Cmd::Unset { mime } => {
|
Cmd::Unset { mime } => {
|
||||||
apps.remove_handler(&mime)?;
|
apps.remove_handler(&mime)?;
|
||||||
}
|
}
|
||||||
|
Cmd::Autocomplete {
|
||||||
|
desktop_files,
|
||||||
|
mimes,
|
||||||
|
extensions,
|
||||||
|
} => {
|
||||||
|
if desktop_files {
|
||||||
|
apps.list_handlers()?;
|
||||||
|
} else if mimes {
|
||||||
|
autocomplete::mimes()?;
|
||||||
|
} else if extensions {
|
||||||
|
autocomplete::extensions()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -158,6 +158,32 @@ impl MimeApps {
|
||||||
|
|
||||||
ascii_table::AsciiTable::default().print(rows);
|
ascii_table::AsciiTable::default().print(rows);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn list_handlers(&self) -> Result<()> {
|
||||||
|
// use rayon::iter::ParallelBridge;
|
||||||
|
// use rayon::prelude::ParallelIterator;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let stdout = std::io::stdout();
|
||||||
|
let mut stdout = stdout.lock();
|
||||||
|
|
||||||
|
std::fs::read_dir("/usr/share/applications")?
|
||||||
|
.chain(std::fs::read_dir({
|
||||||
|
let mut dir = dirs::data_dir().ok_or(Error::NoConfigDir)?;
|
||||||
|
dir.push("applications");
|
||||||
|
dir
|
||||||
|
})?)
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.filter_map(|p| DesktopEntry::try_from(p.path()).ok())
|
||||||
|
.for_each(|e| {
|
||||||
|
stdout.write_all(e.file_name.as_bytes()).unwrap();
|
||||||
|
stdout.write_all(b"\t").unwrap();
|
||||||
|
stdout.write_all(e.name.as_bytes()).unwrap();
|
||||||
|
stdout.write_all(b"\n").unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue