Skip to content

Commit

Permalink
feat: multiple openers for a single rule
Browse files Browse the repository at this point in the history
  • Loading branch information
Linus789 committed Sep 14, 2023
1 parent 9c6b3c9 commit 37880d3
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 10 deletions.
2 changes: 2 additions & 0 deletions config/docs/yazi.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ rules = [

# { mime = "application/json", use = "text" },
{ name = "*.json", use = "text" },

{ name = "*.html", use = [ "browser", "text" ] },
]
```

Expand Down
70 changes: 62 additions & 8 deletions config/src/open/open.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{collections::BTreeMap, path::Path};
use std::{collections::BTreeMap, fmt, path::Path};

use indexmap::IndexSet;
use serde::{Deserialize, Deserializer};
use serde::{de::{self, Visitor}, Deserialize, Deserializer};
use shared::MIME_DIR;

use super::Opener;
Expand All @@ -18,15 +18,16 @@ struct OpenRule {
name: Option<Pattern>,
mime: Option<Pattern>,
#[serde(rename = "use")]
use_: String,
#[serde(deserialize_with = "deserialize_from_str_or_vec")]
use_: Vec<String>,
}

impl Default for Open {
fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() }
}

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
P: AsRef<Path>,
M: AsRef<str>,
Expand All @@ -36,7 +37,18 @@ impl Open {
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))
{
self.openers.get(&rule.use_)
let openers = rule
.use_
.iter()
.filter_map(|use_name| self.openers.get(use_name))
.flatten()
.collect::<IndexSet<_>>();

if openers.is_empty() {
return None;
}

Some(openers)
} else {
None
}
Expand All @@ -49,13 +61,13 @@ impl Open {
P: AsRef<Path>,
M: AsRef<str>,
{
self.openers(path, mime).and_then(|o| o.iter().find(|o| o.block))
self.openers(path, mime).and_then(|o| o.iter().find(|o| o.block).copied())
}

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 flat = grouped.iter().flat_map(|&g| g).collect::<IndexSet<_>>();
flat.into_iter().filter(|&o| grouped.iter().all(|g| g.contains(o))).collect()
let flat = grouped.iter().flatten().collect::<IndexSet<_>>();
flat.into_iter().filter(|&o| grouped.iter().all(|g| g.contains(o))).copied().collect()
}
}

Expand All @@ -79,3 +91,45 @@ impl<'de> Deserialize<'de> for Open {
Ok(Self { openers, rules: outer.open.rules })
}
}

fn deserialize_from_str_or_vec<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
struct StringVisitor;

impl<'de> Visitor<'de> for StringVisitor {
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 strs = Vec::new();
while let Some(value) = seq.next_element::<String>()? {
strs.push(value);
}
Ok(strs)
}

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(StringVisitor)
}
8 changes: 6 additions & 2 deletions core/src/tasks/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,12 @@ impl Tasks {

pub fn file_open(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> bool {
let mut openers = BTreeMap::new();
for (path, mime) in targets {
if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first()) {
let valid_openers = targets
.iter()
.filter_map(|(path, mime)| OPEN.openers(path, mime).map(|o| (path, o)))
.collect::<Vec<_>>();
for (path, opener) in &valid_openers {
if let Some(opener) = opener.first() {
openers.entry(opener).or_insert_with(Vec::new).push(path.as_ref().as_os_str());
}
}
Expand Down

0 comments on commit 37880d3

Please sign in to comment.