//! FIXME: write short doc here mod load_cargo; mod analysis_stats; mod analysis_bench; mod progress_report; use std::{error::Error, fmt::Write, io::Read}; use pico_args::Arguments; use ra_ide::{file_structure, Analysis}; use ra_prof::profile; use ra_syntax::{AstNode, SourceFile}; type Result = std::result::Result>; fn main() -> Result<()> { env_logger::try_init()?; let command = Command::from_args()?; command.run()?; Ok(()) } #[derive(Clone, Copy)] pub enum Verbosity { Spammy, Verbose, Normal, Quiet, } impl Verbosity { fn is_verbose(self) -> bool { match self { Verbosity::Verbose | Verbosity::Spammy => true, _ => false, } } fn is_spammy(self) -> bool { match self { Verbosity::Spammy => true, _ => false, } } } enum Command { Parse { no_dump: bool, }, Symbols, Highlight { rainbow: bool, }, Stats { verbosity: Verbosity, randomize: bool, memory_usage: bool, only: Option, with_deps: bool, path: String, }, Bench { verbose: bool, path: String, op: analysis_bench::Op, }, HelpPrinted, } impl Command { fn from_args() -> Result { let mut matches = Arguments::from_env(); let subcommand = matches.subcommand()?.unwrap_or_default(); let command = match subcommand.as_str() { "parse" => { if matches.contains(["-h", "--help"]) { eprintln!( "\ ra-cli-parse USAGE: ra_cli parse [FLAGS] FLAGS: -h, --help Prints help inforamtion --no-dump" ); return Ok(Command::HelpPrinted); } let no_dump = matches.contains("--no-dump"); matches.finish().or_else(handle_extra_flags)?; Command::Parse { no_dump } } "symbols" => { if matches.contains(["-h", "--help"]) { eprintln!( "\ ra-cli-symbols USAGE: ra_cli highlight [FLAGS] FLAGS: -h, --help Prints help inforamtion" ); return Ok(Command::HelpPrinted); } matches.finish().or_else(handle_extra_flags)?; Command::Symbols } "highlight" => { if matches.contains(["-h", "--help"]) { eprintln!( "\ ra-cli-highlight USAGE: ra_cli highlight [FLAGS] FLAGS: -h, --help Prints help information -r, --rainbow" ); return Ok(Command::HelpPrinted); } let rainbow = matches.contains(["-r", "--rainbow"]); matches.finish().or_else(handle_extra_flags)?; Command::Highlight { rainbow } } "analysis-stats" => { if matches.contains(["-h", "--help"]) { eprintln!( "\ ra-cli-analysis-stats USAGE: ra_cli analysis-stats [FLAGS] [OPTIONS] [PATH] FLAGS: -h, --help Prints help information --memory-usage -v, --verbose -q, --quiet OPTIONS: -o ARGS: " ); return Ok(Command::HelpPrinted); } let verbosity = match ( matches.contains(["-vv", "--spammy"]), matches.contains(["-v", "--verbose"]), matches.contains(["-q", "--quiet"]), ) { (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?, (true, _, false) => Verbosity::Spammy, (false, false, false) => Verbosity::Normal, (false, false, true) => Verbosity::Quiet, (false, true, false) => Verbosity::Verbose, (false, true, true) => Err("Invalid flags: -q conflicts with -v")?, }; let randomize = matches.contains("--randomize"); let memory_usage = matches.contains("--memory-usage"); let only: Option = matches.opt_value_from_str(["-o", "--only"])?; let with_deps: bool = matches.contains("--with-deps"); let path = { let mut trailing = matches.free()?; if trailing.len() != 1 { Err("Invalid flags")?; } trailing.pop().unwrap() }; Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } } "analysis-bench" => { if matches.contains(["-h", "--help"]) { eprintln!( "\ ra_cli-analysis-bench USAGE: ra_cli analysis-bench [FLAGS] [OPTIONS] [PATH] FLAGS: -h, --help Prints help information -v, --verbose OPTIONS: --complete Compute completions at this location --highlight Hightlight this file ARGS: Project to analyse" ); return Ok(Command::HelpPrinted); } let verbose = matches.contains(["-v", "--verbose"]); let path: String = matches.opt_value_from_str("--path")?.unwrap_or_default(); let highlight_path: Option = matches.opt_value_from_str("--highlight")?; let complete_path: Option = matches.opt_value_from_str("--complete")?; let goto_def_path: Option = matches.opt_value_from_str("--goto-def")?; let op = match (highlight_path, complete_path, goto_def_path) { (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() }, (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?), (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?), _ => panic!( "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" ), }; Command::Bench { verbose, path, op } } _ => { eprintln!( "\ ra-cli USAGE: ra_cli FLAGS: -h, --help Prints help information SUBCOMMANDS: analysis-bench analysis-stats highlight parse symbols" ); return Ok(Command::HelpPrinted); } }; Ok(command) } fn run(self) -> Result<()> { match self { Command::Parse { no_dump } => { let _p = profile("parsing"); let file = file()?; if !no_dump { println!("{:#?}", file.syntax()); } std::mem::forget(file); } Command::Symbols => { let file = file()?; for s in file_structure(&file) { println!("{:?}", s); } } Command::Highlight { rainbow } => { let (analysis, file_id) = Analysis::from_single_file(read_stdin()?); let html = analysis.highlight_as_html(file_id, rainbow).unwrap(); println!("{}", html); } Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } => { analysis_stats::run( verbosity, memory_usage, path.as_ref(), only.as_ref().map(String::as_ref), with_deps, randomize, )?; } Command::Bench { verbose, path, op } => { analysis_bench::run(verbose, path.as_ref(), op)?; } Command::HelpPrinted => (), } Ok(()) } } fn handle_extra_flags(e: pico_args::Error) -> Result<()> { if let pico_args::Error::UnusedArgsLeft(flags) = e { let mut invalid_flags = String::new(); for flag in flags { write!(&mut invalid_flags, "{}, ", flag)?; } let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2); Err(format!("Invalid flags: {}", invalid_flags).into()) } else { Err(e.to_string().into()) } } fn file() -> Result { let text = read_stdin()?; Ok(SourceFile::parse(&text).tree()) } fn read_stdin() -> Result { let mut buff = String::new(); std::io::stdin().read_to_string(&mut buff)?; Ok(buff) }