adding working directory option on prjfmt.toml for each formatter (#59)

* adding working directory option on prjfmt.toml for each formatter

* rename workdir into work_dir

* switch from xshell into process::Command

* fix unordered command_context by changing to BTreeMap
This commit is contained in:
Andika Demas Riyandi 2021-02-19 23:02:33 +07:00 committed by GitHub
parent f53c70d5ac
commit 3ad67a3851
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 58 additions and 36 deletions

View File

@ -1,4 +1,4 @@
{ {
"nixEnvSelector.nixShellConfig": "${workspaceRoot}/shell.nix", "files.trimFinalNewlines": true,
"files.trimFinalNewlines": true "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix"
} }

16
Cargo.lock generated
View File

@ -610,7 +610,6 @@ dependencies = [
"structopt", "structopt",
"toml", "toml",
"which", "which",
"xshell",
] ]
[[package]] [[package]]
@ -700,18 +699,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xshell"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed373ede30cea03e8c0af22f48ee1ba80efbf06fec8b4746977e6ee703878de0"
dependencies = [
"xshell-macros",
]
[[package]]
name = "xshell-macros"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6af9f8119104697b0105989a73c578ce33f922d9d6f3dae0e8ae3d538db321"

View File

@ -21,5 +21,4 @@ serde_json = "1.0"
sha-1 = "0.9.2" sha-1 = "0.9.2"
structopt = "0.3" structopt = "0.3"
toml = "0.5" toml = "0.5"
xshell = "0.1"
which = "4.0.2" which = "4.0.2"

View File

@ -12,10 +12,11 @@ A formatter MUST adhere to the following interface:
``` ```
Where Where
* `<command>` is the name of the formatter.
* `[options]` is any number of flags and options that the formatter wants to - `<command>` is the name of the formatter.
provide. - `[options]` is any number of flags and options that the formatter wants to
* `[...<files>]` is one or more files that the formatter should process. provide.
- `[...<files>]` is one or more files that the formatter should process.
Whenever the program is invoked with the list of files, it MUST only process all the files that are passed and format them. Files that are not passed should never be formatted. Whenever the program is invoked with the list of files, it MUST only process all the files that are passed and format them. Files that are not passed should never be formatted.

View File

@ -1,8 +1,6 @@
# A list of known formatters # A list of known formatters
| name | files as argument | write-in-place | update-mtime | | name | files as argument | write-in-place | update-mtime |
|-----------|-------------------|----------------|----------------------| | --------- | ----------------- | -------------- | -------------------- |
| gofmt | yes | yes | keeps the same mtime | | gofmt | yes | yes | keeps the same mtime |
| cargo fmt | no | yes | yes | | cargo fmt | no | yes | yes |

View File

@ -2,4 +2,4 @@
## 0.1.0.0 -- YYYY-mm-dd ## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world. - First version. Released on an unsuspecting world.

View File

@ -2,4 +2,4 @@
## 0.1.0.0 -- YYYY-mm-dd ## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world. - First version. Released on an unsuspecting world.

View File

@ -20,6 +20,8 @@ pub struct Root {
pub struct FmtConfig { pub struct FmtConfig {
/// Command formatter to run /// Command formatter to run
pub command: String, pub command: String,
/// Working directory for formatter
pub work_dir: Option<String>,
/// Argument for formatter /// Argument for formatter
#[serde(default)] #[serde(default)]
pub options: Vec<String>, pub options: Vec<String>,

View File

@ -9,8 +9,8 @@ use std::collections::BTreeSet;
use std::fs::metadata; use std::fs::metadata;
use std::iter::Iterator; use std::iter::Iterator;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command;
use which::which; use which::which;
use xshell::cmd;
/// Make sure that formatter binary exists. This also for other formatter /// Make sure that formatter binary exists. This also for other formatter
pub fn check_bin(command: &str) -> Result<()> { pub fn check_bin(command: &str) -> Result<()> {
@ -61,6 +61,10 @@ pub fn run_treefmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
for c in context { for c in context {
if !c.metadata.is_empty() { if !c.metadata.is_empty() {
println!("Command: {}", c.command); println!("Command: {}", c.command);
println!(
"Working Directory: {}",
c.work_dir.clone().unwrap_or("".to_string())
);
println!("Files:"); println!("Files:");
for m in &c.metadata { for m in &c.metadata {
let path = &m.path; let path = &m.path;
@ -71,17 +75,29 @@ pub fn run_treefmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
} }
// TODO: report errors (both Err(_), and Ok(bad status)) // TODO: report errors (both Err(_), and Ok(bad status))
let _outputs: Vec<xshell::Result<std::process::Output>> = context let _outputs: Vec<std::io::Result<std::process::Output>> = context
.par_iter() .par_iter()
.map(|c| { .map(|c| {
let arg = &c.options; let arg = &c.options;
let cmd_arg = &c.command; let mut cmd_arg = Command::new(&c.command);
let work_dir = match c.work_dir.clone() {
Some(x) => x,
None => String::new(),
};
let paths = c.metadata.iter().map(|f| &f.path); let paths = c.metadata.iter().map(|f| &f.path);
cmd!("{cmd_arg} {arg...} {paths...}").output() if !work_dir.is_empty() {
let _x = std::env::set_current_dir(work_dir);
cmd_arg.args(arg).args(paths).output()
} else {
let _x = std::env::set_current_dir(cwd.clone());
cmd_arg.args(arg).args(paths).output()
}
}) })
.collect(); .collect();
if mfst.manifest.is_empty() || ctxs.is_empty() { if mfst.manifest.is_empty() && ctxs.is_empty() {
CLOG.info("First time running treefmt");
CLOG.info("capturing formatted file's state...");
create_manifest(treefmt_toml, cache_dir, old_ctx)?; create_manifest(treefmt_toml, cache_dir, old_ctx)?;
} else { } else {
// Read the current status of files and insert into the manifest. // Read the current status of files and insert into the manifest.
@ -161,6 +177,7 @@ pub fn create_command_context(
Ok(CmdContext { Ok(CmdContext {
command: config.command.clone(), command: config.command.clone(),
options: config.options.clone(), options: config.options.clone(),
work_dir: config.work_dir.clone(),
metadata: path_to_filemeta(list_files)?, metadata: path_to_filemeta(list_files)?,
}) })
}) })

View File

@ -30,6 +30,7 @@ pub fn create_manifest(
let treefmt = cmd.command; let treefmt = cmd.command;
let manifest = CmdContext { let manifest = CmdContext {
command: treefmt.to_string(), command: treefmt.to_string(),
work_dir: cmd.work_dir,
options: cmd.options, options: cmd.options,
metadata: cmd.metadata, metadata: cmd.metadata,
}; };
@ -102,15 +103,32 @@ pub fn check_treefmt(
cache: &RootManifest, cache: &RootManifest,
) -> Result<Vec<CmdContext>> { ) -> Result<Vec<CmdContext>> {
let cache_context = cache.manifest.values(); let cache_context = cache.manifest.values();
let results = cmd_context.iter().zip(cache_context); let map_ctx: BTreeMap<String, CmdContext> = cmd_context
.into_iter()
.map(|cmd| {
let treefmt = cmd.command.clone();
let ctx = CmdContext {
command: treefmt.to_string(),
work_dir: cmd.work_dir.clone(),
options: cmd.options.clone(),
metadata: cmd.metadata.clone(),
};
(treefmt, ctx)
})
.collect();
let new_cmd_ctx = map_ctx.values();
let results = new_cmd_ctx.clone().into_iter().zip(cache_context);
let cache_context: Vec<CmdContext> = results let cache_context: Vec<CmdContext> = results
.clone() .clone()
.map(|(new, old)| { .map(|(new, old)| {
Ok(CmdContext { Ok(CmdContext {
command: new.command.clone(), command: new.command.clone(),
work_dir: new.work_dir.clone(),
options: new.options.clone(), options: new.options.clone(),
metadata: if new.command != old.command || new.options != old.options { metadata: if new.command != old.command
|| new.options != old.options
|| new.work_dir != old.work_dir
{
// If either the command or the options have changed, invalidate old entries // If either the command or the options have changed, invalidate old entries
new.metadata.clone() new.metadata.clone()
} else { } else {

View File

@ -21,6 +21,8 @@ pub static CLOG: CustomLogOutput = CustomLogOutput::new();
pub struct CmdContext { pub struct CmdContext {
/// formatter command to run /// formatter command to run
pub command: String, pub command: String,
/// formatter work_dir
pub work_dir: Option<String>,
/// formatter arguments or flags /// formatter arguments or flags
pub options: Vec<String>, pub options: Vec<String>,
/// formatter target path /// formatter target path
@ -30,6 +32,7 @@ pub struct CmdContext {
impl PartialEq for CmdContext { impl PartialEq for CmdContext {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.command == other.command self.command == other.command
&& self.work_dir == other.work_dir
&& self.options == other.options && self.options == other.options
&& self.metadata == other.metadata && self.metadata == other.metadata
} }