Merge pull request #65 from numtide/config-simplifications

more config simplification
This commit is contained in:
Jonas Chevalier 2021-02-22 16:11:43 +00:00 committed by GitHub
commit 7a95af7f95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 230 additions and 165 deletions

19
Cargo.lock generated
View File

@ -464,6 +464,24 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "path-absolutize"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6ab2aaa5faefed84db46e4398eab15fa51325606462b5da8b0e230af3ac59a"
dependencies = [
"path-dedot",
]
[[package]]
name = "path-dedot"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658c6e985fce9c25289fe7c86c08a3cbe82c19a3cd5b3bc5945c8c632552e460"
dependencies = [
"once_cell",
]
[[package]]
name = "plotters"
version = "0.3.0"
@ -815,6 +833,7 @@ dependencies = [
"hex",
"ignore",
"log",
"path-absolutize",
"rayon",
"serde",
"serde_json",

View File

@ -23,6 +23,12 @@ structopt = "0.3"
toml = "0.5"
which = "4.0.2"
[dependencies.path-absolutize]
version = "3.0.6"
# Do not use `std::env::set_current_dir`.
# See https://github.com/magiclen/path-absolutize#once_cell_cache
features = ["once_cell_cache"]
[dev-dependencies]
criterion = "0.3"

View File

@ -55,9 +55,10 @@ FLAGS:
-v, --verbose Log verbosity is based off the number of v used
OPTIONS:
-C, --config <config> Specify where to look for the treefmt.toml file
--log-level <log-level> The maximum level of messages that should be logged by treefmt. [possible values:
info, warn, error] [default: debug]
-C <work-dir> Run as if treefmt was started in <work-dir> instead of the current working directory
[default: .]
SUBCOMMANDS:
--init Init a new project with a default config

View File

@ -6,19 +6,28 @@ use std::fs;
use std::path::Path;
use std::{env, path::PathBuf};
pub fn format_cmd(path: Option<PathBuf>) -> anyhow::Result<()> {
let cfg_dir = match path {
pub fn format_cmd(work_dir: PathBuf) -> anyhow::Result<()> {
// Search for the treefmt.toml from there.
let treefmt_toml = match config::lookup(&work_dir) {
Some(p) => p,
None => {
let cwd = env::current_dir()?;
match config::lookup_dir(&cwd) {
Some(p) => p,
None => return Err(anyhow!("treefmt.toml could not be found in {} and up. Use the --init option to create one.", cwd.display()))
}
return Err(anyhow!(
"{} could not be found in {} and up. Use the --init option to create one.",
config::FILENAME,
work_dir.display()
))
}
};
// We assume that the parent always exists since the file is contained in a folder.
let treefmt_root = treefmt_toml.parent().unwrap();
let treefmt_toml = cfg_dir.join("treefmt.toml");
CLOG.debug(&format!(
"Found {} at {}",
treefmt_toml.display(),
treefmt_root.display()
));
// Find the location of the cache directory to store the eval-cache manifests.
let xdg_cache_dir = match env::var("XDG_CACHE_DIR") {
Ok(path) => path,
Err(err) => {
@ -36,24 +45,12 @@ pub fn format_cmd(path: Option<PathBuf>) -> anyhow::Result<()> {
}
}
};
let cache_dir = Path::new(&xdg_cache_dir).join("treefmt/eval-cache");
// Make sure the cache directory exists.
fs::create_dir_all(&cache_dir)?;
// Finally run the main formatter logic from the engine.
run_treefmt(work_dir, cache_dir, treefmt_toml)?;
if treefmt_toml.exists() {
CLOG.debug(&format!(
"Found {} at {}",
treefmt_toml.display(),
cfg_dir.display()
));
CLOG.debug(&format!(
"Change current directory into: {}",
cfg_dir.display()
));
let cache_dir = Path::new(&xdg_cache_dir).join("treefmt/eval-cache");
fs::create_dir_all(&cache_dir)?;
run_treefmt(cfg_dir, cache_dir)?;
} else {
CLOG.error(
"file treefmt.toml couldn't be found. Run `--init` to generate the default setting",
);
}
Ok(())
}

View File

@ -1,15 +1,12 @@
use crate::config;
use crate::CLOG;
use anyhow::Context;
use console::style;
use std::fs;
use std::path::PathBuf;
pub fn init_cmd(path: Option<PathBuf>) -> anyhow::Result<()> {
let file = match path {
Some(loc) => loc,
None => PathBuf::from("."),
};
let file_path = file.join("treefmt.toml");
pub fn init_cmd(work_dir: PathBuf) -> anyhow::Result<()> {
let file_path = work_dir.join(config::FILENAME);
// TODO: detect if file exists
fs::write(
&file_path,

View File

@ -7,6 +7,7 @@ mod init;
use self::format::format_cmd;
use self::init::init_cmd;
use super::customlog::LogLevel;
use path_absolutize::*;
use std::path::PathBuf;
use structopt::StructOpt;
@ -37,16 +38,26 @@ pub struct Cli {
/// The maximum level of messages that should be logged by treefmt. [possible values: info, warn, error]
pub log_level: LogLevel,
#[structopt(long = "config", short = "C")]
/// Specify where to look for the treefmt.toml file
pub config: Option<PathBuf>,
#[structopt(short = "C", default_value = ".")]
/// Run as if treefmt was started in <work-dir> instead of the current working directory.
pub work_dir: PathBuf,
}
/// Use this instead of Cli::from_args(). We do a little bit of post-processing here.
pub fn cli_from_args() -> anyhow::Result<Cli> {
let mut cli = Cli::from_args();
// Make sure the work_dir is an absolute path. Don't use the stdlib canonicalize() function
// because symlinks should not be resolved.
let abs_work_dir = cli.work_dir.absolutize()?;
cli.work_dir = abs_work_dir.to_path_buf();
Ok(cli)
}
/// Run a command with the given logger
pub fn run_cli(cli: Cli) -> anyhow::Result<()> {
match cli.cmd {
Some(Command::Init {}) => init_cmd(cli.config)?,
None => format_cmd(cli.config)?,
Some(Command::Init {}) => init_cmd(cli.work_dir)?,
None => format_cmd(cli.work_dir)?,
}
Ok(())

View File

@ -1,9 +1,12 @@
//! Contains the project configuration schema and parsing
use crate::CLOG;
use anyhow::Result;
use path_absolutize::*;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::fs::read_to_string;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use which::which;
/// Name of the config file
pub const FILENAME: &str = "treefmt.toml";
@ -19,9 +22,10 @@ pub struct Root {
#[derive(Debug, Deserialize)]
pub struct FmtConfig {
/// Command formatter to run
pub command: String,
pub command: PathBuf,
/// Working directory for formatter
pub work_dir: Option<String>,
#[serde(default = "cwd")]
pub work_dir: PathBuf,
/// Argument for formatter
#[serde(default)]
pub options: Vec<String>,
@ -33,12 +37,18 @@ pub struct FmtConfig {
pub excludes: Vec<String>,
}
/// Find the directory that contains the treefmt.toml file. From the current folder, and up.
pub fn lookup_dir(dir: &PathBuf) -> Option<PathBuf> {
// The default work_dir value. It's a bit clunky. See https://github.com/serde-rs/serde/issues/1814
fn cwd() -> PathBuf {
".".into()
}
/// Returns an absolute path to the treefmt.toml file. From the current folder, and up.
pub fn lookup(dir: &PathBuf) -> Option<PathBuf> {
let mut cwd = dir.clone();
loop {
if cwd.join(FILENAME).exists() {
return Some(cwd);
let config_file = cwd.join(FILENAME);
if config_file.exists() {
return Some(config_file);
}
cwd = match cwd.parent() {
Some(x) => x.to_path_buf(),
@ -50,8 +60,49 @@ pub fn lookup_dir(dir: &PathBuf) -> Option<PathBuf> {
}
/// Loads the treefmt.toml config from the given file path.
pub fn from_path(path: &PathBuf) -> Result<Root> {
let content = read_to_string(path)?;
let ret: Root = toml::from_str(&content)?;
pub fn from_path(file_path: &PathBuf) -> Result<Root> {
// unwrap: assume the file is in a folder
let file_dir = file_path.parent().unwrap();
// Load the file
let content = read_to_string(file_path)?;
// Parse the config
let mut ret: Root = toml::from_str(&content)?;
// Expand a bunch of formatter configs. If any of these fail, don't make it a fatal issue. Display the error and continue.
let new_formatter = ret
.formatter
.iter()
.fold(BTreeMap::new(), |mut sum, (name, fmt)| {
match load_formatter(fmt, file_dir) {
// Re-add the resolved formatter if it was successful
Ok(fmt2) => {
sum.insert(name.clone(), fmt2);
}
Err(err) => CLOG.warn(&format!("Ignoring {} because of error: {}", name, err)),
};
sum
});
// Replace the formatters with the loaded ones
ret.formatter = new_formatter;
Ok(ret)
}
fn load_formatter(fmt: &FmtConfig, config_dir: &Path) -> Result<FmtConfig> {
// Expand the work_dir to an absolute path, using the config directory as a reference.
let abs_work_dir = fmt.work_dir.absolutize_virtually(config_dir)?;
// Resolve the path to the binary
let abs_command = which(&fmt.command)?;
assert!(abs_command.is_absolute());
CLOG.debug(&format!(
"Found {} at {}",
fmt.command.display(),
abs_command.display()
));
Ok(FmtConfig {
command: abs_command,
work_dir: abs_work_dir.to_path_buf(),
options: fmt.options.clone(),
includes: fmt.includes.clone(),
excludes: fmt.excludes.clone(),
})
}

View File

@ -1,28 +1,32 @@
//! The main formatting engine logic should be in this module.
use crate::eval_cache::{check_treefmt, create_manifest, read_manifest, RootManifest};
use crate::{config, customlog, CmdContext, CmdMeta, FileMeta, CLOG};
use anyhow::{anyhow, Error, Result};
use filetime::FileTime;
use crate::eval_cache::{check_treefmt, create_manifest, get_mtime, read_manifest, RootManifest};
use crate::{config, CmdContext, CmdMeta, FileMeta, CLOG};
use anyhow::{Error, Result};
use rayon::prelude::*;
use std::collections::BTreeSet;
use std::fs::metadata;
use std::iter::Iterator;
use std::path::PathBuf;
use std::process::Command;
/// Run the treefmt
pub fn run_treefmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
let treefmt_toml = cwd.join(config::FILENAME);
pub fn run_treefmt(cwd: PathBuf, cache_dir: PathBuf, treefmt_toml: PathBuf) -> anyhow::Result<()> {
let project_config = config::from_path(&treefmt_toml)?;
// Load the manifest if it exists, otherwise print the error and use an empty manifest
let mfst: RootManifest = match read_manifest(&treefmt_toml, &cache_dir) {
Ok(mfst) => mfst,
Err(err) => {
CLOG.warn(&format!("Using empty manifest due to error: {}", err));
RootManifest::default()
}
};
// Once the treefmt found the $XDG_CACHE_DIR/treefmt/eval-cache/ folder,
// it will try to scan the manifest and passed it into check_treefmt function
let old_ctx = create_command_context(&cwd, &project_config)?;
// TODO: Resolve all of the formatters paths. If missing, print an error, remove the formatters from the list and continue.
// Load the manifest if it exists, otherwise start with empty manifest
let mfst: RootManifest = read_manifest(&treefmt_toml, &cache_dir)?;
// Compare the list of files with the manifest, keep the ones that are not in the manifest
let ctxs = check_treefmt(&treefmt_toml, &old_ctx, &mfst)?;
let context = if mfst.manifest.is_empty() && ctxs.is_empty() {
@ -31,21 +35,11 @@ pub fn run_treefmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
&ctxs
};
if !treefmt_toml.exists() {
return Err(anyhow!(
"{}treefmt.toml not found, please run --init command",
customlog::ERROR
));
}
println!("===========================");
for c in context {
if !c.metadata.is_empty() {
println!("{}", c.command);
println!(
"Working Directory: {}",
c.work_dir.clone().unwrap_or("".to_string())
);
println!("{}", c.name);
println!("Working Directory: {}", c.work_dir.display());
println!("Files:");
for m in &c.metadata {
let path = &m.path;
@ -60,18 +54,15 @@ pub fn run_treefmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> {
.map(|c| {
let arg = &c.options;
let mut cmd_arg = Command::new(&c.path);
let work_dir = match c.work_dir.clone() {
Some(x) => x,
None => String::new(),
};
// Set the command to run under its working directory.
cmd_arg.current_dir(&c.work_dir);
// Append the default options to the command.
cmd_arg.args(arg);
// Append all of the file paths to format.
let paths = c.metadata.iter().map(|f| &f.path);
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()
}
cmd_arg.args(paths);
// And run
cmd_arg.output()
})
.collect();
@ -135,10 +126,9 @@ pub fn glob_to_path(
pub fn path_to_filemeta(paths: Vec<PathBuf>) -> Result<BTreeSet<FileMeta>> {
let mut filemeta = BTreeSet::new();
for p in paths {
let metadata = metadata(&p)?;
let mtime = FileTime::from_last_modification_time(&metadata).unix_seconds();
let mtime = get_mtime(&p)?;
if !filemeta.insert(FileMeta {
mtimes: mtime,
mtime,
path: p.clone(),
}) {
CLOG.warn("Duplicated file detected:");
@ -156,12 +146,12 @@ pub fn create_command_context(
) -> Result<Vec<CmdContext>> {
let cmd_context: Vec<CmdContext> = toml_content
.formatter
.values()
.map(|config| {
.iter()
.map(|(name, config)| {
let list_files = glob_to_path(&cwd.to_path_buf(), &config.includes, &config.excludes)?;
let cmd_meta = CmdMeta::new(config.command.clone())?;
let cmd_meta = CmdMeta::new(&config.command)?;
Ok(CmdContext {
command: cmd_meta.name,
name: name.clone(),
mtime: cmd_meta.mtime,
path: cmd_meta.path,
options: config.options.clone(),
@ -195,12 +185,11 @@ mod tests {
#[test]
fn test_path_to_filemeta() -> Result<()> {
let file_path = PathBuf::from(r"examples/rust/src/main.rs");
let metadata = metadata(&file_path)?;
let mtime = FileTime::from_last_modification_time(&metadata).unix_seconds();
let mtime = get_mtime(&file_path)?;
let mut vec_path = Vec::new();
vec_path.push(file_path);
let file_meta = FileMeta {
mtimes: mtime,
mtime,
path: PathBuf::from(r"examples/rust/src/main.rs"),
};
let mut set_filemeta = BTreeSet::new();

View File

@ -2,19 +2,22 @@
use crate::{customlog, CmdContext, CLOG};
use anyhow::{anyhow, Error, Result};
use filetime::FileTime;
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
use std::collections::BTreeMap;
use std::fmt;
use std::fs::{read_to_string, File};
use std::io::Write;
use std::path::PathBuf;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Default, Deserialize, Serialize)]
/// RootManifest
pub struct RootManifest {
/// Map of manifests config based on its formatter
pub manifest: BTreeMap<String, CmdContext>,
}
/// Create <hex(hash(path-to-treefmt))>.toml and put it in $XDG_CACHE_DIR/treefmt/eval-cache/
pub fn create_manifest(
treefmt_toml: PathBuf,
@ -27,20 +30,20 @@ pub fn create_manifest(
let map_manifest: BTreeMap<String, CmdContext> = cmd_ctx
.into_iter()
.map(|cmd| {
let treefmt = cmd.command.clone();
let name = cmd.name.clone();
let manifest = CmdContext {
command: cmd.command,
mtime: cmd.mtime,
name: cmd.name,
path: cmd.path,
mtime: cmd.mtime,
work_dir: cmd.work_dir,
options: cmd.options,
metadata: cmd.metadata,
};
(treefmt, manifest)
(name, manifest)
})
.collect();
let manifest_toml = RootManifest {
manifest: map_manifest.clone(),
manifest: map_manifest,
};
f.write_all(
format!(
@ -106,18 +109,18 @@ pub fn check_treefmt(
) -> Result<Vec<CmdContext>> {
let cache_context = cache.manifest.values();
let map_ctx: BTreeMap<String, CmdContext> = cmd_context
.into_iter()
.iter()
.map(|cmd| {
let treefmt = cmd.command.clone();
let name = cmd.name.clone();
let ctx = CmdContext {
command: cmd.command.clone(),
mtime: cmd.mtime.clone(),
name: cmd.name.clone(),
mtime: cmd.mtime,
path: cmd.path.clone(),
work_dir: cmd.work_dir.clone(),
options: cmd.options.clone(),
metadata: cmd.metadata.clone(),
};
(treefmt, ctx)
(name, ctx)
})
.collect();
let new_cmd_ctx = map_ctx.values();
@ -126,12 +129,12 @@ pub fn check_treefmt(
.clone()
.map(|(new, old)| {
Ok(CmdContext {
command: new.command.clone(),
mtime: new.mtime.clone(),
name: new.name.clone(),
path: new.path.clone(),
mtime: new.mtime,
work_dir: new.work_dir.clone(),
options: new.options.clone(),
metadata: if new.command != old.command
metadata: if new.path != old.path
|| new.options != old.options
|| new.work_dir != old.work_dir
{
@ -160,7 +163,7 @@ pub fn check_treefmt(
CLOG.info(&format!(
" - {} last modification time: {}",
p.path.display(),
p.mtimes
p.mtime
));
}
}
@ -199,3 +202,21 @@ mod tests {
Ok(())
}
}
/// Mtime represents a unix epoch file modification time
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Copy, Clone)]
pub struct Mtime(i64);
impl fmt::Display for Mtime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
/// Small utility that stat() and retrieve the mtime of a file
pub fn get_mtime(path: &PathBuf) -> Result<Mtime> {
let metadata = std::fs::metadata(path)?;
Ok(Mtime(
FileTime::from_last_modification_time(&metadata).unix_seconds(),
))
}

View File

@ -7,16 +7,14 @@ pub mod customlog;
pub mod engine;
pub mod eval_cache;
use crate::eval_cache::{get_mtime, Mtime};
use anyhow::Result;
use customlog::CustomLogOutput;
use filetime::FileTime;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::BTreeSet;
use std::fmt;
use std::fs::metadata;
use std::path::PathBuf;
use which::which;
/// The global custom log and user-facing message output.
pub static CLOG: CustomLogOutput = CustomLogOutput::new();
@ -24,14 +22,14 @@ pub static CLOG: CustomLogOutput = CustomLogOutput::new();
#[derive(Debug, Deserialize, Serialize, Clone)]
/// Each context of the formatter config
pub struct CmdContext {
/// formatter command to run
pub command: String,
/// Last modification time listed in the file's metadata
pub mtime: i64,
/// Name of the command
pub name: String,
/// Path to the formatted file
pub path: PathBuf,
/// Last modification time listed in the file's metadata
pub mtime: Mtime,
/// formatter work_dir
pub work_dir: Option<String>,
pub work_dir: PathBuf,
/// formatter arguments or flags
pub options: Vec<String>,
/// formatter target path
@ -44,12 +42,12 @@ impl CmdContext {
let new_meta: BTreeSet<FileMeta> = self
.metadata
.into_iter()
.map(|e| e.update_mtimes())
.map(|e| e.update_mtime())
.collect::<Result<BTreeSet<FileMeta>>>()?;
Ok(CmdContext {
command: self.command,
mtime: self.mtime,
name: self.name,
path: self.path,
mtime: self.mtime,
work_dir: self.work_dir,
options: self.options,
metadata: new_meta,
@ -59,7 +57,8 @@ impl CmdContext {
impl PartialEq for CmdContext {
fn eq(&self, other: &Self) -> bool {
self.command == other.command
self.name == other.name
&& self.path == other.path
&& self.work_dir == other.work_dir
&& self.options == other.options
&& self.metadata == other.metadata
@ -71,58 +70,34 @@ impl Eq for CmdContext {}
#[derive(Debug, Deserialize, Serialize, Clone)]
/// Command metadata created after the first treefmt run
pub struct CmdMeta {
/// Name provided by user's config
pub name: String,
/// Last modification time listed in the file's metadata
pub mtime: i64,
/// Path to the formatted file
/// Absolute path to the formatter
pub path: PathBuf,
/// Last modification time listed in the file's metadata
pub mtime: Mtime,
}
impl CmdMeta {
/// Create new CmdMeta based on the given config name
pub fn new(cmd: String) -> Result<Self> {
let cmd_path = Self::check_bin(&cmd)?;
let metadata = metadata(&cmd_path)?;
let cmd_mtime = FileTime::from_last_modification_time(&metadata).unix_seconds();
///
/// We assume that cmd_path is absolute.
pub fn new(cmd_path: &PathBuf) -> Result<Self> {
assert!(cmd_path.is_absolute());
Ok(CmdMeta {
name: cmd,
mtime: cmd_mtime,
path: cmd_path,
path: cmd_path.clone(),
mtime: get_mtime(&cmd_path)?,
})
}
/// Make sure that formatter binary exists. This also for other formatter
fn check_bin<'a>(command: &'a str) -> Result<PathBuf> {
let cmd_bin = command.split_ascii_whitespace().next().unwrap_or("");
if let Ok(path) = which(cmd_bin) {
CLOG.debug(&format!("Found {} at {}", cmd_bin, path.display()));
return Ok(path);
}
anyhow::bail!(
"Failed to locate formatter named {}. \
Please make sure it is available in your PATH",
command
)
}
}
impl fmt::Display for CmdMeta {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}: ({}, {})",
self.name,
self.path.display(),
self.mtime
)
write!(f, "({}, {})", self.path.display(), self.mtime)
}
}
impl PartialEq for CmdMeta {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.mtime == other.mtime && self.path == other.path
self.path == other.path && self.mtime == other.mtime
}
}
@ -131,19 +106,18 @@ impl Eq for CmdMeta {}
#[derive(Debug, Deserialize, Serialize, Clone)]
/// File metadata created after the first treefmt run
pub struct FileMeta {
/// Last modification time listed in the file's metadata
pub mtimes: i64,
/// Path to the formatted file
pub path: PathBuf,
/// Last modification time listed in the file's metadata
pub mtime: Mtime,
}
impl FileMeta {
fn update_mtimes(self) -> Result<Self> {
let metadata = metadata(&self.path)?;
let mtime = FileTime::from_last_modification_time(&metadata).unix_seconds();
fn update_mtime(self) -> Result<Self> {
let mtime = get_mtime(&self.path)?;
Ok(FileMeta {
mtimes: mtime,
path: self.path,
mtime,
})
}
}
@ -153,10 +127,10 @@ impl Ord for FileMeta {
if self.eq(other) {
return Ordering::Equal;
}
if self.mtimes.eq(&other.mtimes) {
if self.mtime.eq(&other.mtime) {
return self.path.cmp(&other.path);
}
self.mtimes.cmp(&other.mtimes)
self.mtime.cmp(&other.mtime)
}
}
@ -168,7 +142,7 @@ impl PartialOrd for FileMeta {
impl PartialEq for FileMeta {
fn eq(&self, other: &Self) -> bool {
self.mtimes == other.mtimes && self.path == other.path
self.mtime == other.mtime && self.path == other.path
}
}

View File

@ -1,7 +1,6 @@
#![allow(clippy::redundant_closure, clippy::redundant_pattern_matching)]
use structopt::StructOpt;
use treefmt::command::{run_cli, Cli};
use treefmt::command::{cli_from_args, run_cli};
use treefmt::CLOG;
fn main() {
@ -12,7 +11,7 @@ fn main() {
}
fn run() -> anyhow::Result<()> {
let options = Cli::from_args();
let options = cli_from_args()?;
CLOG.set_log_level(options.log_level);