1use std::{fs, process::ExitCode};
2
3use clap::Parser;
4use next_code_frame::{CodeFrameLocation, CodeFrameOptions, Location, render_code_frame};
5
6#[derive(Parser)]
10#[command(name = "code_frame")]
11struct Args {
12 file: String,
14 start: String,
16 end: Option<String>,
18 #[arg(short, long)]
20 message: Option<String>,
21 #[arg(short = 'w', long, default_value_t = 100)]
23 max_width: usize,
24}
25
26fn parse_position(s: &str) -> Option<Location> {
27 let (line, col) = s.split_once(':')?;
28 Some(Location {
29 line: line.parse().ok()?,
30 column: Some(col.parse().ok()?),
31 })
32}
33
34fn main() -> ExitCode {
35 let args = Args::parse();
36
37 let source = match fs::read_to_string(&args.file) {
38 Ok(s) => s,
39 Err(e) => {
40 eprintln!("Error reading {}: {e}", args.file);
41 return ExitCode::FAILURE;
42 }
43 };
44
45 let start = match parse_position(&args.start) {
46 Some(loc) => loc,
47 None => {
48 eprintln!("Invalid start position: {}", args.start);
49 return ExitCode::FAILURE;
50 }
51 };
52
53 let end = match args.end.as_deref().map(parse_position) {
54 Some(Some(loc)) => Some(loc),
55 Some(None) => {
56 eprintln!("Invalid end position: {}", args.end.unwrap());
57 return ExitCode::FAILURE;
58 }
59 None => None,
60 };
61
62 let location = CodeFrameLocation { start, end };
63 let options = CodeFrameOptions {
64 message: args.message,
65 max_width: args.max_width,
66 ..Default::default()
67 };
68
69 match render_code_frame(&source, &location, &options) {
70 Ok(Some(frame)) => {
71 println!("{frame}");
72 ExitCode::SUCCESS
73 }
74 Ok(None) => {
75 ExitCode::SUCCESS
77 }
78 Err(e) => {
79 eprintln!("Error rendering code frame: {e}");
80 ExitCode::FAILURE
81 }
82 }
83}