@@ -207,9 +207,6 @@ pub enum TranslationResults {
207207 /// This file is unknown to and therefore not supported by the CodeChat
208208 /// Editor.
209209 Unknown ,
210- /// This is a CodeChat Editor file but it contains errors that prevent its
211- /// translation. The string contains the error message.
212- Err ( String ) ,
213210 /// A CodeChat Editor file; the struct contains the file's contents
214211 /// translated to CodeMirror.
215212 CodeChat ( CodeChatForWeb ) ,
@@ -224,9 +221,6 @@ pub enum TranslationResultsString {
224221 /// This file is unknown to the CodeChat Editor. It must be viewed raw or
225222 /// using the simple viewer.
226223 Unknown ,
227- /// This is a CodeChat Editor file but it contains errors that prevent its
228- /// translation. The string contains the error message.
229- Err ( String ) ,
230224 /// A CodeChat Editor file; the struct contains the file's contents
231225 /// translated to CodeMirror.
232226 CodeChat ( CodeChatForWeb ) ,
@@ -388,44 +382,62 @@ pub fn find_path_to_toc(file_path: &Path) -> Option<PathBuf> {
388382 }
389383}
390384
385+ #[ derive( Debug , thiserror:: Error ) ]
386+ pub enum CodechatForWebToSourceError {
387+ #[ error( "invalid lexer {0}" ) ]
388+ InvalidLexer ( String ) ,
389+ #[ error( "doc blocks not allowed in Markdown documents" ) ]
390+ DocBlocksNotAllowed ,
391+ #[ error( "TODO: diffs not supported" ) ]
392+ TodoDiff ,
393+ #[ error( "unable to convert from HTML to Markdown: {0}" ) ]
394+ HtmlToMarkdownFailed ( #[ from] HtmlToMarkdownWrappedError ) ,
395+ #[ error( "unable to translate CodeChat to source: {0}" ) ]
396+ CannotTranslateCodeChat ( #[ from] CodeDocBlockVecToSourceError ) ,
397+ }
398+
391399// Transform `CodeChatForWeb` to source code
392400// -----------------------------------------------------------------------------
393401/// This function takes in a source file in web-editable format (the
394402/// `CodeChatForWeb` struct) and transforms it into source code.
395403pub fn codechat_for_web_to_source (
396404 // The file to save plus metadata, stored in the `LexedSourceFile`
397405 codechat_for_web : & CodeChatForWeb ,
398- ) -> Result < String , String > {
406+ ) -> Result < String , CodechatForWebToSourceError > {
407+ let lexer_name = & codechat_for_web. metadata . mode ;
399408 // Given the mode, find the lexer.
400- let lexer: & std:: sync:: Arc < crate :: lexer:: LanguageLexerCompiled > = match LEXERS
401- . map_mode_to_lexer
402- . get ( & codechat_for_web. metadata . mode )
403- {
404- Some ( v) => v,
405- None => return Err ( "Invalid mode" . to_string ( ) ) ,
406- } ;
409+ let lexer: & std:: sync:: Arc < crate :: lexer:: LanguageLexerCompiled > =
410+ match LEXERS . map_mode_to_lexer . get ( lexer_name) {
411+ Some ( v) => v,
412+ None => {
413+ return Err ( CodechatForWebToSourceError :: InvalidLexer (
414+ lexer_name. clone ( ) ,
415+ ) ) ;
416+ }
417+ } ;
407418
408419 // Extract the plain (not diffed) CodeMirror contents.
409420 let CodeMirrorDiffable :: Plain ( ref code_mirror) = codechat_for_web. source else {
410- panic ! ( "No diff!" ) ;
421+ return Err ( CodechatForWebToSourceError :: TodoDiff ) ;
411422 } ;
412423
413424 // If this is a Markdown-only document, handle this special case.
414425 if * lexer. language_lexer . lexer_name == "markdown" {
415426 // There should be no doc blocks.
416427 if !code_mirror. doc_blocks . is_empty ( ) {
417- return Err ( "Doc blocks not allowed in Markdown documents." . to_string ( ) ) ;
428+ return Err ( CodechatForWebToSourceError :: DocBlocksNotAllowed ) ;
418429 }
419430 // Translate the HTML document to Markdown.
420431 let converter = HtmlToMarkdownWrapped :: new ( ) ;
421432 return converter
422433 . convert ( & code_mirror. doc )
423- . map_err ( |e| e . to_string ( ) ) ;
434+ . map_err ( CodechatForWebToSourceError :: HtmlToMarkdownFailed ) ;
424435 }
425436 let code_doc_block_vec_html = code_mirror_to_code_doc_blocks ( code_mirror) ;
426- let code_doc_block_vec =
427- doc_block_html_to_markdown ( code_doc_block_vec_html ) . map_err ( |e| e . to_string ( ) ) ?;
437+ let code_doc_block_vec = doc_block_html_to_markdown ( code_doc_block_vec_html )
438+ . map_err ( CodechatForWebToSourceError :: HtmlToMarkdownFailed ) ?;
428439 code_doc_block_vec_to_source ( & code_doc_block_vec, lexer)
440+ . map_err ( CodechatForWebToSourceError :: CannotTranslateCodeChat )
429441}
430442
431443/// Return the byte index of `s[u16_16_index]`, where the indexing operation is
@@ -504,6 +516,14 @@ struct HtmlToMarkdownWrapped {
504516 word_wrap_config : Configuration ,
505517}
506518
519+ #[ derive( Debug , thiserror:: Error ) ]
520+ pub enum HtmlToMarkdownWrappedError {
521+ #[ error( "unable to convert from HTML to markdown" ) ]
522+ HtmlToMarkdownFailed ( #[ from] std:: io:: Error ) ,
523+ #[ error( "unable to word wrap Markdown" ) ]
524+ WordWrapFailed ( #[ from] anyhow:: Error ) ,
525+ }
526+
507527impl HtmlToMarkdownWrapped {
508528 fn new ( ) -> Self {
509529 HtmlToMarkdownWrapped {
@@ -531,11 +551,10 @@ impl HtmlToMarkdownWrapped {
531551 self . word_wrap_config . line_width = line_width as u32 ;
532552 }
533553
534- fn convert ( & self , html : & str ) -> std :: io :: Result < String > {
554+ fn convert ( & self , html : & str ) -> Result < String , HtmlToMarkdownWrappedError > {
535555 let converted = self . html_to_markdown . convert ( html) ?;
536556 Ok (
537- format_text ( & converted, & self . word_wrap_config , |_, _, _| Ok ( None ) )
538- . map_err ( std:: io:: Error :: other) ?
557+ format_text ( & converted, & self . word_wrap_config , |_, _, _| Ok ( None ) ) ?
539558 // A return value of `None` means the text was unchanged or
540559 // ignored (by an
541560 // [ignoreFileDirective](https://dprint.dev/plugins/markdown/config/)).
@@ -548,7 +567,7 @@ impl HtmlToMarkdownWrapped {
548567// Transform HTML in doc blocks to Markdown.
549568fn doc_block_html_to_markdown (
550569 mut code_doc_block_vec : Vec < CodeDocBlock > ,
551- ) -> std :: io :: Result < Vec < CodeDocBlock > > {
570+ ) -> Result < Vec < CodeDocBlock > , HtmlToMarkdownWrappedError > {
552571 let mut converter = HtmlToMarkdownWrapped :: new ( ) ;
553572 for code_doc_block in & mut code_doc_block_vec {
554573 if let CodeDocBlock :: DocBlock ( doc_block) = code_doc_block {
@@ -566,20 +585,24 @@ fn doc_block_html_to_markdown(
566585 WORD_WRAP_COLUMN ,
567586 ) ,
568587 ) ) ;
569- doc_block. contents = converter
570- . convert ( & doc_block. contents )
571- . map_err ( std:: io:: Error :: other) ?;
588+ doc_block. contents = converter. convert ( & doc_block. contents ) ?;
572589 }
573590 }
574591
575592 Ok ( code_doc_block_vec)
576593}
577594
595+ #[ derive( Debug , PartialEq , thiserror:: Error ) ]
596+ pub enum CodeDocBlockVecToSourceError {
597+ #[ error( "unknown comment opening delimiter '{0}'" ) ]
598+ UnknownCommentOpeningDelimiter ( String ) ,
599+ }
600+
578601// Turn this vec of CodeDocBlocks into a string of source code.
579602fn code_doc_block_vec_to_source (
580603 code_doc_block_vec : & Vec < CodeDocBlock > ,
581604 lexer : & LanguageLexerCompiled ,
582- ) -> Result < String , String > {
605+ ) -> Result < String , CodeDocBlockVecToSourceError > {
583606 let mut file_contents = String :: new ( ) ;
584607 for code_doc_block in code_doc_block_vec {
585608 match code_doc_block {
@@ -636,10 +659,11 @@ fn code_doc_block_vec_to_source(
636659 {
637660 Some ( index) => & lexer. language_lexer . block_comment_delim_arr [ index] . closing ,
638661 None => {
639- return Err ( format ! (
640- "Unknown comment opening delimiter '{}'." ,
641- doc_block. delimiter
642- ) ) ;
662+ return Err (
663+ CodeDocBlockVecToSourceError :: UnknownCommentOpeningDelimiter (
664+ doc_block. delimiter . clone ( ) ,
665+ ) ,
666+ ) ;
643667 }
644668 } ;
645669
@@ -722,6 +746,12 @@ fn code_doc_block_vec_to_source(
722746 Ok ( file_contents)
723747}
724748
749+ #[ derive( Debug , PartialEq , thiserror:: Error ) ]
750+ pub enum SourceToCodeChatForWebError {
751+ #[ error( "unknown lexer {0}" ) ]
752+ UnknownLexer ( String ) ,
753+ }
754+
725755// Transform from source code to `CodeChatForWeb`
726756// -----------------------------------------------------------------------------
727757//
@@ -736,7 +766,7 @@ pub fn source_to_codechat_for_web(
736766 _is_toc : bool ,
737767 // True if this file is part of a project.
738768 _is_project : bool ,
739- ) -> TranslationResults {
769+ ) -> Result < TranslationResults , SourceToCodeChatForWebError > {
740770 // Determine the lexer to use for this file.
741771 let lexer_name;
742772 // First, search for a lexer directive in the file contents.
@@ -745,10 +775,7 @@ pub fn source_to_codechat_for_web(
745775 match LEXERS . map_mode_to_lexer . get ( & lexer_name) {
746776 Some ( v) => v,
747777 None => {
748- return TranslationResults :: Err ( format ! (
749- "<p>Unknown lexer type {}.</p>" ,
750- & lexer_name
751- ) ) ;
778+ return Err ( SourceToCodeChatForWebError :: UnknownLexer ( lexer_name) ) ;
752779 }
753780 }
754781 } else {
@@ -757,7 +784,7 @@ pub fn source_to_codechat_for_web(
757784 Some ( llc) => llc. first ( ) . unwrap ( ) ,
758785 _ => {
759786 // The file type is unknown; treat it as plain text.
760- return TranslationResults :: Unknown ;
787+ return Ok ( TranslationResults :: Unknown ) ;
761788 }
762789 }
763790 } ;
@@ -861,7 +888,7 @@ pub fn source_to_codechat_for_web(
861888 } ,
862889 } ;
863890
864- TranslationResults :: CodeChat ( codechat_for_web)
891+ Ok ( TranslationResults :: CodeChat ( codechat_for_web) )
865892}
866893
867894// Like `source_to_codechat_for_web`, translate a source file to the CodeChat
@@ -875,12 +902,15 @@ pub fn source_to_codechat_for_web_string(
875902 file_path : & Path ,
876903 // True if this file is a TOC.
877904 is_toc : bool ,
878- ) -> (
879- // The resulting translation.
880- TranslationResultsString ,
881- // Path to the TOC, if found; otherwise, None.
882- Option < PathBuf > ,
883- ) {
905+ ) -> Result <
906+ (
907+ // The resulting translation.
908+ TranslationResultsString ,
909+ // Path to the TOC, if found; otherwise, None.
910+ Option < PathBuf > ,
911+ ) ,
912+ SourceToCodeChatForWebError ,
913+ > {
884914 // Determine the file's extension, in order to look up a lexer.
885915 let ext = & file_path
886916 . extension ( )
@@ -893,26 +923,28 @@ pub fn source_to_codechat_for_web_string(
893923 let path_to_toc = find_path_to_toc ( file_path) ;
894924 let is_project = path_to_toc. is_some ( ) ;
895925
896- (
926+ Ok ( (
897927 match source_to_codechat_for_web ( file_contents, & ext. to_string ( ) , is_toc, is_project) {
898- TranslationResults :: CodeChat ( codechat_for_web) => {
899- if is_toc {
900- // For the table of contents sidebar, which is pure
901- // markdown, just return the resulting HTML, rather than the
902- // editable CodeChat for web format.
903- let CodeMirrorDiffable :: Plain ( plain) = codechat_for_web. source else {
904- panic ! ( "No diff!" ) ;
905- } ;
906- TranslationResultsString :: Toc ( plain. doc )
907- } else {
908- TranslationResultsString :: CodeChat ( codechat_for_web)
928+ Err ( err) => return Err ( err) ,
929+ Ok ( translation_results) => match translation_results {
930+ TranslationResults :: CodeChat ( codechat_for_web) => {
931+ if is_toc {
932+ // For the table of contents sidebar, which is pure
933+ // markdown, just return the resulting HTML, rather than the
934+ // editable CodeChat for web format.
935+ let CodeMirrorDiffable :: Plain ( plain) = codechat_for_web. source else {
936+ panic ! ( "No diff!" ) ;
937+ } ;
938+ TranslationResultsString :: Toc ( plain. doc )
939+ } else {
940+ TranslationResultsString :: CodeChat ( codechat_for_web)
941+ }
909942 }
910- }
911- TranslationResults :: Unknown => TranslationResultsString :: Unknown ,
912- TranslationResults :: Err ( err) => TranslationResultsString :: Err ( err) ,
943+ TranslationResults :: Unknown => TranslationResultsString :: Unknown ,
944+ } ,
913945 } ,
914946 path_to_toc,
915- )
947+ ) )
916948}
917949
918950/// Convert markdown to HTML. (This assumes the Markdown defined in the
0 commit comments