1use std::fmt;
9
10#[derive(Debug, PartialEq, Eq)]
11pub enum Error {
12 Parse {
13 message: String,
14 line: Option<u32>,
15 property: Option<String>,
16 },
17}
18
19impl fmt::Display for Error {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Self::Parse {
23 message,
24 line,
25 property,
26 } => {
27 f.write_str("parse error")?;
28 if let Some(l) = line {
29 write!(f, " at line {l}")?;
30 }
31 if let Some(p) = property {
32 write!(f, " [{p}]")?;
33 }
34 write!(f, ": {message}")
35 }
36 }
37 }
38}
39
40impl std::error::Error for Error {}
41
42impl Error {
43 pub fn parse(message: impl Into<String>) -> Self {
46 Self::Parse {
47 message: message.into(),
48 line: None,
49 property: None,
50 }
51 }
52
53 pub fn parse_at_line(line: u32, message: impl Into<String>) -> Self {
56 Self::Parse {
57 message: message.into(),
58 line: Some(line),
59 property: None,
60 }
61 }
62
63 pub fn parse_at(line: u32, property: impl Into<String>, message: impl Into<String>) -> Self {
65 Self::Parse {
66 message: message.into(),
67 line: Some(line),
68 property: Some(property.into()),
69 }
70 }
71}
72
73pub type Result<T> = std::result::Result<T, Error>;
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn display_without_line_or_property() {
81 let err = Error::parse("VEVENT missing DTSTAMP");
82 assert_eq!(err.to_string(), "parse error: VEVENT missing DTSTAMP");
83 }
84
85 #[test]
86 fn display_with_line_only() {
87 let err = Error::parse_at_line(42, "VEVENT missing DTSTAMP");
88 assert_eq!(
89 err.to_string(),
90 "parse error at line 42: VEVENT missing DTSTAMP"
91 );
92 }
93
94 #[test]
95 fn display_with_line_and_property() {
96 let err = Error::parse_at(42, "DTSTAMP", "Invalid value");
97 assert_eq!(
98 err.to_string(),
99 "parse error at line 42 [DTSTAMP]: Invalid value"
100 );
101 }
102}