1use std::fmt;
4
5#[derive(Clone, Debug, PartialEq)]
7pub struct SourcePosition {
8 pub line: usize,
9 pub column: usize,
10}
11
12impl SourcePosition {
13 pub fn new(line: usize, column: usize) -> Self {
14 Self {
15 line,
16 column,
17 }
18 }
19}
20
21#[derive(Debug)]
22enum ErrorKind {
23 Parse { message: String },
24 Syntax { message: String, position: SourcePosition },
25 UndefinedVariable { name: String, position: SourcePosition },
26 UndefinedFilter { name: String, position: SourcePosition },
27 UndefinedTemplate { name: String },
28 Render { message: String },
29 Type { message: String },
30 Io(std::io::Error),
31}
32
33#[derive(Debug)]
35pub struct Error {
36 inner: Box<ErrorInner>,
37}
38
39#[derive(Debug)]
40struct ErrorInner {
41 kind: ErrorKind,
42 source: Option<String>,
43}
44
45impl Error {
46 fn new(kind: ErrorKind) -> Self {
47 Self {
48 inner: Box::new(ErrorInner {
49 kind,
50 source: None,
51 }),
52 }
53 }
54
55 pub fn parse(message: impl Into<String>) -> Self {
57 Self::new(ErrorKind::Parse {
58 message: message.into(),
59 })
60 }
61
62 pub fn syntax(message: impl Into<String>, line: usize, column: usize) -> Self {
64 Self::new(ErrorKind::Syntax {
65 message: message.into(),
66 position: SourcePosition::new(line, column),
67 })
68 }
69
70 pub fn undefined_variable(name: impl Into<String>, line: usize, column: usize) -> Self {
72 Self::new(ErrorKind::UndefinedVariable {
73 name: name.into(),
74 position: SourcePosition::new(line, column),
75 })
76 }
77
78 pub fn undefined_filter(name: impl Into<String>, line: usize, column: usize) -> Self {
80 Self::new(ErrorKind::UndefinedFilter {
81 name: name.into(),
82 position: SourcePosition::new(line, column),
83 })
84 }
85
86 pub fn undefined_template(name: impl Into<String>) -> Self {
88 Self::new(ErrorKind::UndefinedTemplate {
89 name: name.into(),
90 })
91 }
92
93 pub fn render(message: impl Into<String>) -> Self {
95 Self::new(ErrorKind::Render {
96 message: message.into(),
97 })
98 }
99
100 pub fn r#type(message: impl Into<String>) -> Self {
102 Self::new(ErrorKind::Type {
103 message: message.into(),
104 })
105 }
106
107 pub fn with_source(mut self, source: impl Into<String>) -> Self {
109 self.inner.source = Some(source.into());
110 self
111 }
112}
113
114impl From<std::io::Error> for Error {
115 fn from(err: std::io::Error) -> Self {
116 Self::new(ErrorKind::Io(err))
117 }
118}
119
120impl fmt::Display for Error {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match &self.inner.kind {
123 ErrorKind::Parse {
124 message,
125 } => {
126 write!(f, "parse error: {message}")
127 }
128 ErrorKind::Syntax {
129 message,
130 position,
131 } => {
132 write!(f, "syntax error at {}:{}: {message}", position.line, position.column)
133 }
134 ErrorKind::UndefinedVariable {
135 name,
136 position,
137 } => {
138 write!(f, "undefined variable `{name}` at {}:{}", position.line, position.column)
139 }
140 ErrorKind::UndefinedFilter {
141 name,
142 position,
143 } => {
144 write!(f, "undefined filter `{name}` at {}:{}", position.line, position.column)
145 }
146 ErrorKind::UndefinedTemplate {
147 name,
148 } => {
149 write!(f, "undefined template `{name}`")
150 }
151 ErrorKind::Render {
152 message,
153 } => {
154 write!(f, "render error: {message}")
155 }
156 ErrorKind::Type {
157 message,
158 } => {
159 write!(f, "type error: {message}")
160 }
161 ErrorKind::Io(err) => {
162 write!(f, "I/O error: {err}")
163 }
164 }
165 }
166}
167
168impl std::error::Error for Error {
169 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
170 match &self.inner.kind {
171 ErrorKind::Io(err) => Some(err),
172 _ => None,
173 }
174 }
175}