mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat(plugin): plugin config is pretty printed when saving it
A pretty printed configuration file makes it easier to see the differences between two versions of the configuration file.
This commit is contained in:
parent
928e0c6ee9
commit
58b6e75368
6 changed files with 99 additions and 76 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -2733,9 +2733,10 @@ dependencies = [
|
||||||
"clap_complete_nushell",
|
"clap_complete_nushell",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"md-5",
|
"md-5",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml_edit",
|
"toml",
|
||||||
"vergen",
|
"vergen",
|
||||||
"yazi-dds",
|
"yazi-dds",
|
||||||
"yazi-shared",
|
"yazi-shared",
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,10 @@ anyhow = "1.0.83"
|
||||||
clap = { version = "4.5.4", features = [ "derive" ] }
|
clap = { version = "4.5.4", features = [ "derive" ] }
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
|
serde = { version = "1.0.199", features = [ "derive" ] }
|
||||||
serde_json = "1.0.117"
|
serde_json = "1.0.117"
|
||||||
tokio = { version = "1.37.0", features = [ "full" ] }
|
tokio = { version = "1.37.0", features = [ "full" ] }
|
||||||
toml_edit = "0.22.12"
|
toml = "0.8.12"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1.0.83"
|
anyhow = "1.0.83"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ mod package;
|
||||||
|
|
||||||
use args::*;
|
use args::*;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use package::InstallFromConfig;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
|
@ -34,11 +35,11 @@ async fn main() -> anyhow::Result<()> {
|
||||||
Command::Pack(cmd) => {
|
Command::Pack(cmd) => {
|
||||||
package::init();
|
package::init();
|
||||||
if cmd.install {
|
if cmd.install {
|
||||||
package::Package::install_from_config("plugin", false).await?;
|
package::Package::install_from_config(&InstallFromConfig::Plugin, false).await?;
|
||||||
package::Package::install_from_config("flavor", false).await?;
|
package::Package::install_from_config(&InstallFromConfig::Flavor, false).await?;
|
||||||
} else if cmd.upgrade {
|
} else if cmd.upgrade {
|
||||||
package::Package::install_from_config("plugin", true).await?;
|
package::Package::install_from_config(&InstallFromConfig::Plugin, true).await?;
|
||||||
package::Package::install_from_config("flavor", true).await?;
|
package::Package::install_from_config(&InstallFromConfig::Flavor, true).await?;
|
||||||
} else if let Some(repo) = &cmd.add {
|
} else if let Some(repo) = &cmd.add {
|
||||||
package::Package::add_to_config(repo).await?;
|
package::Package::add_to_config(repo).await?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
yazi-cli/src/package/config.rs
Normal file
30
yazi-cli/src/package/config.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct PackageConfig {
|
||||||
|
pub plugin: PluginConfig,
|
||||||
|
pub flavor: FlavorConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PackageConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { plugin: PluginConfig { deps: vec![] }, flavor: FlavorConfig { deps: vec![] } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct PluginConfig {
|
||||||
|
pub deps: Vec<GitDependency>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct FlavorConfig {
|
||||||
|
pub deps: Vec<GitDependency>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct GitDependency {
|
||||||
|
#[serde(rename = "use")]
|
||||||
|
pub use_: String,
|
||||||
|
pub commit: Option<String>,
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#![allow(clippy::module_inception)]
|
#![allow(clippy::module_inception)]
|
||||||
|
|
||||||
mod add;
|
mod add;
|
||||||
|
mod config;
|
||||||
mod deploy;
|
mod deploy;
|
||||||
mod git;
|
mod git;
|
||||||
mod install;
|
mod install;
|
||||||
|
|
@ -10,6 +11,7 @@ mod upgrade;
|
||||||
|
|
||||||
use git::*;
|
use git::*;
|
||||||
pub(super) use package::*;
|
pub(super) use package::*;
|
||||||
|
pub(super) use parser::*;
|
||||||
|
|
||||||
pub(super) fn init() {
|
pub(super) fn init() {
|
||||||
let root = yazi_shared::Xdg::state_dir().join("packages");
|
let root = yazi_shared::Xdg::state_dir().join("packages");
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use toml_edit::{Array, DocumentMut, InlineTable, Item, Value};
|
|
||||||
use yazi_shared::Xdg;
|
use yazi_shared::Xdg;
|
||||||
|
|
||||||
use super::Package;
|
use super::{config::{GitDependency, PackageConfig}, Package};
|
||||||
|
|
||||||
|
pub enum InstallFromConfig {
|
||||||
|
Plugin,
|
||||||
|
Flavor,
|
||||||
|
}
|
||||||
|
|
||||||
impl Package {
|
impl Package {
|
||||||
pub(crate) async fn add_to_config(use_: &str) -> Result<()> {
|
pub(crate) async fn add_to_config(use_: &str) -> Result<()> {
|
||||||
|
|
@ -11,45 +15,39 @@ impl Package {
|
||||||
let Some(name) = package.name() else { bail!("Invalid package `use`") };
|
let Some(name) = package.name() else { bail!("Invalid package `use`") };
|
||||||
|
|
||||||
let path = Xdg::config_dir().join("package.toml");
|
let path = Xdg::config_dir().join("package.toml");
|
||||||
let mut doc = Self::ensure_config(&fs::read_to_string(&path).await.unwrap_or_default())?;
|
let mut config = Self::parse_config(&fs::read_to_string(&path).await.unwrap_or_default())?;
|
||||||
|
|
||||||
Self::ensure_unique(&doc, name)?;
|
ensure_unique(&config, name)?;
|
||||||
package.add().await?;
|
package.add().await?;
|
||||||
|
|
||||||
let mut table = InlineTable::new();
|
let dep =
|
||||||
table.insert("use", package.use_().as_ref().into());
|
GitDependency { use_: package.use_().as_ref().into(), commit: package.commit.into() };
|
||||||
if !package.commit.is_empty() {
|
|
||||||
table.insert("commit", package.commit.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if package.is_flavor {
|
if package.is_flavor {
|
||||||
doc["flavor"]["deps"].as_array_mut().unwrap().push(table);
|
config.flavor.deps.push(dep);
|
||||||
} else {
|
} else {
|
||||||
doc["plugin"]["deps"].as_array_mut().unwrap().push(table);
|
config.plugin.deps.push(dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::write(path, doc.to_string()).await?;
|
fs::write(path, toml::to_string_pretty(&config)?).await.context("Failed to write package.toml")
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn install_from_config(section: &str, upgrade: bool) -> Result<()> {
|
pub(crate) async fn install_from_config(
|
||||||
|
section: &InstallFromConfig,
|
||||||
|
upgrade: bool,
|
||||||
|
) -> Result<()> {
|
||||||
let path = Xdg::config_dir().join("package.toml");
|
let path = Xdg::config_dir().join("package.toml");
|
||||||
let Ok(s) = fs::read_to_string(&path).await else {
|
let Ok(s) = fs::read_to_string(&path).await else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut doc = s.parse::<DocumentMut>().context("Failed to parse package.toml")?;
|
let mut config = Self::parse_config(&s)?;
|
||||||
let Some(deps) = doc.get_mut(section).and_then(|d| d.get_mut("deps")) else {
|
let deps = match section {
|
||||||
return Ok(());
|
InstallFromConfig::Plugin => &mut config.plugin.deps,
|
||||||
|
InstallFromConfig::Flavor => &mut config.flavor.deps,
|
||||||
};
|
};
|
||||||
|
|
||||||
let deps = deps.as_array_mut().context("`deps` must be an array")?;
|
|
||||||
for dep in deps.iter_mut() {
|
for dep in deps.iter_mut() {
|
||||||
let dep = dep.as_inline_table_mut().context("Dependency must be an inline table")?;
|
let mut package = Package::new(&dep.use_, dep.commit.as_deref());
|
||||||
let use_ = dep.get("use").and_then(|d| d.as_str()).context("Missing `use` field")?;
|
|
||||||
let commit = dep.get("commit").and_then(|d| d.as_str());
|
|
||||||
|
|
||||||
let mut package = Package::new(use_, commit);
|
|
||||||
if upgrade {
|
if upgrade {
|
||||||
package.upgrade().await?;
|
package.upgrade().await?;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -57,57 +55,47 @@ impl Package {
|
||||||
}
|
}
|
||||||
|
|
||||||
if package.commit.is_empty() {
|
if package.commit.is_empty() {
|
||||||
dep.remove("commit");
|
dep.commit.take();
|
||||||
} else {
|
} else {
|
||||||
dep.insert("commit", package.commit.into());
|
dep.commit = Some(package.commit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::write(path, doc.to_string()).await.context("Failed to write package.toml")
|
fs::write(path, toml::to_string_pretty(&config)?).await.context("Failed to write package.toml")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_config(s: &str) -> Result<DocumentMut> {
|
fn parse_config(s: &str) -> Result<PackageConfig, anyhow::Error> {
|
||||||
let mut doc = s.parse::<DocumentMut>().context("Failed to parse package.toml")?;
|
toml::from_str::<PackageConfig>(s).context("Failed to parse package.toml")
|
||||||
|
|
||||||
doc
|
|
||||||
.entry("plugin")
|
|
||||||
.or_insert(toml_edit::table())
|
|
||||||
.as_table_mut()
|
|
||||||
.context("Failed to get `plugin` table")?
|
|
||||||
.entry("deps")
|
|
||||||
.or_insert(Item::Value(Array::new().into()))
|
|
||||||
.as_array()
|
|
||||||
.context("Failed to get `deps` array")?;
|
|
||||||
|
|
||||||
doc
|
|
||||||
.entry("flavor")
|
|
||||||
.or_insert(toml_edit::table())
|
|
||||||
.as_table_mut()
|
|
||||||
.context("Failed to get `flavor` table")?
|
|
||||||
.entry("deps")
|
|
||||||
.or_insert(Item::Value(Array::new().into()))
|
|
||||||
.as_array()
|
|
||||||
.context("Failed to get `deps` array")?;
|
|
||||||
|
|
||||||
Ok(doc)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ensure_unique(doc: &DocumentMut, name: &str) -> Result<()> {
|
fn ensure_unique(doc: &PackageConfig, name: &str) -> Result<()> {
|
||||||
#[inline]
|
if doc.plugin.deps.iter().any(|v| v.use_ == name) {
|
||||||
fn same(v: &Value, name: &str) -> bool {
|
|
||||||
v.as_inline_table()
|
|
||||||
.and_then(|t| t.get("use"))
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.is_some_and(|s| Package::new(s, None).name() == Some(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
if doc["plugin"]["deps"].as_array().unwrap().into_iter().any(|v| same(v, name)) {
|
|
||||||
bail!("Plugin `{name}` already exists in package.toml");
|
bail!("Plugin `{name}` already exists in package.toml");
|
||||||
}
|
}
|
||||||
if doc["flavor"]["deps"].as_array().unwrap().into_iter().any(|v| same(v, name)) {
|
if doc.flavor.deps.iter().any(|v| v.use_ == name) {
|
||||||
bail!("Flavor `{name}` already exists in package.toml");
|
bail!("Flavor `{name}` already exists in package.toml");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::package::config::{FlavorConfig, GitDependency, PluginConfig};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_disallow_duplicate() {
|
||||||
|
let config = PackageConfig::default();
|
||||||
|
|
||||||
|
assert!(ensure_unique(&config, "test").is_ok());
|
||||||
|
|
||||||
|
let config = PackageConfig {
|
||||||
|
plugin: PluginConfig { deps: vec![] },
|
||||||
|
flavor: FlavorConfig { deps: vec![GitDependency { use_: "test".into(), commit: None }] },
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(ensure_unique(&config, "test").is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue