refactor: prefer FromStr over Default for configuration parsing with side effects

This commit is contained in:
sxyazi 2024-06-18 16:26:29 +08:00
parent 2a35d30f38
commit f35712a768
No known key found for this signature in database
26 changed files with 280 additions and 256 deletions

72
Cargo.lock generated
View File

@ -218,6 +218,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
dependencies = [
"objc2",
]
[[package]]
name = "bstr"
version = "1.9.1"
@ -970,9 +979,9 @@ dependencies = [
[[package]]
name = "imagesize"
version = "0.12.0"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
[[package]]
name = "indexmap"
@ -1150,15 +1159,6 @@ dependencies = [
"which",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "md-5"
version = "0.10.6"
@ -1328,12 +1328,37 @@ dependencies = [
]
[[package]]
name = "objc"
version = "0.2.7"
name = "objc-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
[[package]]
name = "objc2"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
dependencies = [
"malloc_buf",
"objc-sys",
"objc2-encode",
]
[[package]]
name = "objc2-encode"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8"
[[package]]
name = "objc2-foundation"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.5.0",
"block2",
"libc",
"objc2",
]
[[package]]
@ -2210,17 +2235,18 @@ dependencies = [
[[package]]
name = "trash"
version = "4.1.1"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c254b119cf49bdde3dfef21b1dc492dc8026b75566ca24aa77993eccd7cbc1b5"
checksum = "8d8fbfb70b1fad5c0b788f9b2e1bf4d04e5ac6efa828f1ed9ee462c50ff9cf05"
dependencies = [
"chrono",
"libc",
"log",
"objc",
"objc2",
"objc2-foundation",
"once_cell",
"scopeguard",
"url",
"urlencoding",
"windows",
]
@ -2284,6 +2310,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8parse"
version = "0.2.1"
@ -2758,7 +2790,6 @@ dependencies = [
"indexmap",
"ratatui",
"serde",
"shell-words",
"toml",
"validator",
"yazi-shared",
@ -2934,7 +2965,6 @@ dependencies = [
"serde",
"shell-words",
"tokio",
"tracing",
]
[[package]]

View File

@ -13,15 +13,15 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" }
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
# External dependencies
ansi-to-tui = "3.1.0"
ansi-to-tui = "=3.1.0"
anyhow = "1.0.86"
arc-swap = "1.7.1"
base64 = "0.22.1"
color_quant = "1.1.0"
crossterm = "0.27.0"
futures = "0.3.30"
image = "0.24.9"
imagesize = "0.12.0"
image = "=0.24.9"
imagesize = "0.13.0"
kamadak-exif = "0.5.5"
ratatui = "0.26.3"
scopeguard = "1.2.0"

View File

@ -37,6 +37,10 @@ impl Display for Adapter {
impl Adapter {
pub async fn image_show(self, path: &Path, max: Rect) -> Result<Rect> {
if max.is_empty() {
return Ok(Rect::default());
}
match self {
Self::Kitty => Kitty::image_show(path, max).await,
Self::KittyOld => KittyOld::image_show(path, max).await,

View File

@ -20,6 +20,5 @@ globset = "0.4.14"
indexmap = "2.2.6"
ratatui = "0.26.3"
serde = { version = "1.0.203", features = [ "derive" ] }
shell-words = "1.1.0"
toml = { version = "0.8.14", features = [ "preserve_order" ] }
validator = { version = "0.18.1", features = [ "derive" ] }

View File

@ -1,8 +1,10 @@
use std::str::FromStr;
use serde::{Deserialize, Deserializer};
use yazi_shared::Layer;
use super::Control;
use crate::{Preset, MERGED_KEYMAP};
use crate::Preset;
#[derive(Debug)]
pub struct Keymap {
@ -14,6 +16,28 @@ pub struct Keymap {
pub completion: Vec<Control>,
}
impl Keymap {
#[inline]
pub fn get(&self, layer: Layer) -> &Vec<Control> {
match layer {
Layer::App => unreachable!(),
Layer::Manager => &self.manager,
Layer::Tasks => &self.tasks,
Layer::Select => &self.select,
Layer::Input => &self.input,
Layer::Help => &self.help,
Layer::Completion => &self.completion,
Layer::Which => unreachable!(),
}
}
}
impl FromStr for Keymap {
type Err = toml::de::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
}
impl<'de> Deserialize<'de> for Keymap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@ -62,23 +86,3 @@ impl<'de> Deserialize<'de> for Keymap {
})
}
}
impl Default for Keymap {
fn default() -> Self { toml::from_str(&MERGED_KEYMAP).unwrap() }
}
impl Keymap {
#[inline]
pub fn get(&self, layer: Layer) -> &Vec<Control> {
match layer {
Layer::App => unreachable!(),
Layer::Manager => &self.manager,
Layer::Tasks => &self.tasks,
Layer::Select => &self.select,
Layer::Input => &self.input,
Layer::Help => &self.help,
Layer::Completion => &self.completion,
Layer::Which => unreachable!(),
}
}
}

View File

@ -1,5 +1,7 @@
#![allow(clippy::module_inception)]
use std::str::FromStr;
use yazi_shared::{RoCell, Xdg};
pub mod keymap;
@ -15,7 +17,6 @@ pub mod preview;
mod priority;
mod tasks;
pub mod theme;
mod validation;
pub mod which;
pub use layout::*;
@ -23,10 +24,6 @@ pub(crate) use pattern::*;
pub(crate) use preset::*;
pub use priority::*;
static MERGED_YAZI: RoCell<String> = RoCell::new();
static MERGED_KEYMAP: RoCell<String> = RoCell::new();
static MERGED_THEME: RoCell<String> = RoCell::new();
pub static LAYOUT: RoCell<arc_swap::ArcSwap<Layout>> = RoCell::new();
pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
@ -43,23 +40,23 @@ pub static WHICH: RoCell<which::Which> = RoCell::new();
pub fn init() -> anyhow::Result<()> {
let config_dir = Xdg::config_dir();
MERGED_YAZI.init(Preset::yazi(&config_dir)?);
MERGED_KEYMAP.init(Preset::keymap(&config_dir)?);
MERGED_THEME.init(Preset::theme(&config_dir)?);
let yazi_toml = &Preset::yazi(&config_dir)?;
let keymap_toml = &Preset::keymap(&config_dir)?;
let theme_toml = &Preset::theme(&config_dir)?;
LAYOUT.with(Default::default);
KEYMAP.with(Default::default);
LOG.with(Default::default);
MANAGER.with(Default::default);
OPEN.with(Default::default);
PLUGIN.with(Default::default);
PREVIEW.with(Default::default);
TASKS.with(Default::default);
THEME.with(Default::default);
INPUT.with(Default::default);
SELECT.with(Default::default);
WHICH.with(Default::default);
KEYMAP.init(<_>::from_str(keymap_toml)?);
LOG.init(<_>::from_str(yazi_toml)?);
MANAGER.init(<_>::from_str(yazi_toml)?);
OPEN.init(<_>::from_str(yazi_toml)?);
PLUGIN.init(<_>::from_str(yazi_toml)?);
PREVIEW.init(<_>::from_str(yazi_toml)?);
TASKS.init(<_>::from_str(yazi_toml)?);
THEME.init(<_>::from_str(theme_toml)?);
INPUT.init(<_>::from_str(yazi_toml)?);
SELECT.init(<_>::from_str(yazi_toml)?);
WHICH.init(<_>::from_str(yazi_toml)?);
Ok(())
}

View File

@ -1,14 +1,16 @@
use serde::{Deserialize, Deserializer};
use std::str::FromStr;
use crate::MERGED_YAZI;
use serde::{Deserialize, Deserializer};
#[derive(Debug)]
pub struct Log {
pub enabled: bool,
}
impl Default for Log {
fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() }
impl FromStr for Log {
type Err = toml::de::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
}
impl<'de> Deserialize<'de> for Log {

View File

@ -1,8 +1,9 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use validator::Validate;
use super::{ManagerRatio, MouseEvents, SortBy};
use crate::{validation::check_validation, MERGED_YAZI};
#[derive(Debug, Deserialize, Serialize, Validate)]
pub struct Manager {
@ -24,16 +25,18 @@ pub struct Manager {
pub mouse_events: MouseEvents,
}
impl Default for Manager {
fn default() -> Self {
impl FromStr for Manager {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
struct Outer {
manager: Manager,
}
let manager = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().manager;
let manager = toml::from_str::<Outer>(s)?.manager;
manager.validate()?;
check_validation(manager.validate());
manager
Ok(manager)
}
}

View File

@ -16,7 +16,6 @@ bitflags! {
}
impl MouseEvents {
#[inline]
pub const fn draggable(self) -> bool { self.contains(Self::DRAG) }
}

View File

@ -1,11 +1,11 @@
use std::{collections::HashMap, path::Path};
use std::{collections::HashMap, path::Path, str::FromStr};
use indexmap::IndexSet;
use serde::{Deserialize, Deserializer};
use yazi_shared::MIME_DIR;
use super::Opener;
use crate::{open::OpenRule, Preset, MERGED_YAZI};
use crate::{open::OpenRule, Preset};
#[derive(Debug)]
pub struct Open {
@ -13,10 +13,6 @@ pub struct Open {
openers: HashMap<String, IndexSet<Opener>>,
}
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>>
where
@ -58,6 +54,12 @@ impl Open {
}
}
impl FromStr for Open {
type Err = toml::de::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
}
impl<'de> Deserialize<'de> for Open {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where

View File

@ -6,7 +6,9 @@ use crate::{Pattern, Priority};
#[derive(Debug, Deserialize)]
pub struct Fetcher {
#[serde(skip)]
pub id: u8,
pub idx: u8,
pub id: String,
pub cond: Option<Condition>,
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
@ -24,6 +26,6 @@ pub struct FetcherProps {
impl From<&Fetcher> for FetcherProps {
fn from(fetcher: &Fetcher) -> Self {
Self { id: fetcher.id, name: fetcher.run.name.to_owned(), prio: fetcher.prio }
Self { id: fetcher.idx, name: fetcher.run.name.to_owned(), prio: fetcher.prio }
}
}

View File

@ -1,10 +1,10 @@
use std::path::Path;
use std::{path::Path, str::FromStr};
use serde::Deserialize;
use yazi_shared::MIME_DIR;
use super::{Fetcher, Preloader, Previewer};
use crate::{plugin::MAX_PREWORKERS, Preset, MERGED_YAZI};
use crate::{plugin::MAX_PREWORKERS, Preset};
#[derive(Deserialize)]
pub struct Plugin {
@ -13,65 +13,6 @@ pub struct Plugin {
pub previewers: Vec<Previewer>,
}
impl Default for Plugin {
fn default() -> Self {
#[derive(Deserialize)]
struct Outer {
plugin: Shadow,
}
#[derive(Deserialize)]
struct Shadow {
fetchers: Vec<Fetcher>,
#[serde(default)]
prepend_fetchers: Vec<Fetcher>,
#[serde(default)]
append_fetchers: Vec<Fetcher>,
preloaders: Vec<Preloader>,
#[serde(default)]
prepend_preloaders: Vec<Preloader>,
#[serde(default)]
append_preloaders: Vec<Preloader>,
previewers: Vec<Previewer>,
#[serde(default)]
prepend_previewers: Vec<Previewer>,
#[serde(default)]
append_previewers: Vec<Previewer>,
}
let mut shadow = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().plugin;
if shadow.append_previewers.iter().any(|r| r.any_file()) {
shadow.previewers.retain(|r| !r.any_file());
}
if shadow.append_previewers.iter().any(|r| r.any_dir()) {
shadow.previewers.retain(|r| !r.any_dir());
}
Preset::mix(&mut shadow.fetchers, shadow.prepend_fetchers, shadow.append_fetchers);
Preset::mix(&mut shadow.preloaders, shadow.prepend_preloaders, shadow.append_preloaders);
Preset::mix(&mut shadow.previewers, shadow.prepend_previewers, shadow.append_previewers);
if shadow.fetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize {
panic!("Fetchers and preloaders exceed the limit of {MAX_PREWORKERS}");
}
for (i, p) in shadow.fetchers.iter_mut().enumerate() {
p.id = i as u8;
}
for (i, p) in shadow.preloaders.iter_mut().enumerate() {
p.id = shadow.fetchers.len() as u8 + i as u8;
}
Self {
fetchers: shadow.fetchers,
preloaders: shadow.preloaders,
previewers: shadow.previewers,
}
}
}
impl Plugin {
pub fn fetchers(
&self,
@ -118,3 +59,63 @@ impl Plugin {
})
}
}
impl FromStr for Plugin {
type Err = toml::de::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
struct Outer {
plugin: Shadow,
}
#[derive(Deserialize)]
struct Shadow {
fetchers: Vec<Fetcher>,
#[serde(default)]
prepend_fetchers: Vec<Fetcher>,
#[serde(default)]
append_fetchers: Vec<Fetcher>,
preloaders: Vec<Preloader>,
#[serde(default)]
prepend_preloaders: Vec<Preloader>,
#[serde(default)]
append_preloaders: Vec<Preloader>,
previewers: Vec<Previewer>,
#[serde(default)]
prepend_previewers: Vec<Previewer>,
#[serde(default)]
append_previewers: Vec<Previewer>,
}
let mut shadow = toml::from_str::<Outer>(s)?.plugin;
if shadow.append_previewers.iter().any(|r| r.any_file()) {
shadow.previewers.retain(|r| !r.any_file());
}
if shadow.append_previewers.iter().any(|r| r.any_dir()) {
shadow.previewers.retain(|r| !r.any_dir());
}
Preset::mix(&mut shadow.fetchers, shadow.prepend_fetchers, shadow.append_fetchers);
Preset::mix(&mut shadow.preloaders, shadow.prepend_preloaders, shadow.append_preloaders);
Preset::mix(&mut shadow.previewers, shadow.prepend_previewers, shadow.append_previewers);
if shadow.fetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize {
panic!("Fetchers and preloaders exceed the limit of {MAX_PREWORKERS}");
}
for (i, p) in shadow.fetchers.iter_mut().enumerate() {
p.idx = i as u8;
}
for (i, p) in shadow.preloaders.iter_mut().enumerate() {
p.idx = shadow.fetchers.len() as u8 + i as u8;
}
Ok(Self {
fetchers: shadow.fetchers,
preloaders: shadow.preloaders,
previewers: shadow.previewers,
})
}
}

View File

@ -6,7 +6,8 @@ use crate::{Pattern, Priority};
#[derive(Debug, Deserialize)]
pub struct Preloader {
#[serde(skip)]
pub id: u8,
pub idx: u8,
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
pub run: Cmd,
@ -25,6 +26,6 @@ pub struct PreloaderProps {
impl From<&Preloader> for PreloaderProps {
fn from(preloader: &Preloader) -> Self {
Self { id: preloader.id, name: preloader.run.name.to_owned(), prio: preloader.prio }
Self { id: preloader.idx, name: preloader.run.name.to_owned(), prio: preloader.prio }
}
}

View File

@ -1,7 +1,8 @@
use std::str::FromStr;
use serde::Deserialize;
use super::{Offset, Origin};
use crate::MERGED_YAZI;
#[derive(Deserialize)]
pub struct Input {
@ -63,18 +64,19 @@ pub struct Input {
pub quit_offset: Offset,
}
impl Default for Input {
fn default() -> Self {
impl Input {
pub const fn border(&self) -> u16 { 2 }
}
impl FromStr for Input {
type Err = toml::de::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
struct Outer {
input: Input,
}
toml::from_str::<Outer>(&MERGED_YAZI).unwrap().input
Ok(toml::from_str::<Outer>(s)?.input)
}
}
impl Input {
#[inline]
pub const fn border(&self) -> u16 { 2 }
}

View File

@ -1,7 +1,8 @@
use std::str::FromStr;
use serde::Deserialize;
use super::{Offset, Origin};
use crate::MERGED_YAZI;
#[derive(Deserialize)]
pub struct Select {
@ -11,18 +12,19 @@ pub struct Select {
pub open_offset: Offset,
}
impl Default for Select {
fn default() -> Self {
impl Select {
pub const fn border(&self) -> u16 { 2 }
}
impl FromStr for Select {
type Err = toml::de::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
struct Outer {
select: Select,
}
toml::from_str::<Outer>(&MERGED_YAZI).unwrap().select
Ok(toml::from_str::<Outer>(s)?.select)
}
}
impl Select {
#[inline]
pub const fn border(&self) -> u16 { 2 }
}

View File

@ -1,4 +1,4 @@
use std::{mem, path::{Path, PathBuf}};
use std::{borrow::Cow, mem, path::{Path, PathBuf}};
use anyhow::{anyhow, Context, Result};
use toml::{Table, Value};
@ -8,17 +8,17 @@ use crate::theme::Flavor;
pub(crate) struct Preset;
impl Preset {
pub(crate) fn yazi(p: &Path) -> Result<String> {
pub(crate) fn yazi(p: &Path) -> Result<Cow<str>> {
Self::merge_path(p.join("yazi.toml"), include_str!("../preset/yazi.toml"))
}
pub(crate) fn keymap(p: &Path) -> Result<String> {
pub(crate) fn keymap(p: &Path) -> Result<Cow<str>> {
Self::merge_path(p.join("keymap.toml"), include_str!("../preset/keymap.toml"))
}
pub(crate) fn theme(p: &Path) -> Result<String> {
pub(crate) fn theme(p: &Path) -> Result<Cow<str>> {
let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else {
return Ok(include_str!("../preset/theme.toml").to_owned());
return Ok(include_str!("../preset/theme.toml").into());
};
let Some(use_) = Flavor::parse_use(&user) else {
return Self::merge_str(&user, include_str!("../preset/theme.toml"));
@ -37,18 +37,18 @@ impl Preset {
}
#[inline]
pub(crate) fn merge_str(user: &str, base: &str) -> Result<String> {
pub(crate) fn merge_str(user: &str, base: &str) -> Result<Cow<'static, str>> {
let mut t = user.parse()?;
Self::merge(&mut t, base.parse()?, 2);
Ok(t.to_string())
Ok(t.to_string().into())
}
#[inline]
fn merge_path(user: PathBuf, base: &str) -> Result<String> {
fn merge_path(user: PathBuf, base: &str) -> Result<Cow<str>> {
let s = std::fs::read_to_string(&user).unwrap_or_default();
if s.is_empty() {
return Ok(base.to_string());
return Ok(base.into());
}
Self::merge_str(&s, base).with_context(|| anyhow!("Loading {user:?}"))

View File

@ -1,10 +1,11 @@
use std::{path::PathBuf, time::{self, SystemTime}};
use std::{path::PathBuf, str::FromStr, time::{self, SystemTime}};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use validator::Validate;
use yazi_shared::fs::expand_path;
use crate::{validation::check_validation, Xdg, MERGED_YAZI};
use crate::Xdg;
#[derive(Debug, Serialize)]
pub struct Preview {
@ -22,8 +23,18 @@ pub struct Preview {
pub ueberzug_offset: (f32, f32, f32, f32),
}
impl Default for Preview {
fn default() -> Self {
impl Preview {
#[inline]
pub fn tmpfile(&self, prefix: &str) -> PathBuf {
let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos();
self.cache_dir.join(format!("{prefix}-{}", nanos / 1000))
}
}
impl FromStr for Preview {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
struct Outer {
preview: Shadow,
@ -46,14 +57,14 @@ impl Default for Preview {
ueberzug_offset: (f32, f32, f32, f32),
}
let preview = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().preview;
check_validation(preview.validate());
let preview = toml::from_str::<Outer>(s)?.preview;
preview.validate()?;
let cache_dir =
preview.cache_dir.filter(|p| !p.is_empty()).map_or_else(Xdg::cache_dir, expand_path);
std::fs::create_dir_all(&cache_dir).expect("Failed to create cache directory");
std::fs::create_dir_all(&cache_dir).context("Failed to create cache directory")?;
Preview {
Ok(Preview {
tab_size: preview.tab_size,
max_width: preview.max_width,
max_height: preview.max_height,
@ -66,14 +77,6 @@ impl Default for Preview {
ueberzug_scale: preview.ueberzug_scale,
ueberzug_offset: preview.ueberzug_offset,
}
}
}
impl Preview {
#[inline]
pub fn tmpfile(&self, prefix: &str) -> PathBuf {
let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos();
self.cache_dir.join(format!("{prefix}-{}", nanos / 1000))
})
}
}

View File

@ -1,8 +1,8 @@
use std::str::FromStr;
use serde::Deserialize;
use validator::Validate;
use crate::{validation::check_validation, MERGED_YAZI};
#[derive(Debug, Deserialize, Validate)]
pub struct Tasks {
#[validate(range(min = 1, message = "Cannot be less than 1"))]
@ -18,16 +18,18 @@ pub struct Tasks {
pub suppress_preload: bool,
}
impl Default for Tasks {
fn default() -> Self {
impl FromStr for Tasks {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
struct Outer {
tasks: Tasks,
}
let tasks = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().tasks;
check_validation(tasks.validate());
let tasks = toml::from_str::<Outer>(s)?.tasks;
tasks.validate()?;
tasks
Ok(tasks)
}
}

View File

@ -1,11 +1,10 @@
use std::path::PathBuf;
use std::{path::PathBuf, str::FromStr};
use serde::{Deserialize, Serialize};
use validator::Validate;
use yazi_shared::{fs::expand_path, theme::Style, Xdg};
use super::{Filetype, Flavor, Icons};
use crate::{validation::check_validation, MERGED_THEME};
#[derive(Deserialize, Serialize)]
pub struct Theme {
@ -27,12 +26,13 @@ pub struct Theme {
pub icons: Icons,
}
impl Default for Theme {
fn default() -> Self {
let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap();
impl FromStr for Theme {
type Err = anyhow::Error;
check_validation(theme.manager.validate());
check_validation(theme.which.validate());
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut theme: Self = toml::from_str(s)?;
theme.manager.validate()?;
theme.which.validate()?;
if theme.flavor.use_.is_empty() {
theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme);
@ -41,7 +41,7 @@ impl Default for Theme {
Xdg::config_dir().join(format!("flavors/{}.yazi/tmtheme.xml", theme.flavor.use_));
}
theme
Ok(theme)
}
}

View File

@ -1,29 +0,0 @@
use std::{borrow::Cow, process};
use validator::{ValidationErrors, ValidationErrorsKind};
pub fn check_validation(res: Result<(), ValidationErrors>) {
let Err(errors) = res else { return };
for (field, kind) in errors.into_errors() {
match kind {
ValidationErrorsKind::Struct(errors) => check_validation(Err(*errors)),
ValidationErrorsKind::List(errors) => {
for (i, errors) in errors {
eprint!("Config `{field}[{i}]` format error: ");
check_validation(Err(*errors));
eprintln!();
}
}
ValidationErrorsKind::Field(error) => {
for e in error {
eprintln!(
"Config `{field}` format error: {}\n",
e.message.unwrap_or(Cow::Borrowed("unknown error"))
);
}
}
}
}
process::exit(1);
}

View File

@ -1,8 +1,9 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use validator::Validate;
use super::SortBy;
use crate::MERGED_YAZI;
#[derive(Debug, Deserialize, Serialize, Validate)]
pub struct Which {
@ -13,13 +14,15 @@ pub struct Which {
pub sort_translit: bool,
}
impl Default for Which {
fn default() -> Self {
impl FromStr for Which {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
struct Outer {
which: Which,
}
toml::from_str::<Outer>(&MERGED_YAZI).unwrap().which
Ok(toml::from_str::<Outer>(s)?.which)
}
}

View File

@ -19,11 +19,11 @@ impl Tasks {
for p in PLUGIN.fetchers(&f.url, mime, factors) {
match loaded.get_mut(&f.url) {
Some(n) if *n & (1 << p.id) != 0 => continue,
Some(n) => *n |= 1 << p.id,
None => _ = loaded.insert(f.url.clone(), 1 << p.id),
Some(n) if *n & (1 << p.idx) != 0 => continue,
Some(n) => *n |= 1 << p.idx,
None => _ = loaded.insert(f.url.clone(), 1 << p.idx),
}
tasks[p.id as usize].push(f.clone());
tasks[p.idx as usize].push(f.clone());
}
}
@ -41,9 +41,9 @@ impl Tasks {
let mime = if f.is_dir() { Some(MIME_DIR) } else { mimetype.get(&f.url).map(|s| &**s) };
for p in PLUGIN.preloaders(&f.url, mime) {
match loaded.get_mut(&f.url) {
Some(n) if *n & (1 << p.id) != 0 => continue,
Some(n) => *n |= 1 << p.id,
None => _ = loaded.insert(f.url.clone(), 1 << p.id),
Some(n) if *n & (1 << p.idx) != 0 => continue,
Some(n) => *n |= 1 << p.idx,
None => _ = loaded.insert(f.url.clone(), 1 << p.idx),
}
self.scheduler.preload_paged(p, f);
}

View File

@ -21,7 +21,7 @@ yazi-proxy = { path = "../yazi-proxy", version = "0.2.5" }
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
# External dependencies
ansi-to-tui = "3.1.0"
ansi-to-tui = "=3.1.0"
anyhow = "1.0.86"
base64 = "0.22.1"
crossterm = "0.27.0"

View File

@ -30,4 +30,4 @@ tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_lev
libc = "0.2.155"
[target.'cfg(not(target_os = "android"))'.dependencies]
trash = "4.1.1"
trash = "5.0.0"

View File

@ -24,8 +24,5 @@ serde = { version = "1.0.203", features = [ "derive" ] }
shell-words = "1.1.0"
tokio = { version = "1.38.0", features = [ "full" ] }
# Logging
tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] }
[target."cfg(unix)".dependencies]
libc = "0.2.155"

View File

@ -13,7 +13,7 @@ impl<T> RoCell<T> {
#[inline]
pub fn init(&self, value: T) {
debug_assert!(!self.is_initialized());
debug_assert!(!self.initialized());
unsafe {
*self.0.get() = Some(value);
}
@ -29,25 +29,25 @@ impl<T> RoCell<T> {
#[inline]
pub fn replace(&self, value: T) -> T {
debug_assert!(self.is_initialized());
debug_assert!(self.initialized());
unsafe { mem::replace(&mut *self.0.get(), Some(value)).unwrap_unchecked() }
}
#[inline]
pub fn drop(&self) -> T {
debug_assert!(self.is_initialized());
debug_assert!(self.initialized());
unsafe { mem::take(&mut *self.0.get()).unwrap_unchecked() }
}
#[inline]
fn is_initialized(&self) -> bool { unsafe { (*self.0.get()).is_some() } }
fn initialized(&self) -> bool { unsafe { (*self.0.get()).is_some() } }
}
impl<T> Deref for RoCell<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
debug_assert!(self.is_initialized());
debug_assert!(self.initialized());
unsafe { (*self.0.get()).as_ref().unwrap_unchecked() }
}
}