mirror of
https://github.com/chmln/handlr.git
synced 2024-11-14 21:49:27 +01:00
misc improvements
This commit is contained in:
parent
06b5355acc
commit
b6c6433aa3
9 changed files with 432 additions and 321 deletions
476
Cargo.lock
generated
476
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
13
Cargo.toml
13
Cargo.toml
|
@ -9,22 +9,23 @@ description = "Manage mimeapps.list and default applications with ease"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pest = "2.1.3"
|
pest = "2.1.3"
|
||||||
pest_derive = "2.1.0"
|
pest_derive = "2.1.0"
|
||||||
clap = "3.0.0-beta.1"
|
clap = "3.0.0-beta.2"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
itertools = "0.9.0"
|
itertools = "0.9.0"
|
||||||
json = "0.12.4"
|
json = "0.12.4"
|
||||||
shlex = "0.1.1"
|
shlex = "0.1.1"
|
||||||
thiserror = "1.0.19"
|
thiserror = "1.0.22"
|
||||||
ascii_table = "3.0.1"
|
ascii_table = "3.0.1"
|
||||||
xdg = "2.2.0"
|
xdg = "2.2.0"
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
regex = { version = "1.3.9", default-features = false, features = ["std"] }
|
regex = { version = "1.4.2", default-features = false, features = ["std"] }
|
||||||
mime-db = "0.1.5"
|
mime-db = "1.1.0"
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
confy = "0.4.0"
|
confy = "0.4.0"
|
||||||
serde = "1.0.111"
|
serde = "1.0.117"
|
||||||
xdg-mime = "0.3.2"
|
xdg-mime = "0.3.2"
|
||||||
freedesktop_entry_parser = "0.2.2"
|
freedesktop_entry_parser = "1.1.0"
|
||||||
|
once_cell = "1.4.1"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level=3
|
opt-level=3
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use std::{collections::HashMap, convert::TryFrom, ffi::OsStr};
|
use std::{collections::HashMap, convert::TryFrom, ffi::OsStr};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct SystemApps(pub HashMap<Mime, Vec<Handler>>);
|
pub struct SystemApps(pub HashMap<Mime, Vec<Handler>>);
|
||||||
|
|
||||||
impl SystemApps {
|
impl SystemApps {
|
||||||
|
|
122
src/apps/user.rs
122
src/apps/user.rs
|
@ -1,17 +1,18 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
apps::SystemApps,
|
apps::SystemApps,
|
||||||
common::{DesktopEntry, Handler},
|
common::{DesktopEntry, Handler},
|
||||||
Error, Result,
|
Error, Result, CONFIG,
|
||||||
};
|
};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
|
io::Read,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, pest_derive::Parser)]
|
#[derive(Debug, Default, pest_derive::Parser)]
|
||||||
#[grammar = "common/ini.pest"]
|
#[grammar = "common/ini.pest"]
|
||||||
pub struct MimeApps {
|
pub struct MimeApps {
|
||||||
added_associations: HashMap<Mime, VecDeque<Handler>>,
|
added_associations: HashMap<Mime, VecDeque<Handler>>,
|
||||||
|
@ -20,21 +21,17 @@ pub struct MimeApps {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MimeApps {
|
impl MimeApps {
|
||||||
pub fn add_handler(&mut self, mime: Mime, handler: Handler) -> Result<()> {
|
pub fn add_handler(&mut self, mime: Mime, handler: Handler) {
|
||||||
let handlers = self.default_apps.entry(mime).or_default();
|
self.default_apps
|
||||||
handlers.push_back(handler);
|
.entry(mime)
|
||||||
self.save()?;
|
.or_default()
|
||||||
Ok(())
|
.push_back(handler);
|
||||||
}
|
}
|
||||||
pub fn set_handler(&mut self, mime: Mime, handler: Handler) -> Result<()> {
|
|
||||||
self.default_apps.insert(mime, {
|
pub fn set_handler(&mut self, mime: Mime, handler: Handler) {
|
||||||
let mut handlers = VecDeque::with_capacity(1);
|
self.default_apps.insert(mime, vec![handler].into());
|
||||||
handlers.push_back(handler);
|
|
||||||
handlers
|
|
||||||
});
|
|
||||||
self.save()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_handler(&mut self, mime: &Mime) -> Result<()> {
|
pub fn remove_handler(&mut self, mime: &Mime) -> Result<()> {
|
||||||
if let Some(_removed) = self.default_apps.remove(mime) {
|
if let Some(_removed) = self.default_apps.remove(mime) {
|
||||||
self.save()?;
|
self.save()?;
|
||||||
|
@ -42,11 +39,20 @@ impl MimeApps {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn get_handler(&self, mime: &Mime) -> Result<Handler> {
|
|
||||||
let config = crate::config::Config::load()?;
|
|
||||||
|
|
||||||
|
pub fn get_handler(&self, mime: &Mime) -> Result<Handler> {
|
||||||
|
self.get_handler_from_user(mime)
|
||||||
|
.or_else(|_| {
|
||||||
|
let wildcard =
|
||||||
|
Mime::from_str(&format!("{}/*", mime.type_())).unwrap();
|
||||||
|
self.get_handler_from_user(&wildcard)
|
||||||
|
})
|
||||||
|
.or_else(|_| self.get_handler_from_added_associations(mime))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_handler_from_user(&self, mime: &Mime) -> Result<Handler> {
|
||||||
match self.default_apps.get(mime) {
|
match self.default_apps.get(mime) {
|
||||||
Some(handlers) if config.enable_selector && handlers.len() > 1 => {
|
Some(handlers) if CONFIG.enable_selector && handlers.len() > 1 => {
|
||||||
let handlers = handlers
|
let handlers = handlers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|h| (h, h.get_entry().unwrap().name))
|
.map(|h| (h, h.get_entry().unwrap().name))
|
||||||
|
@ -54,7 +60,7 @@ impl MimeApps {
|
||||||
|
|
||||||
let handler = {
|
let handler = {
|
||||||
let name =
|
let name =
|
||||||
config.select(handlers.iter().map(|h| h.1.clone()))?;
|
CONFIG.select(handlers.iter().map(|h| h.1.clone()))?;
|
||||||
|
|
||||||
handlers
|
handlers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -67,21 +73,21 @@ impl MimeApps {
|
||||||
Ok(handler)
|
Ok(handler)
|
||||||
}
|
}
|
||||||
Some(handlers) => Ok(handlers.get(0).unwrap().clone()),
|
Some(handlers) => Ok(handlers.get(0).unwrap().clone()),
|
||||||
None => match self
|
None => Err(Error::NotFound(mime.to_string())),
|
||||||
.added_associations
|
|
||||||
.get(mime)
|
|
||||||
.map(|h| h.get(0).unwrap().clone())
|
|
||||||
.or_else(|| self.system_apps.get_handler(mime))
|
|
||||||
.ok_or(Error::NotFound(mime.to_string()))
|
|
||||||
{
|
|
||||||
Ok(h) => Ok(h),
|
|
||||||
Err(Error::NotFound(_)) if mime.type_() == "text" => self
|
|
||||||
.get_handler(&mime::TEXT_PLAIN)
|
|
||||||
.map_err(|_| Error::NotFound(mime.to_string())),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_handler_from_added_associations(
|
||||||
|
&self,
|
||||||
|
mime: &Mime,
|
||||||
|
) -> Result<Handler> {
|
||||||
|
self.added_associations
|
||||||
|
.get(mime)
|
||||||
|
.map(|h| h.get(0).unwrap().clone())
|
||||||
|
.or_else(|| self.system_apps.get_handler(mime))
|
||||||
|
.ok_or(Error::NotFound(mime.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn show_handler(&self, mime: &Mime, output_json: bool) -> Result<()> {
|
pub fn show_handler(&self, mime: &Mime, output_json: bool) -> Result<()> {
|
||||||
let handler = self.get_handler(mime)?;
|
let handler = self.get_handler(mime)?;
|
||||||
let output = if output_json {
|
let output = if output_json {
|
||||||
|
@ -104,7 +110,16 @@ impl MimeApps {
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
pub fn read() -> Result<Self> {
|
pub fn read() -> Result<Self> {
|
||||||
let raw_conf = std::fs::read_to_string(Self::path()?)?;
|
let raw_conf = {
|
||||||
|
let mut buf = String::new();
|
||||||
|
std::fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.read(true)
|
||||||
|
.open(Self::path()?)?
|
||||||
|
.read_to_string(&mut buf)?;
|
||||||
|
buf
|
||||||
|
};
|
||||||
let file = Self::parse(Rule::file, &raw_conf)?.next().unwrap();
|
let file = Self::parse(Rule::file, &raw_conf)?.next().unwrap();
|
||||||
|
|
||||||
let mut current_section_name = "".to_string();
|
let mut current_section_name = "".to_string();
|
||||||
|
@ -165,6 +180,7 @@ impl MimeApps {
|
||||||
|
|
||||||
let f = std::fs::OpenOptions::new()
|
let f = std::fs::OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.open(Self::path()?)?;
|
.open(Self::path()?)?;
|
||||||
|
@ -226,3 +242,43 @@ impl MimeApps {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wildcard_mimes() -> Result<()> {
|
||||||
|
let mut user_apps = MimeApps::default();
|
||||||
|
user_apps.add_handler(
|
||||||
|
Mime::from_str("video/*").unwrap(),
|
||||||
|
Handler::assume_valid("mpv.desktop".into()),
|
||||||
|
);
|
||||||
|
user_apps.add_handler(
|
||||||
|
Mime::from_str("video/webm").unwrap(),
|
||||||
|
Handler::assume_valid("brave.desktop".into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
user_apps
|
||||||
|
.get_handler(&Mime::from_str("video/mp4")?)?
|
||||||
|
.to_string(),
|
||||||
|
"mpv.desktop"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
user_apps
|
||||||
|
.get_handler(&Mime::from_str("video/asdf")?)?
|
||||||
|
.to_string(),
|
||||||
|
"mpv.desktop"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
user_apps
|
||||||
|
.get_handler(&Mime::from_str("video/webm")?)?
|
||||||
|
.to_string(),
|
||||||
|
"brave.desktop"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result, CONFIG};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
|
@ -17,7 +17,7 @@ pub struct DesktopEntry {
|
||||||
pub(crate) mimes: Vec<Mime>,
|
pub(crate) mimes: Vec<Mime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
Launch,
|
Launch,
|
||||||
Open,
|
Open,
|
||||||
|
@ -55,16 +55,16 @@ impl DesktopEntry {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn get_cmd(&self, arg: Vec<String>) -> Result<(String, Vec<String>)> {
|
pub fn get_cmd(&self, args: Vec<String>) -> Result<(String, Vec<String>)> {
|
||||||
let special = regex::Regex::new("%(f|F|u|U)").unwrap();
|
let special = regex::Regex::new("%(f|F|u|U)").unwrap();
|
||||||
|
|
||||||
let mut split = shlex::split(&self.exec)
|
let mut split = shlex::split(&self.exec)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|s| match s.as_str() {
|
.flat_map(|s| match s.as_str() {
|
||||||
"%f" | "%F" | "%u" | "%U" => arg.clone(),
|
"%f" | "%F" | "%u" | "%U" => args.clone(),
|
||||||
s if special.is_match(s) => vec![special
|
s if special.is_match(s) => vec![special
|
||||||
.replace_all(s, arg.clone().join(" ").as_str())
|
.replace_all(s, args.clone().join(" ").as_str())
|
||||||
.into()],
|
.into()],
|
||||||
_ => vec![s],
|
_ => vec![s],
|
||||||
})
|
})
|
||||||
|
@ -73,9 +73,8 @@ impl DesktopEntry {
|
||||||
// If the entry expects a terminal (emulator), but this process is not running in one, we
|
// If the entry expects a terminal (emulator), but this process is not running in one, we
|
||||||
// launch a new one.
|
// launch a new one.
|
||||||
if self.term && !atty::is(atty::Stream::Stdout) {
|
if self.term && !atty::is(atty::Stream::Stdout) {
|
||||||
let config = crate::config::Config::load()?;
|
|
||||||
let terminal_emulator_args =
|
let terminal_emulator_args =
|
||||||
shlex::split(&config.terminal_emulator).unwrap();
|
shlex::split(&CONFIG.terminal_emulator).unwrap();
|
||||||
split = terminal_emulator_args
|
split = terminal_emulator_args
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(split.into_iter())
|
.chain(split.into_iter())
|
||||||
|
@ -87,32 +86,29 @@ impl DesktopEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_file(path: &Path) -> Option<DesktopEntry> {
|
fn parse_file(path: &Path) -> Option<DesktopEntry> {
|
||||||
let raw = std::fs::read(&path).ok()?;
|
let raw_entry = freedesktop_entry_parser::parse_entry(&path).ok()?;
|
||||||
let parsed = freedesktop_entry_parser::parse_entry(&raw)
|
let section = raw_entry.section("Desktop Entry");
|
||||||
.filter_map(Result::ok)
|
|
||||||
.find(|s| s.title == b"Desktop Entry")?;
|
|
||||||
|
|
||||||
let mut entry = DesktopEntry::default();
|
let mut entry = DesktopEntry::default();
|
||||||
entry.file_name = path.file_name()?.to_owned();
|
entry.file_name = path.file_name()?.to_owned();
|
||||||
|
|
||||||
for attr in parsed.attrs {
|
for attr in section.attrs().into_iter().filter(|a| a.has_value()) {
|
||||||
match attr.name {
|
match attr.name {
|
||||||
b"Name" if entry.name == "" => {
|
"Name" if entry.name == "" => {
|
||||||
entry.name = String::from_utf8(attr.value.into()).ok()?;
|
entry.name = attr.value.unwrap().into();
|
||||||
}
|
}
|
||||||
b"Exec" => {
|
"Exec" => entry.exec = attr.value.unwrap().into(),
|
||||||
entry.exec = String::from_utf8(attr.value.into()).ok()?
|
"MimeType" => {
|
||||||
}
|
let mut mimes = attr
|
||||||
b"MimeType" => {
|
.value
|
||||||
let mut mimes = String::from_utf8(attr.value.into())
|
.unwrap()
|
||||||
.ok()?
|
|
||||||
.split(";")
|
.split(";")
|
||||||
.filter_map(|m| Mime::from_str(m).ok())
|
.filter_map(|m| Mime::from_str(m).ok())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
mimes.pop();
|
mimes.pop();
|
||||||
entry.mimes = mimes;
|
entry.mimes = mimes;
|
||||||
}
|
}
|
||||||
b"Terminal" => entry.term = attr.value == b"true",
|
"Terminal" => entry.term = attr.value.unwrap() == "true",
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ impl MimeType {
|
||||||
[m] if m == &mime::APPLICATION_OCTET_STREAM => {
|
[m] if m == &mime::APPLICATION_OCTET_STREAM => {
|
||||||
Err(Error::Ambiguous(ext.into()))
|
Err(Error::Ambiguous(ext.into()))
|
||||||
}
|
}
|
||||||
[] => unreachable!(),
|
|
||||||
[guess, ..] => Ok(guess.clone()),
|
[guess, ..] => Ok(guess.clone()),
|
||||||
|
[] => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,17 +28,16 @@ impl TryFrom<&str> for MimeType {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(arg: &str) -> Result<Self> {
|
fn try_from(arg: &str) -> Result<Self> {
|
||||||
if let Ok(url) = url::Url::parse(arg) {
|
match url::Url::parse(arg) {
|
||||||
if url.scheme() == "file" {
|
Ok(url) if url.scheme() == "file" => {
|
||||||
return Self::try_from(url.path())
|
Self::try_from(&*PathBuf::from(url.path()))
|
||||||
}
|
}
|
||||||
Ok(Self(
|
Ok(url) => Ok(Self(
|
||||||
format!("x-scheme-handler/{}", url.scheme())
|
format!("x-scheme-handler/{}", url.scheme())
|
||||||
.parse::<Mime>()
|
.parse::<Mime>()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
))
|
)),
|
||||||
} else {
|
Err(_) => Self::try_from(&*PathBuf::from(arg)),
|
||||||
Self::try_from(&*PathBuf::from(arg))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,17 +45,29 @@ impl TryFrom<&str> for MimeType {
|
||||||
impl TryFrom<&Path> for MimeType {
|
impl TryFrom<&Path> for MimeType {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
fn try_from(path: &Path) -> Result<Self> {
|
fn try_from(path: &Path) -> Result<Self> {
|
||||||
match xdg_mime::SharedMimeInfo::new()
|
use mime::APPLICATION_OCTET_STREAM as UNKNOWN;
|
||||||
.guess_mime_type()
|
let db = xdg_mime::SharedMimeInfo::new();
|
||||||
.path(&path)
|
|
||||||
.guess()
|
let name_guess = || match path.file_name() {
|
||||||
.mime_type()
|
Some(f) => db.get_mime_types_from_file_name(&f.to_string_lossy())
|
||||||
{
|
[0]
|
||||||
guess if guess == &mime::APPLICATION_OCTET_STREAM => {
|
.clone(),
|
||||||
Err(Error::Ambiguous(path.to_owned()))
|
None => UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
let content_guess = db.guess_mime_type().path(&path).guess();
|
||||||
|
|
||||||
|
let mime = match (name_guess(), content_guess.mime_type().clone()) {
|
||||||
|
(m1, m2) if m1 == UNKNOWN && m2 == UNKNOWN => {
|
||||||
|
return Err(Error::Ambiguous(path.to_owned()))
|
||||||
}
|
}
|
||||||
guess => Ok(Self(guess.clone())),
|
(m1, m2) if m1 == UNKNOWN => m2,
|
||||||
}
|
(m1, m2) if m2 == UNKNOWN => m1,
|
||||||
|
(m1, m2) if m1 != m2 => m2,
|
||||||
|
(m1, _) => m1,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self(mime.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +121,12 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filename_priority() -> Result<()> {
|
||||||
|
assert_eq!(MimeType::try_from("./tests/p.html")?.0, "text/html");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_ext() -> Result<()> {
|
fn from_ext() -> Result<()> {
|
||||||
assert_eq!(".mp3".parse::<MimeOrExtension>()?.0, "audio/mpeg");
|
assert_eq!(".mp3".parse::<MimeOrExtension>()?.0, "audio/mpeg");
|
||||||
|
|
|
@ -1,49 +1,31 @@
|
||||||
use std::env;
|
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
fn default_terminal() -> std::string::String{
|
pub static CONFIG: Lazy<Config> = Lazy::new(Config::load);
|
||||||
match env::var("TERMINAL") {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => "xterm -e".to_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_selector() -> std::string::String{
|
|
||||||
"rofi -dmenu -p 'Open With: '".to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_enable_selector() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[serde(default="default_enable_selector")]
|
|
||||||
pub enable_selector: bool,
|
pub enable_selector: bool,
|
||||||
|
|
||||||
#[serde(default="default_selector")]
|
|
||||||
pub selector: String,
|
pub selector: String,
|
||||||
|
|
||||||
#[serde(default="default_terminal")]
|
|
||||||
pub terminal_emulator: String,
|
pub terminal_emulator: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// This seems repetitive but serde does not uses Default
|
// This seems repetitive but serde does not uses Default
|
||||||
Config {
|
Config {
|
||||||
enable_selector: default_enable_selector(),
|
enable_selector: false,
|
||||||
selector: default_selector(),
|
selector: std::env::var("TERMINAL").unwrap_or("xterm -e".into()),
|
||||||
terminal_emulator: default_terminal()
|
terminal_emulator: "rofi -dmenu -p 'Open With: '".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn load() -> Result<Self> {
|
pub fn load() -> Self {
|
||||||
Ok(confy::load("handlr")?)
|
confy::load("handlr").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select<O: Iterator<Item = String>>(
|
pub fn select<O: Iterator<Item = String>>(
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -1,4 +1,6 @@
|
||||||
|
use config::CONFIG;
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
mod apps;
|
mod apps;
|
||||||
mod cli;
|
mod cli;
|
||||||
|
@ -12,16 +14,20 @@ fn main() -> Result<()> {
|
||||||
use common::MimeType;
|
use common::MimeType;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
// create config if it doesn't exist
|
||||||
|
Lazy::force(&CONFIG);
|
||||||
|
|
||||||
let mut apps = apps::MimeApps::read()?;
|
let mut apps = apps::MimeApps::read()?;
|
||||||
crate::config::Config::load()?;
|
|
||||||
|
|
||||||
let res = || -> Result<()> {
|
let res = || -> Result<()> {
|
||||||
match Cmd::parse() {
|
match Cmd::parse() {
|
||||||
Cmd::Set { mime, handler } => {
|
Cmd::Set { mime, handler } => {
|
||||||
apps.set_handler(mime.0, handler)?;
|
apps.set_handler(mime.0, handler);
|
||||||
|
apps.save()?;
|
||||||
}
|
}
|
||||||
Cmd::Add { mime, handler } => {
|
Cmd::Add { mime, handler } => {
|
||||||
apps.add_handler(mime.0, handler)?;
|
apps.add_handler(mime.0, handler);
|
||||||
|
apps.save()?;
|
||||||
}
|
}
|
||||||
Cmd::Launch { mime, args } => {
|
Cmd::Launch { mime, args } => {
|
||||||
apps.get_handler(&mime.0)?.launch(args)?;
|
apps.get_handler(&mime.0)?.launch(args)?;
|
||||||
|
|
3
tests/p.html
Normal file
3
tests/p.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<html>
|
||||||
|
<p>asdf</p>
|
||||||
|
</html>
|
Loading…
Reference in a new issue