@@ -80,8 +80,8 @@ pub type GenerateResult<T> = Result<T, GenerateError>;
8080pub enum GenerateError {
8181 #[ error( "Error with specified path -- {0}" ) ]
8282 GrammarPath ( String ) ,
83- #[ error( "{0}" ) ]
84- IO ( String ) ,
83+ #[ error( transparent ) ]
84+ IO ( IoError ) ,
8585 #[ cfg( feature = "load" ) ]
8686 #[ error( transparent) ]
8787 LoadGrammarFile ( #[ from] LoadGrammarError ) ,
@@ -100,9 +100,28 @@ pub enum GenerateError {
100100 SuperTypeCycle ( #[ from] SuperTypeCycleError ) ,
101101}
102102
103- impl From < std:: io:: Error > for GenerateError {
104- fn from ( value : std:: io:: Error ) -> Self {
105- Self :: IO ( value. to_string ( ) )
103+ #[ derive( Debug , Error , Serialize ) ]
104+ pub struct IoError {
105+ pub error : String ,
106+ pub path : Option < String > ,
107+ }
108+
109+ impl IoError {
110+ fn new ( error : & std:: io:: Error , path : Option < & Path > ) -> Self {
111+ Self {
112+ error : error. to_string ( ) ,
113+ path : path. map ( |p| p. to_string_lossy ( ) . to_string ( ) ) ,
114+ }
115+ }
116+ }
117+
118+ impl std:: fmt:: Display for IoError {
119+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
120+ write ! ( f, "{}" , self . error) ?;
121+ if let Some ( ref path) = self . path {
122+ write ! ( f, " ({path})" ) ?;
123+ }
124+ Ok ( ( ) )
106125 }
107126}
108127
@@ -117,27 +136,20 @@ pub enum LoadGrammarError {
117136 #[ error( "Failed to load grammar.js -- {0}" ) ]
118137 LoadJSGrammarFile ( #[ from] JSError ) ,
119138 #[ error( "Failed to load grammar.json -- {0}" ) ]
120- IO ( String ) ,
139+ IO ( IoError ) ,
121140 #[ error( "Unknown grammar file extension: {0:?}" ) ]
122141 FileExtension ( PathBuf ) ,
123142}
124143
125- #[ cfg( feature = "load" ) ]
126- impl From < std:: io:: Error > for LoadGrammarError {
127- fn from ( value : std:: io:: Error ) -> Self {
128- Self :: IO ( value. to_string ( ) )
129- }
130- }
131-
132144#[ cfg( feature = "load" ) ]
133145#[ derive( Debug , Error , Serialize ) ]
134146pub enum ParseVersionError {
135147 #[ error( "{0}" ) ]
136148 Version ( String ) ,
137149 #[ error( "{0}" ) ]
138150 JSON ( String ) ,
139- #[ error( "{0}" ) ]
140- IO ( String ) ,
151+ #[ error( transparent ) ]
152+ IO ( IoError ) ,
141153}
142154
143155#[ cfg( feature = "load" ) ]
@@ -152,8 +164,21 @@ pub enum JSError {
152164 JSRuntimeUtf8 { runtime : String , error : String } ,
153165 #[ error( "`{runtime}` process exited with status {code}" ) ]
154166 JSRuntimeExit { runtime : String , code : i32 } ,
155- #[ error( "{0}" ) ]
156- IO ( String ) ,
167+ #[ error( "Failed to open stdin for `{runtime}`" ) ]
168+ JSRuntimeStdin { runtime : String } ,
169+ #[ error( "Failed to write {item} to `{runtime}`'s stdin -- {error}" ) ]
170+ JSRuntimeWrite {
171+ runtime : String ,
172+ item : String ,
173+ error : String ,
174+ } ,
175+ #[ error( "Failed to read output from `{runtime}` -- {error}" ) ]
176+ JSRuntimeRead { runtime : String , error : String } ,
177+ #[ error( transparent) ]
178+ IO ( IoError ) ,
179+ #[ cfg( feature = "qjs-rt" ) ]
180+ #[ error( "Failed to get relative path" ) ]
181+ RelativePath ,
157182 #[ error( "Could not parse this package's version as semver -- {0}" ) ]
158183 Semver ( String ) ,
159184 #[ error( "Failed to serialze grammar JSON -- {0}" ) ]
@@ -163,13 +188,6 @@ pub enum JSError {
163188 QuickJS ( String ) ,
164189}
165190
166- #[ cfg( feature = "load" ) ]
167- impl From < std:: io:: Error > for JSError {
168- fn from ( value : std:: io:: Error ) -> Self {
169- Self :: IO ( value. to_string ( ) )
170- }
171- }
172-
173191#[ cfg( feature = "load" ) ]
174192impl From < serde_json:: Error > for JSError {
175193 fn from ( value : serde_json:: Error ) -> Self {
@@ -230,7 +248,8 @@ where
230248 . try_exists ( )
231249 . map_err ( |e| GenerateError :: GrammarPath ( e. to_string ( ) ) ) ?
232250 {
233- fs:: create_dir_all ( & path_buf) ?;
251+ fs:: create_dir_all ( & path_buf)
252+ . map_err ( |e| GenerateError :: IO ( IoError :: new ( & e, Some ( path_buf. as_path ( ) ) ) ) ) ?;
234253 repo_path = path_buf;
235254 repo_path. join ( "grammar.js" )
236255 } else {
@@ -247,15 +266,12 @@ where
247266 let header_path = src_path. join ( "tree_sitter" ) ;
248267
249268 // Ensure that the output directory exists
250- fs:: create_dir_all ( & src_path) ?;
269+ fs:: create_dir_all ( & src_path)
270+ . map_err ( |e| GenerateError :: IO ( IoError :: new ( & e, Some ( src_path. as_path ( ) ) ) ) ) ?;
251271
252272 if grammar_path. file_name ( ) . unwrap ( ) != "grammar.json" {
253- fs:: write ( src_path. join ( "grammar.json" ) , & grammar_json) . map_err ( |e| {
254- GenerateError :: IO ( format ! (
255- "Failed to write grammar.json to {} -- {e}" ,
256- src_path. display( )
257- ) )
258- } ) ?;
273+ fs:: write ( src_path. join ( "grammar.json" ) , & grammar_json)
274+ . map_err ( |e| GenerateError :: IO ( IoError :: new ( & e, Some ( src_path. as_path ( ) ) ) ) ) ?;
259275 }
260276
261277 // If our job is only to generate `grammar.json` and not `parser.c`, stop here.
@@ -297,7 +313,8 @@ where
297313
298314 write_file ( & src_path. join ( "parser.c" ) , c_code) ?;
299315 write_file ( & src_path. join ( "node-types.json" ) , node_types_json) ?;
300- fs:: create_dir_all ( & header_path) ?;
316+ fs:: create_dir_all ( & header_path)
317+ . map_err ( |e| GenerateError :: IO ( IoError :: new ( & e, Some ( header_path. as_path ( ) ) ) ) ) ?;
301318 write_file ( & header_path. join ( "alloc.h" ) , ALLOC_HEADER ) ?;
302319 write_file ( & header_path. join ( "array.h" ) , ARRAY_HEADER ) ?;
303320 write_file ( & header_path. join ( "parser.h" ) , PARSER_HEADER ) ?;
@@ -413,9 +430,8 @@ fn read_grammar_version(repo_path: &Path) -> Result<Option<Version>, ParseVersio
413430 let json = path
414431 . exists ( )
415432 . then ( || {
416- let contents = fs:: read_to_string ( path. as_path ( ) ) . map_err ( |e| {
417- ParseVersionError :: IO ( format ! ( "Failed to read `{}` -- {e}" , path. display( ) ) )
418- } ) ?;
433+ let contents = fs:: read_to_string ( path. as_path ( ) )
434+ . map_err ( |e| ParseVersionError :: IO ( IoError :: new ( & e, Some ( path. as_path ( ) ) ) ) ) ?;
419435 serde_json:: from_str :: < TreeSitterJson > ( & contents) . map_err ( |e| {
420436 ParseVersionError :: JSON ( format ! ( "Failed to parse `{}` -- {e}" , path. display( ) ) )
421437 } )
@@ -449,14 +465,16 @@ pub fn load_grammar_file(
449465 }
450466 match grammar_path. extension ( ) . and_then ( |e| e. to_str ( ) ) {
451467 Some ( "js" ) => Ok ( load_js_grammar_file ( grammar_path, js_runtime) ?) ,
452- Some ( "json" ) => Ok ( fs:: read_to_string ( grammar_path) ?) ,
468+ Some ( "json" ) => Ok ( fs:: read_to_string ( grammar_path)
469+ . map_err ( |e| LoadGrammarError :: IO ( IoError :: new ( & e, Some ( grammar_path) ) ) ) ?) ,
453470 _ => Err ( LoadGrammarError :: FileExtension ( grammar_path. to_owned ( ) ) ) ?,
454471 }
455472}
456473
457474#[ cfg( feature = "load" ) ]
458475fn load_js_grammar_file ( grammar_path : & Path , js_runtime : Option < & str > ) -> JSResult < String > {
459- let grammar_path = dunce:: canonicalize ( grammar_path) ?;
476+ let grammar_path = dunce:: canonicalize ( grammar_path)
477+ . map_err ( |e| JSError :: IO ( IoError :: new ( & e, Some ( grammar_path) ) ) ) ?;
460478
461479 #[ cfg( feature = "qjs-rt" ) ]
462480 if js_runtime == Some ( "native" ) {
@@ -497,7 +515,9 @@ fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResu
497515 let mut js_stdin = js_process
498516 . stdin
499517 . take ( )
500- . ok_or_else ( || JSError :: IO ( format ! ( "Failed to open stdin for `{js_runtime}`" ) ) ) ?;
518+ . ok_or_else ( || JSError :: JSRuntimeStdin {
519+ runtime : js_runtime. to_string ( ) ,
520+ } ) ?;
501521
502522 let cli_version = Version :: parse ( env ! ( "CARGO_PKG_VERSION" ) ) ?;
503523 write ! (
@@ -507,21 +527,26 @@ fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResu
507527 globalThis.TREE_SITTER_CLI_VERSION_PATCH = {};" ,
508528 cli_version. major, cli_version. minor, cli_version. patch,
509529 )
510- . map_err ( |e| {
511- JSError :: IO ( format ! (
512- "Failed to write tree-sitter version to `{js_runtime}`'s stdin -- {e}"
513- ) )
514- } ) ?;
515- js_stdin. write ( include_bytes ! ( "./dsl.js" ) ) . map_err ( |e| {
516- JSError :: IO ( format ! (
517- "Failed to write grammar dsl to `{js_runtime}`'s stdin -- {e}"
518- ) )
530+ . map_err ( |e| JSError :: JSRuntimeWrite {
531+ runtime : js_runtime. to_string ( ) ,
532+ item : "tree-sitter version" . to_string ( ) ,
533+ error : e. to_string ( ) ,
519534 } ) ?;
535+ js_stdin
536+ . write ( include_bytes ! ( "./dsl.js" ) )
537+ . map_err ( |e| JSError :: JSRuntimeWrite {
538+ runtime : js_runtime. to_string ( ) ,
539+ item : "grammar dsl" . to_string ( ) ,
540+ error : e. to_string ( ) ,
541+ } ) ?;
520542 drop ( js_stdin) ;
521543
522544 let output = js_process
523545 . wait_with_output ( )
524- . map_err ( |e| JSError :: IO ( format ! ( "Failed to read output from `{js_runtime}` -- {e}" ) ) ) ?;
546+ . map_err ( |e| JSError :: JSRuntimeRead {
547+ runtime : js_runtime. to_string ( ) ,
548+ error : e. to_string ( ) ,
549+ } ) ?;
525550 match output. status . code ( ) {
526551 Some ( 0 ) => {
527552 let stdout = String :: from_utf8 ( output. stdout ) . map_err ( |e| JSError :: JSRuntimeUtf8 {
@@ -537,9 +562,15 @@ fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResu
537562 grammar_json = & stdout[ pos + 1 ..] ;
538563
539564 let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
540- stdout. write_all ( node_output. as_bytes ( ) ) ?;
541- stdout. write_all ( b"\n " ) ?;
542- stdout. flush ( ) ?;
565+ stdout
566+ . write_all ( node_output. as_bytes ( ) )
567+ . map_err ( |e| JSError :: IO ( IoError :: new ( & e, None ) ) ) ?;
568+ stdout
569+ . write_all ( b"\n " )
570+ . map_err ( |e| JSError :: IO ( IoError :: new ( & e, None ) ) ) ?;
571+ stdout
572+ . flush ( )
573+ . map_err ( |e| JSError :: IO ( IoError :: new ( & e, None ) ) ) ?;
543574 }
544575
545576 Ok ( serde_json:: to_string_pretty ( & serde_json:: from_str :: <
@@ -559,8 +590,7 @@ fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResu
559590
560591#[ cfg( feature = "load" ) ]
561592pub fn write_file ( path : & Path , body : impl AsRef < [ u8 ] > ) -> GenerateResult < ( ) > {
562- fs:: write ( path, body)
563- . map_err ( |e| GenerateError :: IO ( format ! ( "Failed to write {:?} -- {e}" , path. file_name( ) ) ) )
593+ fs:: write ( path, body) . map_err ( |e| GenerateError :: IO ( IoError :: new ( & e, Some ( path) ) ) )
564594}
565595
566596#[ cfg( test) ]
0 commit comments