refactor: config

This commit is contained in:
sxyazi 2023-07-14 19:34:24 +08:00
parent 43ec2ada0b
commit 19fcc78a12
No known key found for this signature in database
17 changed files with 237 additions and 230 deletions

View file

@ -1,6 +1,6 @@
use std::{collections::BTreeMap, fmt};
use serde::{de::Visitor, Deserializer};
use serde::{de::{self, Visitor}, Deserializer};
#[derive(Debug, Default)]
pub struct Exec {
@ -44,7 +44,7 @@ impl Exec {
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
A: de::SeqAccess<'de>,
{
let mut execs = Vec::new();
while let Some(value) = &seq.next_element::<String>()? {
@ -55,7 +55,7 @@ impl Exec {
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
E: de::Error,
{
Ok(value.split(';').map(Exec::from).collect())
}

View file

@ -1,7 +1,9 @@
use anyhow::bail;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use serde::{de::Visitor, Deserialize, Deserializer};
use serde::Deserialize;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)]
#[serde(try_from = "String")]
pub struct Key {
pub code: KeyCode,
pub shift: bool,
@ -13,21 +15,36 @@ impl Default for Key {
fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } }
}
impl From<&str> for Key {
fn from(value: &str) -> Self {
let mut key = Default::default();
if value.is_empty() {
return key;
impl From<KeyEvent> for Key {
fn from(value: KeyEvent) -> Self {
let shift = if let KeyCode::Char(c) = value.code { c.is_ascii_uppercase() } else { false };
Self {
code: value.code,
shift: shift || value.modifiers.contains(KeyModifiers::SHIFT),
ctrl: value.modifiers.contains(KeyModifiers::CONTROL),
alt: value.modifiers.contains(KeyModifiers::ALT),
}
}
}
impl TryFrom<String> for Key {
type Error = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> {
if s.is_empty() {
bail!("empty key")
}
if !value.starts_with("<") || !value.ends_with(">") {
let c = value.chars().next().unwrap();
let mut key = Self::default();
if !s.starts_with("<") || !s.ends_with(">") {
let c = s.chars().next().unwrap();
key.code = KeyCode::Char(c);
key.shift = c.is_ascii_uppercase();
return key;
return Ok(key);
}
let mut it = value[1..value.len() - 1].split_inclusive('-').peekable();
let mut it = s[1..s.len() - 1].split_inclusive('-').peekable();
while let Some(x) = it.next() {
match x {
"S-" => key.shift = true,
@ -65,48 +82,13 @@ impl From<&str> for Key {
c if it.peek().is_none() => {
key.code = KeyCode::Char(c.chars().next().unwrap());
}
_ => {}
}
}
key
}
}
impl From<KeyEvent> for Key {
fn from(value: KeyEvent) -> Self {
let shift = if let KeyCode::Char(c) = value.code { c.is_ascii_uppercase() } else { false };
Self {
code: value.code,
shift: shift || value.modifiers.contains(KeyModifiers::SHIFT),
ctrl: value.modifiers.contains(KeyModifiers::CONTROL),
alt: value.modifiers.contains(KeyModifiers::ALT),
}
}
}
impl<'de> Deserialize<'de> for Key {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct KeyVisitor;
impl<'de> Visitor<'de> for KeyVisitor {
type Value = Key;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a key string, e.g. <C-a>")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Key::from(value))
k => bail!("unknown key: {}", k),
}
}
deserializer.deserialize_str(KeyVisitor)
if key.code == KeyCode::Null {
bail!("empty key")
}
Ok(key)
}
}

View file

@ -1,41 +1,52 @@
use std::fs;
use serde::Deserialize;
use serde::{Deserialize, Deserializer};
use xdg::BaseDirectories;
use super::{Exec, Key};
#[derive(Deserialize, Debug)]
#[derive(Debug, Deserialize)]
pub struct Single {
pub on: Vec<Key>,
#[serde(deserialize_with = "Exec::deserialize")]
pub exec: Vec<Exec>,
}
#[derive(Deserialize, Debug)]
#[derive(Debug)]
pub struct Keymap {
pub manager: Vec<Single>,
pub tasks: Vec<Single>,
pub input: Vec<Single>,
}
impl Keymap {
pub fn new() -> Self {
impl<'de> Deserialize<'de> for Keymap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Shadow {
manager: Inner,
tasks: Inner,
input: Inner,
}
#[derive(Deserialize)]
struct Inner {
keymap: Vec<Single>,
}
#[derive(Deserialize)]
struct All {
manager: Inner,
tasks: Inner,
input: Inner,
}
let path = BaseDirectories::new().unwrap().get_config_file("yazi/keymap.toml");
let all: All = toml::from_str(&fs::read_to_string(path).unwrap()).unwrap();
Self { manager: all.manager.keymap, tasks: all.tasks.keymap, input: all.input.keymap }
let shadow = Shadow::deserialize(deserializer)?;
Ok(Self {
manager: shadow.manager.keymap,
tasks: shadow.tasks.keymap,
input: shadow.input.keymap,
})
}
}
impl Keymap {
pub fn new() -> Self {
let path = BaseDirectories::new().unwrap().get_config_file("yazi/keymap.toml");
toml::from_str(&fs::read_to_string(path).unwrap()).unwrap()
}
}

View file

@ -5,7 +5,7 @@ use xdg::BaseDirectories;
use super::SortBy;
#[derive(Deserialize, Debug)]
#[derive(Debug, Deserialize)]
pub struct Manager {
#[serde(skip)]
pub cwd: PathBuf,

View file

@ -1,6 +1,8 @@
use serde::{de::Visitor, Deserialize, Deserializer};
use anyhow::bail;
use serde::Deserialize;
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Deserialize, Clone, Copy)]
#[serde(try_from = "String")]
pub enum SortBy {
Alphabetical,
Created,
@ -8,39 +10,16 @@ pub enum SortBy {
Size,
}
impl From<&str> for SortBy {
fn from(value: &str) -> Self {
match value {
impl TryFrom<String> for SortBy {
type Error = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> {
Ok(match s.as_str() {
"created" => Self::Created,
"modified" => Self::Modified,
"size" => Self::Size,
_ => Self::Alphabetical,
}
}
}
impl<'de> Deserialize<'de> for SortBy {
fn deserialize<D>(deserializer: D) -> Result<SortBy, D::Error>
where
D: Deserializer<'de>,
{
struct SortByVisitor;
impl<'de> Visitor<'de> for SortByVisitor {
type Value = SortBy;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a sort_by string, e.g. modified")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(SortBy::from(value))
}
}
deserializer.deserialize_str(SortByVisitor)
"alphabetical" => Self::Alphabetical,
_ => bail!("invalid sort_by value: {}", s),
})
}
}

View file

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

View file

@ -1,29 +1,18 @@
use std::{collections::BTreeMap, fs, path::Path};
use serde::Deserialize;
use serde::{Deserialize, Deserializer};
use xdg::BaseDirectories;
use super::Opener;
use crate::config::Pattern;
#[derive(Clone, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Opener {
pub cmd: String,
pub args: Vec<String>,
#[serde(default)]
pub block: bool,
#[serde(skip)]
pub spread: bool,
}
#[derive(Deserialize, Debug)]
#[derive(Debug)]
pub struct Open {
#[serde(skip)]
openers: BTreeMap<String, Vec<Opener>>,
rules: Vec<OpenRule>,
rules: Vec<OpenRule>,
}
#[derive(Deserialize, Debug)]
#[derive(Debug, Deserialize)]
struct OpenRule {
name: Option<Pattern>,
mime: Option<Pattern>,
@ -32,23 +21,9 @@ struct OpenRule {
}
impl Open {
pub fn new() -> Open {
#[derive(Deserialize)]
struct Outer {
opener: BTreeMap<String, Vec<Opener>>,
open: Open,
}
pub fn new() -> Self {
let path = BaseDirectories::new().unwrap().get_config_file("yazi/yazi.toml");
let mut outer = toml::from_str::<Outer>(&fs::read_to_string(path).unwrap()).unwrap();
for opener in outer.opener.values_mut() {
for one in opener.iter_mut() {
one.spread = one.args.iter().any(|a| a == "$*");
}
}
Self { openers: outer.opener, rules: outer.open.rules }
toml::from_str(&fs::read_to_string(path).unwrap()).unwrap()
}
pub fn opener(&self, path: &Path, mime: &str) -> Option<&Opener> {
@ -63,3 +38,23 @@ impl Open {
})
}
}
impl<'de> Deserialize<'de> for Open {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Outer {
opener: BTreeMap<String, Vec<Opener>>,
open: OuterOpen,
}
#[derive(Deserialize)]
struct OuterOpen {
rules: Vec<OpenRule>,
}
let outer = Outer::deserialize(deserializer)?;
Ok(Self { openers: outer.opener, rules: outer.open.rules })
}
}

31
src/config/open/opener.rs Normal file
View file

@ -0,0 +1,31 @@
use serde::{Deserialize, Deserializer};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Opener {
pub cmd: String,
pub args: Vec<String>,
pub block: bool,
pub spread: bool,
}
impl<'de> Deserialize<'de> for Opener {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
pub struct Shadow {
pub cmd: String,
pub args: Vec<String>,
#[serde(default)]
pub block: bool,
#[serde(skip)]
pub spread: bool,
}
let shadow = Shadow::deserialize(deserializer)?;
let spread = shadow.args.contains(&"$*".to_string());
Ok(Self { cmd: shadow.cmd, args: shadow.args, block: shadow.block, spread })
}
}

View file

@ -1,8 +1,9 @@
use std::path::Path;
use serde::{de::Visitor, Deserialize, Deserializer};
use serde::Deserialize;
#[derive(Debug)]
#[derive(Debug, Deserialize)]
#[serde(try_from = "String")]
pub struct Pattern {
inner: glob::Pattern,
is_folder: bool,
@ -16,35 +17,17 @@ impl Pattern {
}
}
impl From<&str> for Pattern {
fn from(value: &str) -> Self {
let is_folder = value.ends_with('/');
Self { inner: glob::Pattern::new(value.trim_end_matches('/')).unwrap_or_default(), is_folder }
impl TryFrom<&str> for Pattern {
type Error = anyhow::Error;
fn try_from(s: &str) -> Result<Self, Self::Error> {
let is_folder = s.ends_with('/');
Ok(Self { inner: glob::Pattern::new(s.trim_end_matches('/'))?, is_folder })
}
}
impl<'de> Deserialize<'de> for Pattern {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct PatternVisitor;
impl TryFrom<String> for Pattern {
type Error = anyhow::Error;
impl<'de> Visitor<'de> for PatternVisitor {
type Value = Pattern;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a glob pattern, e.g. *.rs")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Pattern::from(value))
}
}
deserializer.deserialize_str(PatternVisitor)
}
fn try_from(s: String) -> Result<Self, Self::Error> { Self::try_from(s.as_str()) }
}

View file

@ -3,13 +3,7 @@ use std::fs;
use serde::Deserialize;
use xdg::BaseDirectories;
#[derive(Deserialize, Debug)]
pub struct Manager {
pub sort_by: String,
pub sort_reverse: bool,
}
#[derive(Deserialize, Debug)]
#[derive(Debug, Deserialize)]
pub struct Preview {
pub tab_size: u32,
@ -25,6 +19,7 @@ impl Preview {
}
let path = BaseDirectories::new().unwrap().get_config_file("yazi/yazi.toml");
toml::from_str::<Outer>(&fs::read_to_string(path).unwrap()).unwrap().preview
let outer: Outer = toml::from_str(&fs::read_to_string(path).unwrap()).unwrap();
outer.preview
}
}

View file

@ -1,39 +1,42 @@
use anyhow::Result;
use std::ops::Deref;
use anyhow::{bail, Result};
use ratatui::style;
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Color {
pub fg: String,
pub bg: String,
#[serde(try_from = "String")]
pub struct Color(style::Color);
impl Default for Color {
fn default() -> Self { Self(style::Color::Reset) }
}
impl Color {
pub fn fg_rgb(&self) -> style::Color {
if self.fg.len() < 7 {
return style::Color::Reset;
}
let convert = || -> Result<style::Color> {
Ok(style::Color::Rgb(
u8::from_str_radix(&self.fg[1..3], 16)?,
u8::from_str_radix(&self.fg[3..5], 16)?,
u8::from_str_radix(&self.fg[5..7], 16)?,
))
};
convert().unwrap_or(style::Color::Reset)
}
impl TryFrom<String> for Color {
type Error = anyhow::Error;
pub fn bg_rgb(&self) -> style::Color {
if self.bg.len() < 7 {
return style::Color::Reset;
fn try_from(s: String) -> Result<Self, Self::Error> {
if s.len() < 7 {
bail!("Invalid color: {}", s);
}
let convert = || -> Result<style::Color> {
Ok(style::Color::Rgb(
u8::from_str_radix(&self.bg[1..3], 16)?,
u8::from_str_radix(&self.bg[3..5], 16)?,
u8::from_str_radix(&self.bg[5..7], 16)?,
))
};
convert().unwrap_or(style::Color::Reset)
Ok(Self(style::Color::Rgb(
u8::from_str_radix(&s[1..3], 16)?,
u8::from_str_radix(&s[3..5], 16)?,
u8::from_str_radix(&s[5..7], 16)?,
)))
}
}
impl Deref for Color {
type Target = style::Color;
fn deref(&self) -> &Self::Target { &self.0 }
}
#[derive(Deserialize)]
pub struct ColorDual {
#[serde(default)]
pub fg: Color,
#[serde(default)]
pub bg: Color,
}

View file

@ -0,0 +1,29 @@
use serde::{Deserialize, Deserializer};
use crate::config::Pattern;
#[derive(Debug, Deserialize)]
pub struct Filetype {
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
#[serde(default)]
pub bg: String,
#[serde(default)]
pub fg: String,
#[serde(default)]
pub bold: bool,
}
impl Filetype {
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Filetype>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct FiletypeOuter {
rules: Vec<Filetype>,
}
Ok(FiletypeOuter::deserialize(deserializer)?.rules)
}
}

View file

@ -1,6 +1,6 @@
use std::fmt;
use serde::{de::Visitor, Deserializer};
use serde::{de::{self, Visitor}, Deserializer};
use crate::config::Pattern;
@ -11,10 +11,6 @@ pub struct Icon {
}
impl Icon {
pub fn new(name: String, display: String) -> Self {
Self { name: Pattern::from(name.as_ref()), display }
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Icon>, D::Error>
where
D: Deserializer<'de>,
@ -30,11 +26,15 @@ impl Icon {
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
A: de::MapAccess<'de>,
{
let mut icons = Vec::new();
while let Some((key, value)) = &map.next_entry::<String, String>()? {
icons.push(Icon::new(key.clone(), value.clone()));
icons.push(Icon {
name: Pattern::try_from(key.clone())
.map_err(|e| de::Error::custom(e.to_string()))?,
display: value.clone(),
});
}
Ok(icons)
}

View file

@ -1,7 +1,9 @@
mod color;
mod filetype;
mod icon;
mod theme;
pub use color::*;
pub use filetype::*;
pub use icon::*;
pub use theme::*;

View file

@ -3,32 +3,29 @@ use std::{fs, path::PathBuf};
use serde::Deserialize;
use xdg::BaseDirectories;
use super::{Color, Icon};
use super::{ColorDual, Filetype, Icon};
use crate::misc::absolute_path;
#[derive(Deserialize)]
pub struct Mode {
pub normal: Color,
pub select: Color,
pub unselect: Color,
pub normal: ColorDual,
pub select: ColorDual,
pub unselect: ColorDual,
}
#[derive(Deserialize)]
pub struct Tab {
pub active: Color,
pub inactive: Color,
pub active: ColorDual,
pub inactive: ColorDual,
}
#[derive(Deserialize)]
pub struct Selection {
pub normal: Color,
pub hovered: Color,
pub selected: Color,
pub normal: ColorDual,
pub hovered: ColorDual,
pub selected: ColorDual,
}
#[derive(Deserialize)]
pub struct Filetype {}
#[derive(Deserialize)]
pub struct Syntect {
pub theme: PathBuf,
@ -39,10 +36,11 @@ pub struct Theme {
pub mode: Mode,
pub tab: Tab,
pub selection: Selection,
pub filetype: Filetype,
pub syntect: Syntect,
#[serde(deserialize_with = "Filetype::deserialize")]
pub filetypes: Vec<Filetype>,
#[serde(deserialize_with = "Icon::deserialize")]
pub icons: Vec<Icon>,
pub syntect: Syntect,
}
impl Theme {

View file

@ -12,7 +12,7 @@ pub enum Mode {
impl Mode {
#[inline]
pub fn color(&self) -> &theme::Color {
pub fn color(&self) -> &theme::ColorDual {
match *self {
Mode::Normal => &THEME.mode.normal,
Mode::Select(_) => &THEME.mode.select,

View file

@ -29,19 +29,16 @@ impl<'a> Widget for Layout<'a> {
)
.split(area);
Paragraph::new("").style(Style::default().fg(mode.color().bg_rgb())).render(chunks[0], buf);
Paragraph::new("").style(Style::default().fg(*mode.color().bg)).render(chunks[0], buf);
Paragraph::new(format!(" {} ", mode))
.style(
Style::default()
.fg(mode.color().fg_rgb())
.bg(mode.color().bg_rgb())
.add_modifier(Modifier::BOLD),
Style::default().fg(*mode.color().fg).bg(*mode.color().bg).add_modifier(Modifier::BOLD),
)
.render(chunks[1], buf);
Paragraph::new(" master ")
.style(Style::default().fg(mode.color().bg_rgb()).bg(Color::Rgb(72, 77, 102)))
.style(Style::default().fg(*mode.color().bg).bg(Color::Rgb(72, 77, 102)))
.render(chunks[2], buf);
Paragraph::new("").style(Style::default().fg(Color::Rgb(72, 77, 102))).render(chunks[3], buf);