mirror of
https://github.com/numtide/treefmt.git
synced 2024-09-11 17:07:44 +03:00
Engine refactor (#48)
* refactor: move src/formatters/tool.rs to src/engine.rs Start to put the utilities in the right place. More follow-up refactoring is required. * clean run_cli * engine annotations * docs: started a list of formatters * refactor types, function, and argument naming Co-authored-by: zimbatm <zimbatm@zimbatm.com>
This commit is contained in:
parent
22ae114512
commit
c8e525f78c
8
docs/formatters.md
Normal file
8
docs/formatters.md
Normal file
@ -0,0 +1,8 @@
|
||||
# A list of known formatters
|
||||
|
||||
|
||||
|
||||
| name | files as argument | write-in-place | update-mtime |
|
||||
|-----------|-------------------|----------------|----------------------|
|
||||
| gofmt | yes | yes | keeps the same mtime |
|
||||
| cargo fmt | no | yes | yes |
|
@ -1,5 +1,5 @@
|
||||
use crate::command::Cli;
|
||||
use crate::formatters::tool::run_prjfmt;
|
||||
use super::Cli;
|
||||
use crate::engine::run_prjfmt;
|
||||
use crate::CLOG;
|
||||
use anyhow::anyhow;
|
||||
use std::env;
|
||||
|
@ -6,7 +6,7 @@ mod init;
|
||||
|
||||
use self::format::format_cmd;
|
||||
use self::init::init_cmd;
|
||||
use crate::customlog::LogLevel;
|
||||
use super::customlog::LogLevel;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
@ -46,13 +46,9 @@ pub struct Cli {
|
||||
|
||||
/// Run a command with the given logger
|
||||
pub fn run_cli(cli: Cli) -> anyhow::Result<()> {
|
||||
if let Some(command) = cli.cmd {
|
||||
match command {
|
||||
Command::Init { path } => init_cmd(path)?,
|
||||
}
|
||||
} else {
|
||||
format_cmd(cli)?;
|
||||
return Ok(());
|
||||
match cli.cmd {
|
||||
Some(Command::Init { path }) => init_cmd(path)?,
|
||||
None => format_cmd(cli)?,
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
|
@ -1,10 +1,17 @@
|
||||
//! Fancy custom log functionality.
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use crate::emoji;
|
||||
use anyhow;
|
||||
use console::style;
|
||||
use console::Emoji;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
|
||||
pub static FOLDER: Emoji = Emoji("📂", "");
|
||||
pub static WARN: Emoji = Emoji("⚠️", ":-)");
|
||||
pub static ERROR: Emoji = Emoji("⛔", "");
|
||||
pub static INFO: Emoji = Emoji("ℹ️", "");
|
||||
pub static DEBUG: Emoji = Emoji("🐛", "");
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// The log level for prjfmt
|
||||
@ -77,12 +84,7 @@ impl CustomLogOutput {
|
||||
/// Add debug message.
|
||||
pub fn debug(&self, message: &str) {
|
||||
if !self.quiet() && self.is_log_enabled(LogLevel::Debug) {
|
||||
let debug = format!(
|
||||
"{} {}: {}",
|
||||
emoji::DEBUG,
|
||||
style("[DEBUG]").bold().dim(),
|
||||
message,
|
||||
);
|
||||
let debug = format!("{} {}: {}", DEBUG, style("[DEBUG]").bold().dim(), message,);
|
||||
self.message(&debug);
|
||||
}
|
||||
}
|
||||
@ -90,12 +92,7 @@ impl CustomLogOutput {
|
||||
/// Add an informational message.
|
||||
pub fn info(&self, message: &str) {
|
||||
if !self.quiet() && self.is_log_enabled(LogLevel::Info) {
|
||||
let info = format!(
|
||||
"{} {}: {}",
|
||||
emoji::INFO,
|
||||
style("[INFO]").bold().dim(),
|
||||
message,
|
||||
);
|
||||
let info = format!("{} {}: {}", INFO, style("[INFO]").bold().dim(), message,);
|
||||
self.message(&info);
|
||||
}
|
||||
}
|
||||
@ -103,12 +100,7 @@ impl CustomLogOutput {
|
||||
/// Add a warning message.
|
||||
pub fn warn(&self, message: &str) {
|
||||
if !self.quiet() && self.is_log_enabled(LogLevel::Warn) {
|
||||
let warn = format!(
|
||||
"{} {}: {}",
|
||||
emoji::WARN,
|
||||
style("[WARN]").bold().dim(),
|
||||
message
|
||||
);
|
||||
let warn = format!("{} {}: {}", WARN, style("[WARN]").bold().dim(), message);
|
||||
self.message(&warn);
|
||||
}
|
||||
}
|
||||
@ -116,12 +108,7 @@ impl CustomLogOutput {
|
||||
/// Add an error message.
|
||||
pub fn error(&self, message: &str) {
|
||||
if self.is_log_enabled(LogLevel::Error) {
|
||||
let err = format!(
|
||||
"{} {}: {}",
|
||||
emoji::ERROR,
|
||||
style("[ERR]").bold().dim(),
|
||||
message
|
||||
);
|
||||
let err = format!("{} {}: {}", ERROR, style("[ERR]").bold().dim(), message);
|
||||
self.message(&err);
|
||||
}
|
||||
}
|
||||
|
11
src/emoji.rs
11
src/emoji.rs
@ -1,11 +0,0 @@
|
||||
//! Emoji contants used by `prjfmt`.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use console::Emoji;
|
||||
|
||||
pub static FOLDER: Emoji = Emoji("📂", "");
|
||||
pub static WARN: Emoji = Emoji("⚠️", ":-)");
|
||||
pub static ERROR: Emoji = Emoji("⛔", "");
|
||||
pub static INFO: Emoji = Emoji("ℹ️", "");
|
||||
pub static DEBUG: Emoji = Emoji("🐛", "");
|
@ -1,18 +1,20 @@
|
||||
use super::manifest::create_prjfmt_manifest;
|
||||
use crate::formatters::check::check_prjfmt;
|
||||
use crate::formatters::manifest::{read_prjfmt_manifest, RootManifest};
|
||||
use crate::{emoji, CLOG};
|
||||
//! The main formatting engine logic should be in this module.
|
||||
|
||||
use crate::formatters::{
|
||||
check::check_prjfmt,
|
||||
manifest::{create_manifest, read_manifest},
|
||||
RootManifest,
|
||||
};
|
||||
use crate::{customlog, CmdContext, FileExtensions, FileMeta, Root, CLOG};
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use filetime::FileTime;
|
||||
use rayon::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::BTreeSet;
|
||||
use std::fs::{metadata, read_to_string};
|
||||
use std::iter::{IntoIterator, Iterator};
|
||||
use std::path::PathBuf;
|
||||
use xshell::cmd;
|
||||
use which::which;
|
||||
use xshell::cmd;
|
||||
|
||||
/// Make sure that formatter binary exists. This also for other formatter
|
||||
pub fn check_bin(command: &str) -> Result<()> {
|
||||
@ -29,12 +31,25 @@ pub fn check_bin(command: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
/// Run the prjfmt
|
||||
//
|
||||
// 1. Find and load prjfmt.toml
|
||||
// 1b. Resolve all of the formatters paths. If missing, print an error, remove the formatters from the list and continue.
|
||||
// 2. Load the manifest if it exists, otherwise start with empty manifest
|
||||
// 3. Get the list of files, use the ones passed as argument if not empty, other default to all.
|
||||
// Errorr if a file belongs to two formatters
|
||||
// => HashMap<formatter>(files, mtimes) // map A
|
||||
// 4. Compare the list of files with the manifest, keep the ones that are not in the manifest. // map B
|
||||
// 5. Iterate over each formatter (in parallel)
|
||||
// a. Run the formatter with the list of files
|
||||
// b. Collect the new list of (files, mtimes) and return that // map C
|
||||
// 6. Merge map C into map B. Write this as the new manifest.
|
||||
|
||||
pub fn run_prjfmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
|
||||
let prjfmt_toml = cwd.as_path().join("prjfmt.toml");
|
||||
|
||||
// Once the prjfmt found the $XDG_CACHE_DIR/prjfmt/eval-cache/ folder,
|
||||
// it will try to scan the manifest and passed it into check_prjfmt function
|
||||
let manifest: RootManifest = read_prjfmt_manifest(&prjfmt_toml, &cache_dir)?;
|
||||
let manifest: RootManifest = read_manifest(&prjfmt_toml, &cache_dir)?;
|
||||
let old_ctx = create_command_context(&prjfmt_toml)?;
|
||||
let ctxs = check_prjfmt(&prjfmt_toml, &old_ctx, &manifest)?;
|
||||
|
||||
@ -47,7 +62,7 @@ pub fn run_prjfmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
|
||||
if !prjfmt_toml.as_path().exists() {
|
||||
return Err(anyhow!(
|
||||
"{}prjfmt.toml not found, please run --init command",
|
||||
emoji::ERROR
|
||||
customlog::ERROR
|
||||
));
|
||||
}
|
||||
|
||||
@ -76,6 +91,8 @@ pub fn run_prjfmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
|
||||
let cmd_arg = &c.command;
|
||||
let paths = c.metadata.iter().map(|f| &f.path);
|
||||
cmd!("{cmd_arg} {arg...} {paths...}").output()
|
||||
// TODO: go over all the paths, and collect the ones that have a new mtime.
|
||||
// => list (file, mtime)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -97,11 +114,11 @@ pub fn run_prjfmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
|
||||
.collect();
|
||||
|
||||
if manifest.manifest.is_empty() || ctxs.is_empty() {
|
||||
create_prjfmt_manifest(prjfmt_toml, cache_dir, old_ctx)?;
|
||||
create_manifest(prjfmt_toml, cache_dir, old_ctx)?;
|
||||
} else {
|
||||
println!("Format successful");
|
||||
println!("capturing formatted file's state...");
|
||||
create_prjfmt_manifest(prjfmt_toml, cache_dir, new_ctx)?;
|
||||
create_manifest(prjfmt_toml, cache_dir, new_ctx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -195,7 +212,7 @@ pub fn create_command_context(prjfmt_toml: &PathBuf) -> Result<Vec<CmdContext>>
|
||||
None => {
|
||||
return Err(anyhow!(
|
||||
"{}prjfmt.toml not found, please run --init command",
|
||||
emoji::ERROR
|
||||
customlog::ERROR
|
||||
))
|
||||
}
|
||||
};
|
||||
@ -221,107 +238,6 @@ pub fn create_command_context(prjfmt_toml: &PathBuf) -> Result<Vec<CmdContext>>
|
||||
Ok(cmd_context)
|
||||
}
|
||||
|
||||
/// prjfmt.toml structure
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Root {
|
||||
/// Map of formatters into the config
|
||||
pub formatters: BTreeMap<String, FmtConfig>,
|
||||
}
|
||||
|
||||
/// Config for each formatters
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct FmtConfig {
|
||||
/// File extensions that want to be formatted
|
||||
pub files: FileExtensions,
|
||||
/// File or Folder that is included to be formatted
|
||||
pub includes: Option<Vec<String>>,
|
||||
/// File or Folder that is excluded to be formatted
|
||||
pub excludes: Option<Vec<String>>,
|
||||
/// Command formatter to run
|
||||
pub command: Option<String>,
|
||||
/// Argument for formatter
|
||||
pub options: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// File extensions can be single string (e.g. "*.hs") or
|
||||
/// list of string (e.g. [ "*.hs", "*.rs" ])
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum FileExtensions {
|
||||
/// Single file type
|
||||
SingleFile(String),
|
||||
/// List of file type
|
||||
MultipleFile(Vec<String>),
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a FileExtensions {
|
||||
type Item = &'a String;
|
||||
type IntoIter = either::Either<std::iter::Once<&'a String>, std::slice::Iter<'a, String>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
FileExtensions::SingleFile(glob) => either::Either::Left(std::iter::once(glob)),
|
||||
FileExtensions::MultipleFile(globs) => either::Either::Right(globs.iter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
/// Each context of the formatter config
|
||||
pub struct CmdContext {
|
||||
/// formatter command to run
|
||||
pub command: String,
|
||||
/// formatter arguments or flags
|
||||
pub options: Vec<String>,
|
||||
/// formatter target path
|
||||
pub metadata: BTreeSet<FileMeta>,
|
||||
}
|
||||
|
||||
impl PartialEq for CmdContext {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.command == other.command
|
||||
&& self.options == other.options
|
||||
&& self.metadata == other.metadata
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for CmdContext {}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
/// File metadata created after the first prjfmt run
|
||||
pub struct FileMeta {
|
||||
/// Last modification time listed in the file's metadata
|
||||
pub mtimes: i64,
|
||||
/// Path to the formatted file
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl Ord for FileMeta {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.eq(other) {
|
||||
return Ordering::Equal;
|
||||
}
|
||||
if self.mtimes.eq(&other.mtimes) {
|
||||
return self.path.cmp(&other.path);
|
||||
}
|
||||
self.mtimes.cmp(&other.mtimes)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for FileMeta {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FileMeta {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.mtimes == other.mtimes && self.path == other.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FileMeta {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
@ -1,6 +1,5 @@
|
||||
use crate::formatters::manifest::RootManifest;
|
||||
use crate::formatters::tool::CmdContext;
|
||||
use crate::CLOG;
|
||||
use super::RootManifest;
|
||||
use crate::{CmdContext, CLOG};
|
||||
use anyhow::{Error, Result};
|
||||
use std::path::PathBuf;
|
||||
use std::vec::Vec;
|
||||
|
@ -1,23 +1,21 @@
|
||||
use crate::{emoji, CLOG};
|
||||
use super::RootManifest;
|
||||
use crate::{customlog, CmdContext, CLOG};
|
||||
|
||||
use super::tool::CmdContext;
|
||||
use anyhow::{anyhow, Result};
|
||||
use hex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha1::{Digest, Sha1};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::{read_to_string, File};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::str;
|
||||
|
||||
/// Create <hex(hash(path-to-prjfmt))>.toml and put it in $XDG_CACHE_DIR/prjfmt/eval-cache/
|
||||
pub fn create_prjfmt_manifest(
|
||||
pub fn create_manifest(
|
||||
prjfmt_toml: PathBuf,
|
||||
cache_dir: PathBuf,
|
||||
cmd_ctx: Vec<CmdContext>,
|
||||
) -> Result<()> {
|
||||
let hash_toml = create_prjfmt_hash(&prjfmt_toml)?;
|
||||
let hash_toml = create_hash(&prjfmt_toml)?;
|
||||
|
||||
let mut f = File::create(cache_dir.as_path().join(hash_toml))?;
|
||||
let map_manifest: BTreeMap<String, CmdContext> = cmd_ctx
|
||||
@ -38,8 +36,8 @@ pub fn create_prjfmt_manifest(
|
||||
f.write_all(
|
||||
format!(
|
||||
"# {} DO NOT HAND EDIT THIS FILE {}\n\n{}",
|
||||
emoji::WARN,
|
||||
emoji::WARN,
|
||||
customlog::WARN,
|
||||
customlog::WARN,
|
||||
toml::to_string_pretty(&manifest_toml)?
|
||||
)
|
||||
.as_bytes(),
|
||||
@ -48,12 +46,12 @@ pub fn create_prjfmt_manifest(
|
||||
}
|
||||
|
||||
/// Read the <hex(hash(path-to-prjfmt))>.toml and return list of config's cache evaluation
|
||||
pub fn read_prjfmt_manifest(prjfmt_toml: &PathBuf, path: &PathBuf) -> Result<RootManifest> {
|
||||
let hash_toml = create_prjfmt_hash(&prjfmt_toml)?;
|
||||
let manifest_toml = path.as_path().join(&hash_toml);
|
||||
pub fn read_manifest(prjfmt_toml: &PathBuf, cache_dir: &PathBuf) -> Result<RootManifest> {
|
||||
let hash_toml = create_hash(&prjfmt_toml)?;
|
||||
let manifest_toml = cache_dir.as_path().join(&hash_toml);
|
||||
|
||||
if manifest_toml.as_path().exists() {
|
||||
CLOG.debug(&format!("Found {} in: {}", hash_toml, path.display()));
|
||||
CLOG.debug(&format!("Found {} in: {}", hash_toml, cache_dir.display()));
|
||||
let open_file = match read_to_string(manifest_toml.as_path()) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
@ -75,10 +73,15 @@ pub fn read_prjfmt_manifest(prjfmt_toml: &PathBuf, path: &PathBuf) -> Result<Roo
|
||||
}
|
||||
}
|
||||
|
||||
fn create_prjfmt_hash(prjfmt_toml: &PathBuf) -> Result<String> {
|
||||
fn create_hash(prjfmt_toml: &PathBuf) -> Result<String> {
|
||||
let prjfmt_str = match prjfmt_toml.to_str() {
|
||||
Some(str) => str.as_bytes(),
|
||||
None => return Err(anyhow!("{}cannot convert to string slice", emoji::ERROR)),
|
||||
None => {
|
||||
return Err(anyhow!(
|
||||
"{}cannot convert to string slice",
|
||||
customlog::ERROR
|
||||
))
|
||||
}
|
||||
};
|
||||
let prjfmt_hash = Sha1::digest(prjfmt_str);
|
||||
let result = hex::encode(prjfmt_hash);
|
||||
@ -86,23 +89,16 @@ fn create_prjfmt_hash(prjfmt_toml: &PathBuf) -> Result<String> {
|
||||
Ok(manifest_toml)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
/// RootManifest
|
||||
pub struct RootManifest {
|
||||
/// Map of manifests config based on its formatter
|
||||
pub manifest: BTreeMap<String, CmdContext>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Every same path produce same hash
|
||||
#[test]
|
||||
fn test_create_prjfmt_hash() -> Result<()> {
|
||||
fn test_create_hash() -> Result<()> {
|
||||
let file_path = PathBuf::from(r"examples/monorepo/prjfmt.toml");
|
||||
let prjfmt_hash = "02e97bc0a67b5d61f3152c184690216085ef0c03.toml";
|
||||
assert_eq!(create_prjfmt_hash(&file_path)?, prjfmt_hash);
|
||||
assert_eq!(create_hash(&file_path)?, prjfmt_hash);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -5,5 +5,14 @@
|
||||
pub mod check;
|
||||
/// Manifest configuration
|
||||
pub mod manifest;
|
||||
/// Formatter utility
|
||||
pub mod tool;
|
||||
|
||||
use crate::CmdContext;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
/// RootManifest
|
||||
pub struct RootManifest {
|
||||
/// Map of manifests config based on its formatter
|
||||
pub manifest: BTreeMap<String, CmdContext>,
|
||||
}
|
||||
|
108
src/lib.rs
108
src/lib.rs
@ -1,13 +1,117 @@
|
||||
//! Your favorite all-in-one formatter tool!
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
pub mod command;
|
||||
pub mod customlog;
|
||||
pub mod emoji;
|
||||
pub mod engine;
|
||||
pub mod formatters;
|
||||
|
||||
use customlog::CustomLogOutput;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// The global custom log and user-facing message output.
|
||||
pub static CLOG: CustomLogOutput = CustomLogOutput::new();
|
||||
|
||||
/// prjfmt.toml structure
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Root {
|
||||
/// Map of formatters into the config
|
||||
pub formatters: BTreeMap<String, FmtConfig>,
|
||||
}
|
||||
|
||||
/// Config for each formatters
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct FmtConfig {
|
||||
/// File extensions that want to be formatted
|
||||
pub files: FileExtensions,
|
||||
/// File or Folder that is included to be formatted
|
||||
pub includes: Option<Vec<String>>,
|
||||
/// File or Folder that is excluded to be formatted
|
||||
pub excludes: Option<Vec<String>>,
|
||||
/// Command formatter to run
|
||||
pub command: Option<String>,
|
||||
/// Argument for formatter
|
||||
pub options: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// File extensions can be single string (e.g. "*.hs") or
|
||||
/// list of string (e.g. [ "*.hs", "*.rs" ])
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum FileExtensions {
|
||||
/// Single file type
|
||||
SingleFile(String),
|
||||
/// List of file type
|
||||
MultipleFile(Vec<String>),
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a FileExtensions {
|
||||
type Item = &'a String;
|
||||
type IntoIter = either::Either<std::iter::Once<&'a String>, std::slice::Iter<'a, String>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
FileExtensions::SingleFile(glob) => either::Either::Left(std::iter::once(glob)),
|
||||
FileExtensions::MultipleFile(globs) => either::Either::Right(globs.iter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
/// Each context of the formatter config
|
||||
pub struct CmdContext {
|
||||
/// formatter command to run
|
||||
pub command: String,
|
||||
/// formatter arguments or flags
|
||||
pub options: Vec<String>,
|
||||
/// formatter target path
|
||||
pub metadata: BTreeSet<FileMeta>,
|
||||
}
|
||||
|
||||
impl PartialEq for CmdContext {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.command == other.command
|
||||
&& self.options == other.options
|
||||
&& self.metadata == other.metadata
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for CmdContext {}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
/// File metadata created after the first prjfmt run
|
||||
pub struct FileMeta {
|
||||
/// Last modification time listed in the file's metadata
|
||||
pub mtimes: i64,
|
||||
/// Path to the formatted file
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl Ord for FileMeta {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.eq(other) {
|
||||
return Ordering::Equal;
|
||||
}
|
||||
if self.mtimes.eq(&other.mtimes) {
|
||||
return self.path.cmp(&other.path);
|
||||
}
|
||||
self.mtimes.cmp(&other.mtimes)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for FileMeta {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FileMeta {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.mtimes == other.mtimes && self.path == other.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FileMeta {}
|
||||
|
Loading…
Reference in New Issue
Block a user