1 use rustc_error_messages::{ 2 fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}, 3 FluentArgs, FluentError, 4 }; 5 use std::borrow::Cow; 6 use std::error::Error; 7 use std::fmt; 8 9 #[derive(Debug)] 10 pub enum TranslateError<'args> { 11 One { 12 id: &'args Cow<'args, str>, 13 args: &'args FluentArgs<'args>, 14 kind: TranslateErrorKind<'args>, 15 }, 16 Two { 17 primary: Box<TranslateError<'args>>, 18 fallback: Box<TranslateError<'args>>, 19 }, 20 } 21 22 impl<'args> TranslateError<'args> { message(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self23 pub fn message(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { 24 Self::One { id, args, kind: TranslateErrorKind::MessageMissing } 25 } primary(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self26 pub fn primary(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { 27 Self::One { id, args, kind: TranslateErrorKind::PrimaryBundleMissing } 28 } attribute( id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>, attr: &'args str, ) -> Self29 pub fn attribute( 30 id: &'args Cow<'args, str>, 31 args: &'args FluentArgs<'args>, 32 attr: &'args str, 33 ) -> Self { 34 Self::One { id, args, kind: TranslateErrorKind::AttributeMissing { attr } } 35 } value(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self36 pub fn value(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { 37 Self::One { id, args, kind: TranslateErrorKind::ValueMissing } 38 } 39 fluent( id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>, errs: Vec<FluentError>, ) -> Self40 pub fn fluent( 41 id: &'args Cow<'args, str>, 42 args: &'args FluentArgs<'args>, 43 errs: Vec<FluentError>, 44 ) -> Self { 45 Self::One { id, args, kind: TranslateErrorKind::Fluent { errs } } 46 } 47 and(self, fallback: TranslateError<'args>) -> TranslateError<'args>48 pub fn and(self, fallback: TranslateError<'args>) -> TranslateError<'args> { 49 Self::Two { primary: Box::new(self), fallback: Box::new(fallback) } 50 } 51 } 52 53 #[derive(Debug)] 54 pub enum TranslateErrorKind<'args> { 55 MessageMissing, 56 PrimaryBundleMissing, 57 AttributeMissing { attr: &'args str }, 58 ValueMissing, 59 Fluent { errs: Vec<FluentError> }, 60 } 61 62 impl fmt::Display for TranslateError<'_> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 64 use TranslateErrorKind::*; 65 66 match self { 67 Self::One { id, args, kind } => { 68 writeln!(f, "failed while formatting fluent string `{id}`: ")?; 69 match kind { 70 MessageMissing => writeln!(f, "message was missing")?, 71 PrimaryBundleMissing => writeln!(f, "the primary bundle was missing")?, 72 AttributeMissing { attr } => { 73 writeln!(f, "the attribute `{attr}` was missing")?; 74 writeln!(f, "help: add `.{attr} = <message>`")?; 75 } 76 ValueMissing => writeln!(f, "the value was missing")?, 77 Fluent { errs } => { 78 for err in errs { 79 match err { 80 FluentError::ResolverError(ResolverError::Reference( 81 ReferenceKind::Message { id, .. } 82 | ReferenceKind::Variable { id, .. }, 83 )) => { 84 if args.iter().any(|(arg_id, _)| arg_id == id) { 85 writeln!( 86 f, 87 "argument `{id}` exists but was not referenced correctly" 88 )?; 89 writeln!(f, "help: try using `{{${id}}}` instead")?; 90 } else { 91 writeln!( 92 f, 93 "the fluent string has an argument `{id}` that was not found." 94 )?; 95 let vars: Vec<&str> = 96 args.iter().map(|(a, _v)| a).collect(); 97 match &*vars { 98 [] => writeln!(f, "help: no arguments are available")?, 99 [one] => writeln!( 100 f, 101 "help: the argument `{one}` is available" 102 )?, 103 [first, middle @ .., last] => { 104 write!(f, "help: the arguments `{first}`")?; 105 for a in middle { 106 write!(f, ", `{a}`")?; 107 } 108 writeln!(f, " and `{last}` are available")?; 109 } 110 } 111 } 112 } 113 _ => writeln!(f, "{err}")?, 114 } 115 } 116 } 117 } 118 } 119 // If someone cares about primary bundles, they'll probably notice it's missing 120 // regardless or will be using `debug_assertions` 121 // so we skip the arm below this one to avoid confusing the regular user. 122 Self::Two { primary: box Self::One { kind: PrimaryBundleMissing, .. }, fallback } => { 123 fmt::Display::fmt(fallback, f)?; 124 } 125 Self::Two { primary, fallback } => { 126 writeln!( 127 f, 128 "first, fluent formatting using the primary bundle failed:\n {primary}\n \ 129 while attempting to recover by using the fallback bundle instead, another error occurred:\n{fallback}" 130 )?; 131 } 132 } 133 Ok(()) 134 } 135 } 136 137 impl Error for TranslateError<'_> {} 138