1- use std:: sync:: Arc ;
1+ use std:: { error :: Error , fmt :: Display , iter , sync:: Arc } ;
22
3- use miette:: { Diagnostic , SourceSpan } ;
4- use thiserror:: Error ;
3+ use miette:: { Diagnostic , LabeledSpan , Severity , SourceSpan } ;
54
65#[ cfg( doc) ]
76use {
@@ -34,30 +33,43 @@ use {
3433/// ╰────
3534/// help: Floating point numbers must be base 10, and have numbers after the decimal point.
3635/// ```
37- #[ derive( Debug , Diagnostic , Clone , Eq , PartialEq , Error ) ]
38- #[ error( "Failed to parse KDL document" ) ]
36+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
3937pub struct KdlError {
4038 /// Original input that this failure came from.
41- #[ source_code]
4239 pub input : Arc < String > ,
4340
4441 /// Sub-diagnostics for this failure.
45- #[ related]
4642 pub diagnostics : Vec < KdlDiagnostic > ,
4743}
4844
45+ impl Display for KdlError {
46+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
47+ write ! ( f, "Failed to parse KDL document" )
48+ }
49+ }
50+ impl Error for KdlError { }
51+
52+ impl Diagnostic for KdlError {
53+ fn source_code ( & self ) -> Option < & dyn miette:: SourceCode > {
54+ Some ( & self . input )
55+ }
56+
57+ fn related < ' a > ( & ' a self ) -> Option < Box < dyn Iterator < Item = & ' a dyn Diagnostic > + ' a > > {
58+ Some ( Box :: new (
59+ self . diagnostics . iter ( ) . map ( |d| d as & dyn Diagnostic ) ,
60+ ) )
61+ }
62+ }
63+
4964/// An individual diagnostic message for a KDL parsing issue.
5065///
5166/// While generally signifying errors, they can also be treated as warnings.
52- #[ derive( Debug , Diagnostic , Clone , Eq , PartialEq , Error ) ]
53- #[ error( "{}" , message. clone( ) . unwrap_or_else( || "Unexpected error" . into( ) ) ) ]
67+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
5468pub struct KdlDiagnostic {
5569 /// Shared source for the diagnostic.
56- #[ source_code]
5770 pub input : Arc < String > ,
5871
5972 /// Offset in chars of the error.
60- #[ label( "{}" , label. clone( ) . unwrap_or_else( || "here" . into( ) ) ) ]
6173 pub span : SourceSpan ,
6274
6375 /// Message for the error itself.
@@ -67,12 +79,42 @@ pub struct KdlDiagnostic {
6779 pub label : Option < String > ,
6880
6981 /// Suggestion for fixing the parser error.
70- #[ help]
7182 pub help : Option < String > ,
7283
7384 /// Severity level for the Diagnostic.
74- #[ diagnostic( severity) ]
75- pub severity : miette:: Severity ,
85+ pub severity : Severity ,
86+ }
87+
88+ impl Display for KdlDiagnostic {
89+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
90+ let message = self
91+ . message
92+ . clone ( )
93+ . unwrap_or_else ( || "Unexpected error" . into ( ) ) ;
94+ write ! ( f, "{message}" )
95+ }
96+ }
97+ impl Error for KdlDiagnostic { }
98+
99+ impl Diagnostic for KdlDiagnostic {
100+ fn source_code ( & self ) -> Option < & dyn miette:: SourceCode > {
101+ Some ( & self . input )
102+ }
103+
104+ fn severity ( & self ) -> Option < Severity > {
105+ Some ( self . severity )
106+ }
107+
108+ fn help < ' a > ( & ' a self ) -> Option < Box < dyn Display + ' a > > {
109+ self . help . as_ref ( ) . map ( |s| Box :: new ( s) as Box < dyn Display > )
110+ }
111+
112+ fn labels ( & self ) -> Option < Box < dyn Iterator < Item = miette:: LabeledSpan > + ' _ > > {
113+ let label = self . label . clone ( ) . unwrap_or_else ( || "here" . to_owned ( ) ) ;
114+ let labeled_span = LabeledSpan :: new_with_span ( Some ( label) , self . span ) ;
115+
116+ Some ( Box :: new ( iter:: once ( labeled_span) ) )
117+ }
76118}
77119
78120#[ cfg( feature = "v1" ) ]
@@ -87,8 +129,84 @@ impl From<kdlv1::KdlError> for KdlError {
87129 message: Some ( format!( "{}" , value. kind) ) ,
88130 label: value. label. map( |x| x. into( ) ) ,
89131 help: value. help. map( |x| x. into( ) ) ,
90- severity: miette :: Severity :: Error ,
132+ severity: Severity :: Error ,
91133 } ] ,
92134 }
93135 }
94136}
137+
138+ #[ cfg( test) ]
139+ mod tests {
140+ use super :: * ;
141+
142+ #[ test]
143+ fn kdl_error ( ) {
144+ let kdl_diagnostic = KdlDiagnostic {
145+ input : Default :: default ( ) ,
146+ span : SourceSpan :: new ( 0 . into ( ) , 0 ) ,
147+ message : Default :: default ( ) ,
148+ label : Default :: default ( ) ,
149+ help : Default :: default ( ) ,
150+ severity : Default :: default ( ) ,
151+ } ;
152+
153+ let kdl_error = KdlError {
154+ input : Arc :: new ( "bark? i guess?" . to_owned ( ) ) ,
155+ diagnostics : vec ! [ kdl_diagnostic. clone( ) , kdl_diagnostic] ,
156+ } ;
157+
158+ // Test `Error` impl
159+ assert_eq ! ( kdl_error. to_string( ) , "Failed to parse KDL document" ) ;
160+ assert ! ( kdl_error. source( ) . is_none( ) ) ;
161+
162+ // Test `Diagnostic` impl
163+ let related: Vec < _ > = kdl_error. related ( ) . unwrap ( ) . collect ( ) ;
164+ assert_eq ! ( related. len( ) , 2 ) ;
165+ assert_eq ! (
166+ kdl_error
167+ . source_code( )
168+ . unwrap( )
169+ . read_span( & SourceSpan :: new( 0 . into( ) , 5 ) , 0 , 0 )
170+ . unwrap( )
171+ . data( ) ,
172+ b"bark?"
173+ ) ;
174+ }
175+
176+ #[ test]
177+ fn kdl_diagnostic ( ) {
178+ let mut kdl_diagnostic = KdlDiagnostic {
179+ input : Arc :: new ( "Catastrophic failure!!!" . to_owned ( ) ) ,
180+ span : SourceSpan :: new ( 0 . into ( ) , 3 ) ,
181+ message : None ,
182+ label : Some ( "cute" . to_owned ( ) ) ,
183+ help : Some ( "try harder?" . to_owned ( ) ) ,
184+ severity : Severity :: Error ,
185+ } ;
186+
187+ // Test `Error` impl
188+ assert_eq ! ( kdl_diagnostic. to_string( ) , "Unexpected error" ) ;
189+ assert ! ( kdl_diagnostic. source( ) . is_none( ) ) ;
190+
191+ kdl_diagnostic. message = Some ( "mega bad news, kiddo" . to_owned ( ) ) ;
192+
193+ assert_eq ! ( kdl_diagnostic. to_string( ) , "mega bad news, kiddo" ) ;
194+ assert ! ( kdl_diagnostic. source( ) . is_none( ) ) ;
195+
196+ // Test `Diagnostic` impl
197+ let labels: Vec < _ > = kdl_diagnostic. labels ( ) . unwrap ( ) . collect ( ) ;
198+ assert_eq ! ( labels. len( ) , 1 ) ;
199+ assert_eq ! ( labels[ 0 ] . label( ) . unwrap( ) , "cute" ) ;
200+ assert_eq ! (
201+ kdl_diagnostic
202+ . source_code( )
203+ . unwrap( )
204+ . read_span( labels[ 0 ] . inner( ) , 0 , 0 )
205+ . unwrap( )
206+ . data( ) ,
207+ b"Cat"
208+ ) ;
209+ assert_eq ! ( kdl_diagnostic. help( ) . unwrap( ) . to_string( ) , "try harder?" ) ;
210+ assert_eq ! ( kdl_diagnostic. severity( ) . unwrap( ) , Severity :: Error ) ;
211+ }
212+ }
0 commit comments