rustc_errors/
error.rs

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