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",
|
||||
"crossterm",
|
||||
"md-5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"toml_edit",
|
||||
"toml",
|
||||
"vergen",
|
||||
"yazi-dds",
|
||||
"yazi-shared",
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ anyhow = "1.0.83"
|
|||
clap = { version = "4.5.4", features = [ "derive" ] }
|
||||
crossterm = "0.27.0"
|
||||
md-5 = "0.10.6"
|
||||
serde = { version = "1.0.199", features = [ "derive" ] }
|
||||
serde_json = "1.0.117"
|
||||
tokio = { version = "1.37.0", features = [ "full" ] }
|
||||
toml_edit = "0.22.12"
|
||||
toml = "0.8.12"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.83"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ mod package;
|
|||
|
||||
use args::*;
|
||||
use clap::Parser;
|
||||
use package::InstallFromConfig;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
|
|
@ -34,11 +35,11 @@ async fn main() -> anyhow::Result<()> {
|
|||
Command::Pack(cmd) => {
|
||||
package::init();
|
||||
if cmd.install {
|
||||
package::Package::install_from_config("plugin", false).await?;
|
||||
package::Package::install_from_config("flavor", false).await?;
|
||||
package::Package::install_from_config(&InstallFromConfig::Plugin, false).await?;
|
||||
package::Package::install_from_config(&InstallFromConfig::Flavor, false).await?;
|
||||
} else if cmd.upgrade {
|
||||
package::Package::install_from_config("plugin", true).await?;
|
||||
package::Package::install_from_config("flavor", true).await?;
|
||||
package::Package::install_from_config(&InstallFromConfig::Plugin, true).await?;
|
||||
package::Package::install_from_config(&InstallFromConfig::Flavor, true).await?;
|
||||
} else if let Some(repo) = &cmd.add {
|
||||
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)]
|
||||
|
||||
mod add;
|
||||
mod config;
|
||||
mod deploy;
|
||||
mod git;
|
||||
mod install;
|
||||
|
|
@ -10,6 +11,7 @@ mod upgrade;
|
|||
|
||||
use git::*;
|
||||
pub(super) use package::*;
|
||||
pub(super) use parser::*;
|
||||
|
||||
pub(super) fn init() {
|
||||
let root = yazi_shared::Xdg::state_dir().join("packages");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use tokio::fs;
|
||||
use toml_edit::{Array, DocumentMut, InlineTable, Item, Value};
|
||||
use yazi_shared::Xdg;
|
||||
|
||||
use super::Package;
|
||||
use super::{config::{GitDependency, PackageConfig}, Package};
|
||||
|
||||
pub enum InstallFromConfig {
|
||||
Plugin,
|
||||
Flavor,
|
||||
}
|
||||
|
||||
impl Package {
|
||||
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 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?;
|
||||
|
||||
let mut table = InlineTable::new();
|
||||
table.insert("use", package.use_().as_ref().into());
|
||||
if !package.commit.is_empty() {
|
||||
table.insert("commit", package.commit.into());
|
||||
}
|
||||
|
||||
let dep =
|
||||
GitDependency { use_: package.use_().as_ref().into(), commit: package.commit.into() };
|
||||
if package.is_flavor {
|
||||
doc["flavor"]["deps"].as_array_mut().unwrap().push(table);
|
||||
config.flavor.deps.push(dep);
|
||||
} else {
|
||||
doc["plugin"]["deps"].as_array_mut().unwrap().push(table);
|
||||
config.plugin.deps.push(dep);
|
||||
}
|
||||
|
||||
fs::write(path, doc.to_string()).await?;
|
||||
Ok(())
|
||||
fs::write(path, toml::to_string_pretty(&config)?).await.context("Failed to write package.toml")
|
||||
}
|
||||
|
||||
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 Ok(s) = fs::read_to_string(&path).await else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut doc = s.parse::<DocumentMut>().context("Failed to parse package.toml")?;
|
||||
let Some(deps) = doc.get_mut(section).and_then(|d| d.get_mut("deps")) else {
|
||||
return Ok(());
|
||||
let mut config = Self::parse_config(&s)?;
|
||||
let deps = match section {
|
||||
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() {
|
||||
let dep = dep.as_inline_table_mut().context("Dependency must be an inline table")?;
|
||||
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);
|
||||
let mut package = Package::new(&dep.use_, dep.commit.as_deref());
|
||||
if upgrade {
|
||||
package.upgrade().await?;
|
||||
} else {
|
||||
|
|
@ -57,57 +55,47 @@ impl Package {
|
|||
}
|
||||
|
||||
if package.commit.is_empty() {
|
||||
dep.remove("commit");
|
||||
dep.commit.take();
|
||||
} 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> {
|
||||
let mut doc = s.parse::<DocumentMut>().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<()> {
|
||||
#[inline]
|
||||
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");
|
||||
}
|
||||
if doc["flavor"]["deps"].as_array().unwrap().into_iter().any(|v| same(v, name)) {
|
||||
bail!("Flavor `{name}` already exists in package.toml");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
fn parse_config(s: &str) -> Result<PackageConfig, anyhow::Error> {
|
||||
toml::from_str::<PackageConfig>(s).context("Failed to parse package.toml")
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_unique(doc: &PackageConfig, name: &str) -> Result<()> {
|
||||
if doc.plugin.deps.iter().any(|v| v.use_ == name) {
|
||||
bail!("Plugin `{name}` already exists in package.toml");
|
||||
}
|
||||
if doc.flavor.deps.iter().any(|v| v.use_ == name) {
|
||||
bail!("Flavor `{name}` already exists in package.toml");
|
||||
}
|
||||
|
||||
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