feat: multiple openers for a single rule (#154)

This commit is contained in:
Linus 2023-09-16 23:20:04 +00:00 committed by GitHub
parent a04faa9dbc
commit 09243716a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 15 deletions

View file

@ -81,6 +81,9 @@ rules = [
# { mime = "application/json", use = "text" }, # { mime = "application/json", use = "text" },
{ name = "*.json", use = "text" }, { name = "*.json", use = "text" },
# Multiple openers for a single rule
{ name = "*.html", use = [ "browser", "text" ] },
] ]
``` ```

View file

@ -1,5 +1,7 @@
mod open; mod open;
mod opener; mod opener;
mod rule;
pub use open::*; pub use open::*;
pub use opener::*; pub use opener::*;
use rule::*;

View file

@ -5,7 +5,7 @@ use serde::{Deserialize, Deserializer};
use shared::MIME_DIR; use shared::MIME_DIR;
use super::Opener; use super::Opener;
use crate::{Pattern, MERGED_YAZI}; use crate::{open::OpenRule, MERGED_YAZI};
#[derive(Debug)] #[derive(Debug)]
pub struct Open { pub struct Open {
@ -13,20 +13,12 @@ pub struct Open {
rules: Vec<OpenRule>, rules: Vec<OpenRule>,
} }
#[derive(Debug, Deserialize)]
struct OpenRule {
name: Option<Pattern>,
mime: Option<Pattern>,
#[serde(rename = "use")]
use_: String,
}
impl Default for Open { impl Default for Open {
fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() } fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() }
} }
impl Open { impl Open {
pub fn openers<P, M>(&self, path: P, mime: M) -> Option<&IndexSet<Opener>> pub fn openers<P, M>(&self, path: P, mime: M) -> Option<IndexSet<&Opener>>
where where
P: AsRef<Path>, P: AsRef<Path>,
M: AsRef<str>, M: AsRef<str>,
@ -36,7 +28,14 @@ impl Open {
if rule.mime.as_ref().map_or(false, |m| m.matches(&mime)) if rule.mime.as_ref().map_or(false, |m| m.matches(&mime))
|| rule.name.as_ref().map_or(false, |n| n.match_path(&path, is_folder)) || rule.name.as_ref().map_or(false, |n| n.match_path(&path, is_folder))
{ {
self.openers.get(&rule.use_) let openers = rule
.use_
.iter()
.filter_map(|use_| self.openers.get(use_))
.flatten()
.collect::<IndexSet<_>>();
if openers.is_empty() { None } else { Some(openers) }
} else { } else {
None None
} }
@ -49,12 +48,12 @@ impl Open {
P: AsRef<Path>, P: AsRef<Path>,
M: AsRef<str>, M: AsRef<str>,
{ {
self.openers(path, mime).and_then(|o| o.iter().find(|o| o.block)) self.openers(path, mime).and_then(|o| o.into_iter().find(|o| o.block))
} }
pub fn common_openers(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> Vec<&Opener> { pub fn common_openers(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> Vec<&Opener> {
let grouped = targets.iter().filter_map(|(p, m)| self.openers(p, m)).collect::<Vec<_>>(); let grouped: Vec<_> = targets.iter().filter_map(|(p, m)| self.openers(p, m)).collect();
let flat = grouped.iter().flat_map(|&g| g).collect::<IndexSet<_>>(); let flat: IndexSet<_> = grouped.iter().flatten().copied().collect();
flat.into_iter().filter(|&o| grouped.iter().all(|g| g.contains(o))).collect() flat.into_iter().filter(|&o| grouped.iter().all(|g| g.contains(o))).collect()
} }
} }

58
config/src/open/rule.rs Normal file
View file

@ -0,0 +1,58 @@
use std::fmt;
use serde::{de::{self, Visitor}, Deserialize, Deserializer};
use crate::pattern::Pattern;
#[derive(Debug, Deserialize)]
pub(super) struct OpenRule {
pub(super) name: Option<Pattern>,
pub(super) mime: Option<Pattern>,
#[serde(rename = "use")]
#[serde(deserialize_with = "OpenRule::deserialize")]
pub(super) use_: Vec<String>,
}
impl OpenRule {
fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
struct UseVisitor;
impl<'de> Visitor<'de> for UseVisitor {
type Value = Vec<String>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string, or array of strings")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut uses = Vec::new();
while let Some(use_) = seq.next_element::<String>()? {
uses.push(use_);
}
Ok(uses)
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![value.to_owned()])
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![v])
}
}
deserializer.deserialize_any(UseVisitor)
}
}

View file

@ -134,7 +134,7 @@ impl Tasks {
pub fn file_open(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> bool { pub fn file_open(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> bool {
let mut openers = BTreeMap::new(); let mut openers = BTreeMap::new();
for (path, mime) in targets { for (path, mime) in targets {
if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first()) { if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first().copied()) {
openers.entry(opener).or_insert_with(Vec::new).push(path.as_ref().as_os_str()); openers.entry(opener).or_insert_with(Vec::new).push(path.as_ref().as_os_str());
} }
} }