#![allow(clippy::print_stdout)] use std::path::PathBuf; use anyhow::{bail, Context, Result}; use clap::{command, Parser, ValueEnum}; use rustpython_parser::lexer::lex; use rustpython_parser::{parse_tokens, Mode}; use ruff_formatter::SourceCode; use ruff_python_ast::source_code::CommentRangesBuilder; use crate::format_node; #[derive(ValueEnum, Clone, Debug)] pub enum Emit { /// Write back to the original files Files, /// Write to stdout Stdout, } #[derive(Parser)] #[command(author, version, about, long_about = None)] pub struct Cli { /// Python files to format. If there are none, stdin will be used. `-` as stdin is not supported pub files: Vec, #[clap(long)] pub emit: Option, /// Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits with 1 and prints /// a diff if formatting is required. #[clap(long)] pub check: bool, #[clap(long)] pub print_ir: bool, #[clap(long)] pub print_comments: bool, } pub fn format_and_debug_print(input: &str, cli: &Cli) -> Result { let mut tokens = Vec::new(); let mut comment_ranges = CommentRangesBuilder::default(); for result in lex(input, Mode::Module) { let (token, range) = match result { Ok((token, range)) => (token, range), Err(err) => bail!("Source contains syntax errors {err:?}"), }; comment_ranges.visit_token(&token, range); tokens.push(Ok((token, range))); } let comment_ranges = comment_ranges.finish(); // Parse the AST. let python_ast = parse_tokens(tokens, Mode::Module, "") .with_context(|| "Syntax error in input")?; let formatted = format_node(&python_ast, &comment_ranges, input)?; if cli.print_ir { println!("{}", formatted.document().display(SourceCode::new(input))); } if cli.print_comments { println!( "{:?}", formatted.context().comments().debug(SourceCode::new(input)) ); } Ok(formatted .print() .with_context(|| "Failed to print the formatter IR")? .as_code() .to_string()) }