webvtt_parser/
error.rs

1use core::fmt;
2
3use nom::error::{ContextError, Error, ErrorKind, ParseError};
4use nom_locate::LocatedSpan;
5
6#[derive(Debug, Clone)]
7pub struct VttError {
8    /// What we are looking for
9    pub looking_for: String,
10    /// Span with error details
11    pub line: u32,
12    pub offset: usize,
13    pub fragment: String,
14    /// Context-specific message
15    pub message: Option<String>,
16}
17
18impl std::fmt::Display for VttError {
19    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
20        let Self {
21            looking_for,
22            line,
23            offset,
24            fragment,
25            message,
26        } = self;
27        let message = match message.as_ref() {
28            Some(message) => format!("\n{message}"),
29            None => "".to_owned(),
30        };
31
32        write!(
33            formatter,
34            ".vtt parsing error at {line}:{offset}, looking for {looking_for}. Found: {fragment}{message}",
35        )
36    }
37}
38
39impl<'a> ParseError<LocatedSpan<&'a str>> for VttError {
40    fn from_error_kind(input: LocatedSpan<&'a str>, kind: ErrorKind) -> Self {
41        VttError {
42            message: None,
43            line: input.location_line(),
44            offset: input.location_offset(),
45            looking_for: format!("{kind:?}"),
46            fragment: input.fragment().to_string(),
47        }
48    }
49
50    fn append(input: LocatedSpan<&'a str>, kind: ErrorKind, _other: Self) -> Self {
51        VttError {
52            message: None,
53            line: input.location_line(),
54            offset: input.location_offset(),
55            looking_for: format!("{kind:?}"),
56            fragment: input.fragment().to_string(),
57        }
58    }
59
60    fn from_char(input: LocatedSpan<&'a str>, c: char) -> Self {
61        VttError {
62            message: None,
63            line: input.location_line(),
64            offset: input.location_offset(),
65            looking_for: c.to_string(),
66            fragment: input.fragment().to_string(),
67        }
68    }
69
70    fn or(self, other: Self) -> Self {
71        let message = format!(
72            "Failure. Looking for {} or {}\n",
73            self.looking_for, other.looking_for
74        );
75
76        VttError {
77            line: self.line,
78            offset: self.offset,
79            fragment: self.fragment,
80            looking_for: self.looking_for,
81            message: Some(message),
82        }
83    }
84}
85
86impl<'a> ContextError<LocatedSpan<&'a str>> for VttError {
87    fn add_context(input: LocatedSpan<&'a str>, ctx: &'static str, other: Self) -> Self {
88        VttError {
89            message: Some(ctx.to_string()),
90            line: input.location_line(),
91            offset: input.location_offset(),
92            looking_for: other.looking_for,
93            fragment: input.fragment().to_string(),
94        }
95    }
96}
97
98impl<'a> From<nom::Err<Error<LocatedSpan<&'a str>>>> for VttError {
99    fn from(error: nom::Err<Error<LocatedSpan<&'a str>>>) -> Self {
100        match error {
101            nom::Err::Error(Error { input, code }) => VttError::from_error_kind(input, code),
102            nom::Err::Failure(Error { input, code }) => VttError::from_error_kind(input, code),
103            nom::Err::Incomplete(_) => VttError {
104                line: 0,
105                offset: 0,
106                fragment: "".to_owned(),
107                looking_for: "".to_owned(),
108                message: Some("Incomplete data, giving up parsing.".to_owned()),
109            },
110        }
111    }
112}