Compare commits
2 commits
2812a556e1
...
3bea615b52
Author | SHA1 | Date | |
---|---|---|---|
3bea615b52 | |||
6ba09c84f7 |
4 changed files with 105 additions and 40 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "open"
|
name = "open"
|
||||||
version = "1.0.0"
|
version = "2.0.0"
|
||||||
authors = ["Valerie Wolfe <sleeplessval@gmail.com>"]
|
authors = ["Valerie Wolfe <sleeplessval@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|
70
man/open.1
Normal file
70
man/open.1
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
.Dd $Mdocdate$
|
||||||
|
.Dt OPEN 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm open
|
||||||
|
.Nd opens files with a user-defined program.
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm open
|
||||||
|
.Op Ar file
|
||||||
|
.Nm open
|
||||||
|
.Op Fl hpv
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is a replacement for
|
||||||
|
.Xr xdg-open 1
|
||||||
|
that is more easily configurable with a TOML file. Its options are as follows:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl h\ |\ --help
|
||||||
|
Displays a brief help text.
|
||||||
|
.It Fl p\ |\ --path
|
||||||
|
Displays the path to the configuration file being used.
|
||||||
|
.It Fl v\ |\ --version
|
||||||
|
Displays version information.
|
||||||
|
.It Ar file
|
||||||
|
The file to open. If not provided, the current directory is used.
|
||||||
|
.El
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width DS
|
||||||
|
.It $HOME/.config/open.toml
|
||||||
|
The global configuration file in TOML format.
|
||||||
|
.It .open
|
||||||
|
The local configuration file in TOML format.
|
||||||
|
.Nm open
|
||||||
|
will search upwards to try to find a local file. Local configuration items are prioritized.
|
||||||
|
.El
|
||||||
|
.Sh CONFIGURATION
|
||||||
|
Files can be matched on extension or exact name. Filenames are in the 'filename' array, and extensions are in the 'extension' array.
|
||||||
|
.Pp
|
||||||
|
.Dl [[extension]]
|
||||||
|
.Dl match = (string or array; matching value(s))
|
||||||
|
.Dl command = (string; the command to open with)
|
||||||
|
.Dl shell = (boolean; decides if the command is run in the terminal)
|
||||||
|
.Pp
|
||||||
|
.Pp
|
||||||
|
The "dir" section is used to set associations for directories:
|
||||||
|
.Pp
|
||||||
|
.Dl [dir]
|
||||||
|
.Dl command = (string; the command to open with)
|
||||||
|
.Dl shell = (boolean; decides if the command is run in the terminal)
|
||||||
|
.Pp
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It 1
|
||||||
|
No configuration file was found.
|
||||||
|
.It 4
|
||||||
|
The target file does not exist.
|
||||||
|
.It 5
|
||||||
|
No matching configuration section was found for the target file.
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr xdg-open 1 ,
|
||||||
|
.Xr open 3p
|
||||||
|
.Sh AUTHORS
|
||||||
|
.An -nosplit
|
||||||
|
.An Valerie Wolfe Aq Mt sleeplessval@gmail.com
|
||||||
|
.Sh BUGS
|
||||||
|
.Nm
|
||||||
|
hides the
|
||||||
|
.Xr open 3p
|
||||||
|
builtin, breaking convention and possibly some older script files.
|
56
src/main.rs
56
src/main.rs
|
@ -6,10 +6,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use pico_args::Arguments;
|
use pico_args::Arguments;
|
||||||
use toml::value::{ Array, Value };
|
use toml::value::Value;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod util;
|
||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -33,17 +35,9 @@ fn main() {
|
||||||
|
|
||||||
// path flag (-p / --path)
|
// path flag (-p / --path)
|
||||||
if args.contains(["-p", "--path"]) {
|
if args.contains(["-p", "--path"]) {
|
||||||
let local = config.local_path;
|
if let Some(local) = config.local_path { println!("{local}"); }
|
||||||
let global = config.global_path;
|
else if let Some(global) = config.global_path { println!("{global}"); }
|
||||||
if local.is_some() {
|
else { error::no_configs(); }
|
||||||
println!("{}", local.unwrap());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if global.is_some() {
|
|
||||||
println!("{}", global.unwrap());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error::no_configs();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,16 +48,17 @@ fn main() {
|
||||||
if !target.exists() { error::not_found(&target); }
|
if !target.exists() { error::not_found(&target); }
|
||||||
|
|
||||||
// get section
|
// get section
|
||||||
// ordering: filename -> type (ext/dir)
|
// ordering: filename -> type (ext/dir) -> default
|
||||||
let mut section = None;
|
let mut section = None;
|
||||||
|
|
||||||
// by exact filename
|
// by exact filename
|
||||||
let filename = target.file_name();
|
if let Some(filename) = target.file_name() {
|
||||||
if filename.is_some() {
|
let array = config.get("filename");
|
||||||
let filename_section = config.get(filename.unwrap().to_str().unwrap());
|
let matches: Vec<Value>;
|
||||||
if filename_section.is_some() {
|
if let Some(Value::Array(array)) = array { matches = util::matches(array, filename.to_string_lossy().into()); }
|
||||||
section = filename_section;
|
else { matches = Vec::new(); }
|
||||||
}
|
|
||||||
|
section = if matches.len() > 0 { matches.get(0).cloned() } else { None };
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle types; dir first
|
// handle types; dir first
|
||||||
|
@ -76,24 +71,13 @@ fn main() {
|
||||||
|
|
||||||
// handle types; extensions second
|
// handle types; extensions second
|
||||||
if section.is_none() {
|
if section.is_none() {
|
||||||
let extension = target.extension();
|
if let Some(extension) = target.extension() {
|
||||||
if extension.is_some() {
|
let array: Option<Value> = config.get("extension");
|
||||||
let extension = extension.unwrap().to_str();
|
let matches: Vec<Value>;
|
||||||
|
if let Some(Value::Array(array)) = array { matches = util::matches(array, extension.to_string_lossy().into()); }
|
||||||
|
else { matches = Vec::new(); }
|
||||||
|
|
||||||
// pull extension array and filter matches
|
section = if matches.len() > 0 { matches.get(0).cloned() } else { None };
|
||||||
let i_macrosection: Option<Value> = config.get("extension");
|
|
||||||
let macrosection: Array = i_macrosection.unwrap().as_array().unwrap().to_owned();
|
|
||||||
let matches = macrosection.iter().filter(|value| {
|
|
||||||
let table = value.as_table().unwrap();
|
|
||||||
let i_target = table.get("match").unwrap();
|
|
||||||
let target = i_target.as_str();
|
|
||||||
target == extension
|
|
||||||
}).map(|value| value.to_owned() );
|
|
||||||
|
|
||||||
let sections: Vec<Value> = matches.collect();
|
|
||||||
if sections.len() > 0 {
|
|
||||||
section = sections.get(0).cloned();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
src/util.rs
17
src/util.rs
|
@ -1,5 +1,16 @@
|
||||||
use std::{
|
|
||||||
process::{ Command, Stdio }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use toml::{ value::Array, Value };
|
||||||
|
|
||||||
|
/// gets array entries with matching "match" values.
|
||||||
|
pub fn matches(macrosection: Array, to_match: String) -> Vec<Value> {
|
||||||
|
macrosection.iter().filter(|value| {
|
||||||
|
if let Some(table) = value.as_table() {
|
||||||
|
match table.get("match").unwrap() {
|
||||||
|
Value::String(target) => *target == to_match,
|
||||||
|
Value::Array(values) => values.contains(&Value::String(to_match.clone())),
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
} else { false }
|
||||||
|
}).map(|value| value.to_owned()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue