at main 152 lines 4.6 kB view raw
1//! Error types for weaver - thin wrapper over jacquard errors 2 3use jacquard::{types::string::AtStrError, xrpc::GenericXrpcError}; 4use miette::{Diagnostic, NamedSource, SourceOffset, SourceSpan}; 5use std::borrow::Cow; 6 7/// Main error type for weaver operations 8#[derive(thiserror::Error, Debug, Diagnostic)] 9pub enum WeaverError { 10 /// Jacquard Agent error 11 #[error(transparent)] 12 #[diagnostic_source] 13 Agent(#[from] jacquard::client::error::AgentError), 14 15 /// Jacquard Identity resolution error 16 #[error(transparent)] 17 #[diagnostic_source] 18 Identity(#[from] jacquard::identity::resolver::IdentityError), 19 20 /// Invalid notebook structure 21 #[error("invalid notebook structure: {0}")] 22 InvalidNotebook(String), 23 24 /// Markdown parsing/rendering error 25 #[error("markdown error: {0}")] 26 Markdown(String), 27 28 /// IO error 29 #[error(transparent)] 30 Io(#[from] n0_future::io::Error), 31 32 /// Parse error with source location 33 #[error(transparent)] 34 #[diagnostic_source] 35 Parse(#[from] ParseError), 36 37 /// Serialization/deserialization error 38 #[error(transparent)] 39 #[diagnostic_source] 40 Serde(#[from] SerDeError), 41 42 /// Task join error 43 #[error(transparent)] 44 Task(#[from] n0_future::task::JoinError), 45 46 /// atproto string parsing error 47 #[error(transparent)] 48 AtprotoString(#[from] AtStrError), 49 50 /// XRPC error 51 #[error(transparent)] 52 Xrpc(#[from] jacquard::xrpc::XrpcError<GenericXrpcError>), 53} 54 55/// Parse error with source code location information 56#[derive(thiserror::Error, Debug, Diagnostic)] 57#[error("parse error: {}",self.kind)] 58#[diagnostic(code(weaver::parse))] 59pub struct ParseError { 60 #[diagnostic_source] 61 kind: ParseErrorKind, 62 #[source_code] 63 src: NamedSource<Cow<'static, str>>, 64 #[label("error")] 65 err_location: SourceSpan, 66 err_line_col: Option<(usize, usize)>, 67 #[help] 68 advice: Option<String>, 69} 70 71impl ParseError { 72 pub fn with_source(self, src: NamedSource<Cow<'static, str>>) -> Self { 73 if let Some((line, column)) = self.err_line_col { 74 let location = SourceSpan::new( 75 SourceOffset::from_location(src.inner(), line, column), 76 self.err_location.len(), 77 ); 78 Self { 79 kind: self.kind, 80 src, 81 err_location: location, 82 err_line_col: Some((line, column)), 83 advice: self.advice, 84 } 85 } else { 86 let (line, col) = offset_to_line_col(self.err_location.offset(), &self.src); 87 let len = self.err_location.len(); 88 let location = 89 SourceSpan::new(SourceOffset::from_location(src.inner(), line, col), len); 90 Self { 91 kind: self.kind, 92 src, 93 err_location: location, 94 err_line_col: self.err_line_col, 95 advice: self.advice, 96 } 97 } 98 } 99} 100 101#[derive(thiserror::Error, Debug, Diagnostic)] 102#[non_exhaustive] 103pub enum ParseErrorKind { 104 #[error(transparent)] 105 SerdeError(#[from] SerDeError), 106 #[error("error in markdown parsing or rendering: {0}")] 107 MarkdownError(markdown_weaver::CowStr<'static>), 108} 109 110/// Serialization/deserialization errors 111#[derive(thiserror::Error, Debug, Diagnostic)] 112#[non_exhaustive] 113pub enum SerDeError { 114 #[error(transparent)] 115 #[diagnostic_source] 116 Json(#[from] serde_json::Error), 117} 118 119impl From<serde_json::Error> for ParseError { 120 fn from(err: serde_json::Error) -> Self { 121 let line = err.line(); 122 let column = err.column(); 123 let location = SourceSpan::new(SourceOffset::from_location("", line, column), 0); 124 Self { 125 kind: ParseErrorKind::SerdeError(SerDeError::Json(err)), 126 src: NamedSource::new(Cow::Borrowed("json"), Cow::Borrowed("")), 127 err_location: location, 128 advice: None, 129 err_line_col: Some((line, column)), 130 } 131 } 132} 133 134fn offset_to_line_col(offset: usize, src: &NamedSource<Cow<'static, str>>) -> (usize, usize) { 135 let mut acc_chars = 0usize; 136 137 for (i, line) in src.inner().split_inclusive('\n').enumerate() { 138 acc_chars += line.len(); 139 if offset < acc_chars { 140 let mut col = 0usize; 141 let line_offset = offset - acc_chars; 142 for (byte_idx, _) in line.char_indices() { 143 if byte_idx >= line_offset { 144 return (i + 1, col); 145 } 146 col += 1; 147 } 148 return (i + 1, col); 149 } 150 } 151 (src.inner().lines().count(), 0) 152}