use super::{
MacroError, ParseError, UnsupportedError, attribute,
fluent::{self, GroupError},
};
use proc_macro_error2::{emit_call_site_error, emit_error};
use proc_macro2::TokenStream;
use syn::spanned::Spanned;
fn attribute(error: attribute::Error, attribute_stream: &TokenStream) {
match error {
attribute::Error::Build(build_error) => {
for located_error in build_error.locations() {
let error_source = attribute_stream.span();
emit_error! { error_source, "invalid glob";
note = located_error.to_string();
};
}
}
attribute::Error::Walk(walk_error) => {
let help = if let Some(path) = walk_error.path() {
let path_name = path.to_str().unwrap();
match path.try_exists() {
Ok(true) => {
format!("the path `{path_name}` exists, but unable to access it")
}
_ => format!("the path `{path_name}` doesn't seem to exist"),
}
} else {
String::from("no associated path")
};
emit_error! { attribute_stream, "error at depth {} while walking path", walk_error.depth();
help = help;
};
}
attribute::Error::NoMatchesInEntry {
path,
complete_match,
} => {
assert!(path.exists());
assert_eq!(path.to_string(), complete_match);
emit_error! { attribute_stream, "cannot match against an exact path";
help = "The attribute should use glob syntax to match against multiple files";
note = "For example, you can:\n{}\n{}",
"- Match against directories: locale/**/errors.ftl",
"- Match against files: locale/*.ftl";
};
}
attribute::Error::NoMatchesInGlob { base_path } => {
emit_error! { attribute_stream, "no matches found for glob";
help = "Failed to match in path: {}", base_path.to_string();
note = "For example, you can:\n{}\n{}",
" - Match against directories: locale/**/errors.ftl",
" - Match against files: locale/*.ftl";
};
}
attribute::Error::InvalidLocale {
path,
locale,
source,
} => {
emit_error! { attribute_stream, "invalid locale in file path";
help = "Expected `{}` to be a valid locale identifier while reading `{}`", locale, path;
note = source;
}
}
}
}
fn fluent(error: fluent::Error) {
eprintln!("{:?}", miette::Error::new(error));
emit_call_site_error!("invalid Fluent source code, see above for details");
}
fn group(error: GroupError, attribute_stream: &TokenStream) {
match error {
GroupError::Fluent(error) => fluent(error),
GroupError::InvalidMessage {
fluent_name,
rust_name,
span,
} => {
emit_error! { span.unwrap(), "no matching Fluent message in canonical locale";
help = "The field `{}` implies the Fluent message `{}`", rust_name, fluent_name
};
}
GroupError::MissingCanonicalLocale {
canonical_locale,
matched_locales,
} => emit_error! { attribute_stream, "missing canonical locale `{}`", canonical_locale;
help = "{}", matched_locales;
},
}
}
fn unsupported(error: UnsupportedError) {
match error {
UnsupportedError::Union { span } => emit_error! { span, "unions are not supported";
help = "Use a `struct` or `enum` instead"
},
UnsupportedError::UnnamedFields { span, field_count } => {
emit_error! { span, "only 1 unnamed field is supported, got {} fields", field_count;
help = "When there are multiple fields, each field needs a name so it can be referenced by Fluent code";
note = "Using a single unnamed field forwards to that field's implementation of `Localize`";
}
}
}
}
fn parse(error: ParseError, attribute_stream: &TokenStream, derive_input_stream: &TokenStream) {
match error {
ParseError::InvalidAttribute(invalid_attribute) => {
emit_error! { attribute_stream, invalid_attribute;
help = "Expected a path glob, for example {}",
r#"#[localize("i18n/**/strings.ftl")]"#;
}
}
ParseError::InvalidDeriveInput(invalid_derive_input) => {
emit_error! { derive_input_stream, invalid_derive_input;
help = "This macro can only be used on structs or enums";
}
}
}
}
pub fn emit(error: MacroError, attribute_stream: &TokenStream, derive_input_stream: &TokenStream) {
match error {
MacroError::Attribute(error) => attribute(error, attribute_stream),
MacroError::Group(error) => group(error, attribute_stream),
MacroError::Unsupported(error) => unsupported(error),
MacroError::ParseError(error) => parse(error, attribute_stream, derive_input_stream),
}
}