From 5f0a1e67c64082c848418daa2b51020eb42c5c12 Mon Sep 17 00:00:00 2001 From: Akshay Date: Mon, 25 Oct 2021 22:08:52 +0530 Subject: rework cli, use subcommands instead --- bin/src/config.rs | 213 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 119 insertions(+), 94 deletions(-) (limited to 'bin/src/config.rs') diff --git a/bin/src/config.rs b/bin/src/config.rs index 6d7bc49..202ff6c 100644 --- a/bin/src/config.rs +++ b/bin/src/config.rs @@ -15,118 +15,74 @@ use crate::err::ConfigErr; #[derive(Clap, Debug)] #[clap(version = "0.1.0", author = "Akshay ")] pub struct Opts { - /// File or directory to run statix on - #[clap(default_value = ".")] - pub target: String, + #[clap(subcommand)] + pub cmd: SubCommand, +} + +#[derive(Clap, Debug)] +pub enum SubCommand { + /// Lints and suggestions for the nix programming language + Check(Check), + /// Find and fix issues raised by statix-check + Fix(Fix), + /// Fix exactly one issue at provided position + Single(Single), +} + +#[derive(Clap, Debug)] +pub struct Check { + /// File or directory to run check on + #[clap(default_value = ".", parse(from_os_str))] + target: PathBuf, /// Globs of file patterns to skip #[clap(short, long)] - pub ignore: Vec, + ignore: Vec, /// Output format. /// Supported values: errfmt, json (on feature flag only) - #[clap(short = 'o', long)] - format: Option, - - /// Find and fix issues raised by statix - #[clap(short = 'f', long)] - pub fix: bool, - - /// Do not fix files in place, display a diff instead - #[clap(short = 'd', long = "dry-run")] - diff_only: bool, -} - - -#[derive(Debug, Copy, Clone)] -pub enum OutFormat { - #[cfg(feature = "json")] - Json, - Errfmt, - StdErr, + #[clap(short = 'o', long, default_value = "OutFormat::StdErr")] + pub format: OutFormat, } -impl Default for OutFormat { - fn default() -> Self { - OutFormat::StdErr +impl Check { + pub fn vfs(&self) -> Result { + let files = walk_with_ignores(&self.ignore, &self.target)?; + vfs(files) } } -impl FromStr for OutFormat { - type Err = &'static str; +#[derive(Clap, Debug)] +pub struct Fix { + /// File or directory to run fix on + #[clap(default_value = ".", parse(from_os_str))] + target: PathBuf, - fn from_str(value: &str) -> Result { - match value.to_ascii_lowercase().as_str() { - #[cfg(feature = "json")] "json" => Ok(Self::Json), - "errfmt" => Ok(Self::Errfmt), - "stderr" => Ok(Self::StdErr), - "json" => Err("statix was not compiled with the `json` feature flag"), - _ => Err("unknown output format, try: json, errfmt"), - } - } -} + /// Globs of file patterns to skip + #[clap(short, long)] + ignore: Vec, -#[derive(Debug)] -pub struct LintConfig { - pub files: Vec, - pub format: OutFormat, + /// Do not fix files in place, display a diff instead + #[clap(short, long = "dry-run")] + pub diff_only: bool, } -impl LintConfig { - pub fn from_opts(opts: Opts) -> Result { - let ignores = build_ignore_set(&opts.ignore).map_err(|err| { - ConfigErr::InvalidGlob(err.glob().map(|i| i.to_owned()), err.kind().clone()) - })?; - - let files = walk_nix_files(&opts.target)? - .filter(|path| !ignores.is_match(path)) - .collect(); - - Ok(Self { - files, - format: opts.format.unwrap_or_default(), - }) - } - +impl Fix { pub fn vfs(&self) -> Result { - let mut vfs = ReadOnlyVfs::default(); - for file in self.files.iter() { - let _id = vfs.alloc_file_id(&file); - let data = fs::read_to_string(&file).map_err(ConfigErr::InvalidPath)?; - vfs.set_file_contents(&file, data.as_bytes()); - } - Ok(vfs) + let files = walk_with_ignores(&self.ignore, &self.target)?; + vfs(files) } } -pub struct FixConfig { - pub files: Vec, - pub diff_only: bool, -} - -impl FixConfig { - pub fn from_opts(opts: Opts) -> Result { - let ignores = build_ignore_set(&opts.ignore).map_err(|err| { - ConfigErr::InvalidGlob(err.glob().map(|i| i.to_owned()), err.kind().clone()) - })?; - - let files = walk_nix_files(&opts.target)? - .filter(|path| !ignores.is_match(path)) - .collect(); - - let diff_only = opts.diff_only; - Ok(Self { files, diff_only }) - } - - pub fn vfs(&self) -> Result { - let mut vfs = ReadOnlyVfs::default(); - for file in self.files.iter() { - let _id = vfs.alloc_file_id(&file); - let data = fs::read_to_string(&file).map_err(ConfigErr::InvalidPath)?; - vfs.set_file_contents(&file, data.as_bytes()); - } - Ok(vfs) - } +#[derive(Clap, Debug)] +pub struct Single { + /// File to run single-fix on + #[clap(default_value = ".", parse(from_os_str))] + pub target: PathBuf, + + /// Position to attempt a fix at + #[clap(short, long, parse(try_from_str = parse_line_col))] + pub position: (usize, usize), } mod dirs { @@ -185,7 +141,23 @@ mod dirs { } } -fn build_ignore_set(ignores: &Vec) -> Result { +fn parse_line_col(src: &str) -> Result<(usize, usize), ConfigErr> { + let parts = src.split(","); + match parts.collect::>().as_slice() { + [line, col] => { + let l = line + .parse::() + .map_err(|_| ConfigErr::InvalidPosition(src.to_owned()))?; + let c = col + .parse::() + .map_err(|_| ConfigErr::InvalidPosition(src.to_owned()))?; + Ok((l, c)) + } + _ => Err(ConfigErr::InvalidPosition(src.to_owned())), + } +} + +fn build_ignore_set(ignores: &[String]) -> Result { let mut set = GlobSetBuilder::new(); for pattern in ignores { let glob = GlobBuilder::new(&pattern).build()?; @@ -198,3 +170,56 @@ fn walk_nix_files>(target: P) -> Result>( + ignores: &[String], + target: P, +) -> Result, ConfigErr> { + let ignores = build_ignore_set(ignores).map_err(|err| { + ConfigErr::InvalidGlob(err.glob().map(|i| i.to_owned()), err.kind().clone()) + })?; + + Ok(walk_nix_files(&target)? + .filter(|path| !ignores.is_match(path)) + .collect()) +} + +fn vfs(files: Vec) -> Result { + let mut vfs = ReadOnlyVfs::default(); + for file in files.iter() { + let _id = vfs.alloc_file_id(&file); + let data = fs::read_to_string(&file).map_err(ConfigErr::InvalidPath)?; + vfs.set_file_contents(&file, data.as_bytes()); + } + Ok(vfs) + +} + +#[derive(Debug, Copy, Clone)] +pub enum OutFormat { + #[cfg(feature = "json")] + Json, + Errfmt, + StdErr, +} + +impl Default for OutFormat { + fn default() -> Self { + OutFormat::StdErr + } +} + +impl FromStr for OutFormat { + type Err = &'static str; + + fn from_str(value: &str) -> Result { + match value.to_ascii_lowercase().as_str() { + #[cfg(feature = "json")] + "json" => Ok(Self::Json), + "errfmt" => Ok(Self::Errfmt), + "stderr" => Ok(Self::StdErr), + "json" => Err("statix was not compiled with the `json` feature flag"), + _ => Err("unknown output format, try: json, errfmt"), + } + } +} -- cgit v1.2.3