omake/
context.rs

1//! Simple implementation of a `Context` struct designed to track parsing/execution location.
2
3use std::path::PathBuf;
4
5/// Represents parsing/execution context, specifically, which file and where in the file something
6/// is happening.
7#[derive(Clone, Debug)]
8pub struct Context {
9    pub path: Option<PathBuf>,
10    pub content: Option<String>,
11
12    // Line/row number is determined when iterating the input, so we use `usize` here to match the
13    // return type of `enumerate()`. Both line and row are `1`-indexed to match the convention other
14    // programs (including other make implementations) use when referencing line/column numbers, so
15    // `0` is a sentinel value indicating that the value is not set.
16    pub line_index: Option<usize>,
17    pub column_index: Option<usize>,
18}
19
20impl Context {
21    pub fn new() -> Self {
22        Self {
23            path: None,
24            content: None,
25            line_index: None,
26            column_index: None,
27        }
28    }
29
30    pub fn label(&self) -> Option<String> {
31        let Some(path_display) = self.path.as_ref().map(|p| p.display() ) else {
32            return None;
33        };
34
35        // Assume row index would only be set if line index is set. Also, increment indices by 1 to
36        // convert to the traditional line/column display "number".
37        return match self.line_index {
38            Some(line) => match self.column_index {
39                Some(column) => Some(format!("{}:{}:{}", path_display, line + 1, column + 1)),
40                None => Some(format!("{}:{}", path_display, line + 1)),
41            },
42            None => Some(path_display.to_string()),
43        };
44    }
45
46    pub fn display_line(&self) -> Option<String> {
47        self.content.as_ref().map(|content| match self.line_index {
48            Some(line) => {
49                let line_s = (line + 1).to_string();
50                let pad = " ".repeat(line_s.len());
51
52                match self.column_index {
53                    Some(column) => format!(
54                        "{pad} |\n{line_number} | {content}\n{pad} | {caret_padding}^\n",
55                        pad = pad,
56                        line_number = line_s,
57                        content = content,
58                        caret_padding = " ".repeat(column),
59                    ),
60                    None => format!(
61                        "{pad} |\n{line_number} | {content}\n{pad} | \n",
62                        pad = pad,
63                        line_number = line_s,
64                        content = content,
65                    ),
66                }
67            }
68            None => format!(" | {content}\n", content = content),
69        })
70    }
71}
72
73impl From<PathBuf> for Context {
74    fn from(path: PathBuf) -> Self {
75        let mut context = Self::new();
76        context.path = Some(path);
77        context
78    }
79}