scrape_core/query/
error.rs1use thiserror::Error;
4
5use crate::error::{SourcePosition, SourceSpan};
6
7pub type QueryResult<T> = std::result::Result<T, QueryError>;
9
10#[derive(Debug, Error, Clone, PartialEq, Eq)]
16pub enum QueryError {
17 #[error("invalid selector{}: {message}", format_position(span.as_ref()))]
19 InvalidSelector {
20 message: String,
22 span: Option<SourceSpan>,
24 },
25}
26
27fn format_position(span: Option<&SourceSpan>) -> String {
28 span.map_or_else(String::new, |s| {
29 format!(" at line {}, column {}", s.start.line, s.start.column)
30 })
31}
32
33impl QueryError {
34 #[must_use]
36 pub fn invalid_selector(message: impl Into<String>) -> Self {
37 Self::InvalidSelector { message: message.into(), span: None }
38 }
39
40 #[must_use]
42 pub fn invalid_selector_at(message: impl Into<String>, line: usize, column: usize) -> Self {
43 Self::InvalidSelector {
44 message: message.into(),
45 span: Some(SourceSpan::new(
46 SourcePosition::new(line, column, 0),
47 SourcePosition::new(line, column + 1, 0),
48 )),
49 }
50 }
51
52 #[must_use]
54 pub fn span(&self) -> Option<&SourceSpan> {
55 match self {
56 Self::InvalidSelector { span, .. } => span.as_ref(),
57 }
58 }
59
60 #[must_use]
62 pub fn line(&self) -> Option<usize> {
63 self.span().map(|s| s.start.line)
64 }
65
66 #[must_use]
68 pub fn column(&self) -> Option<usize> {
69 self.span().map(|s| s.start.column)
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_query_error_display() {
79 let err = QueryError::invalid_selector("unexpected token at position 5");
80 assert_eq!(err.to_string(), "invalid selector: unexpected token at position 5");
81 }
82
83 #[test]
84 fn test_query_error_with_position() {
85 let err = QueryError::invalid_selector_at("unexpected token", 1, 5);
86 assert_eq!(err.to_string(), "invalid selector at line 1, column 5: unexpected token");
87 assert_eq!(err.line(), Some(1));
88 assert_eq!(err.column(), Some(5));
89 }
90
91 #[test]
92 fn test_query_error_equality() {
93 let err1 = QueryError::invalid_selector("foo");
94 let err2 = QueryError::invalid_selector("foo");
95 let err3 = QueryError::invalid_selector("bar");
96 assert_eq!(err1, err2);
97 assert_ne!(err1, err3);
98 }
99
100 #[test]
101 fn test_query_error_span() {
102 let err_with_span = QueryError::invalid_selector_at("test", 2, 7);
103 assert!(err_with_span.span().is_some());
104
105 let err_without_span = QueryError::invalid_selector("test");
106 assert!(err_without_span.span().is_none());
107 assert_eq!(err_without_span.line(), None);
108 assert_eq!(err_without_span.column(), None);
109 }
110
111 #[test]
112 fn test_query_result_type() {
113 let ok: QueryResult<i32> = Ok(42);
114 let err: QueryResult<i32> = Err(QueryError::invalid_selector("test"));
115
116 assert!(ok.is_ok());
117 assert!(err.is_err());
118 }
119}