Skip to main content

code_frame/
code_frame.rs

1use std::{fs, process::ExitCode};
2
3use clap::Parser;
4use next_code_frame::{CodeFrameLocation, CodeFrameOptions, Location, render_code_frame};
5
6/// Render a code frame for the given file and position.
7///
8/// Lines and columns are 1-indexed.
9#[derive(Parser)]
10#[command(name = "code_frame")]
11struct Args {
12    /// Source file to render
13    file: String,
14    /// Start position (line:column, 1-indexed)
15    start: String,
16    /// End position (line:column, 1-indexed)
17    end: Option<String>,
18    /// Message to display with the error
19    #[arg(short, long)]
20    message: Option<String>,
21    /// Maximum output width in columns
22    #[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            // Location is out of range (e.g., empty file or line past EOF)
76            ExitCode::SUCCESS
77        }
78        Err(e) => {
79            eprintln!("Error rendering code frame: {e}");
80            ExitCode::FAILURE
81        }
82    }
83}