Emit compilation errors from Fluent source code
Dependencies
- [2]
JZXXFWQKAdd tests for `locale_select` backends on unix - [3]
RLX6XPNZReturn an error when user provides an exact path - [4]
BQ6N55O7Refactor how `Group` stores messages - [5]
4MRF5E76Generate simple locale matching code in `localize()` - [6]
HJMYJDC7Simplify `fluent_embed::group` module - [7]
5TEX4MNUSplit `fluent_embed` into `group` and `parse` modules - [8]
2XQ6ZB4WStore multiple locales in a single `Group` - [9]
SHNZZSZGCreate `cli_macros` shim crate - [10]
XEEXWJLGAdd simple end-to-end test for selectors - [11]
D652S2N3Rename `parse` module to `parse_fluent` - [12]
XGNME3WRMove `Group::derive_enum` to new `crate::parse_macro` module - [13]
VNSHGQYNSupport using glob paths in `localize` macro - [14]
UOMQT7LTAdd support for cardinal CLDR plural selectors - [15]
ROSR4HD5Parse captured glob as locale - [16]
5FIVUZYFUnify `fluent_embed` macro API as `localize()` - [17]
V5S5K33AAdd basic error handling for invalid paths in proc_macro attribute - [18]
HCGVXOF7Add language negotiation using `fluent-langneg` - [19]
QSK7JRBAAdd simple `attribute_path` function - [20]
P6FW2GGORemove unnecessary parameters in generated `localize()` function - [21]
3WEPY3OXAdd `locale` parameter to derived `localize()` function - [22]
VYPJUPPKHandle `GlobError::Build` in proc_macro error reporting - [23]
O77KA6C4Create `fluent_embed` crate - [24]
OCR4YRQ2Parse group from fluent file specified by macro attribute - [25]
NO3PDO7PRefactor `fluent_embed` to support structs - [*]
UKFEFT6LCreate basic `Output` proc-macro - [*]
WBI5HFOBAdd simple wrapper for `libc::settext()` to query system locale - [*]
56F2YE6HUse `prettyplease` to format macro output - [*]
VZYZRAO4Move `output-macros` crate into workspace
Change contents
- replacement in fluent_embed/src/parse_macro.rs at line 1
use crate::{group::Group, parse_fluent::ReferenceKind};use crate::group::Group;use crate::parse_fluent::{FluentError, ReferenceKind, References};use std::collections::HashSet; - replacement in fluent_embed/src/parse_macro.rs at line 10
fn expr_for_message(group: &Group, id: &str, reference_kind: ReferenceKind) -> TokenStream {fn expr_for_message(group: &Group,id: &str,reference_kind: &References,) -> Result<TokenStream, FluentError> { - replacement in fluent_embed/src/parse_macro.rs at line 16
let canonical_message = group.canonical_message(id, reference_kind);let canonical_message = group.canonical_message(id, reference_kind)?; - replacement in fluent_embed/src/parse_macro.rs at line 18
let (additional_locales, additional_messages): (Vec<_>, Vec<_>) =group.additional_messages(id, reference_kind).unzip();let (additional_locales, additional_messages): (Vec<_>, Vec<_>) = group.additional_messages(id, reference_kind)?.into_iter().unzip(); - replacement in fluent_embed/src/parse_macro.rs at line 28
quote! {Ok(quote! { - replacement in fluent_embed/src/parse_macro.rs at line 40
}})}/// Create a list of unique field names that can be referencedfn unique_named_fields(named_fields: &syn::FieldsNamed) -> HashSet<String> {named_fields.named.iter()// Get the `syn::Ident` for each field.map(|field| {field.ident.as_ref().expect("Named fields should have an associated ident")}).map(|ident| ident.to_string()).collect::<HashSet<String>>() - replacement in fluent_embed/src/parse_macro.rs at line 59
// TODO: check that the fields in fluent source reference fields that existpub fn derive_struct(group: Group, ident: &syn::Ident) -> TokenStream {pub fn derive_struct(group: Group,ident: &syn::Ident,fields: &syn::Fields,) -> Result<TokenStream, FluentError> {// Turn the struct fields into a list of valid referenceslet references = match fields {syn::Fields::Named(named_fields) => References {// Reference using `self.{reference_name}`kind: ReferenceKind::StructField,// Create a list of unique field names that can be referencedcandidates: unique_named_fields(named_fields),},syn::Fields::Unnamed(_) => todo!(),syn::Fields::Unit => todo!(),}; - replacement in fluent_embed/src/parse_macro.rs at line 77
expr_for_message(&group, &ident_kebab_case, ReferenceKind::StructField)expr_for_message(&group, &ident_kebab_case, &references) - replacement in fluent_embed/src/parse_macro.rs at line 83
) -> TokenStream {) -> Result<TokenStream, FluentError> { - replacement in fluent_embed/src/parse_macro.rs at line 105
let arm_body = expr_for_message(&group, &variant_kebab_case, ReferenceKind::EnumField);let references = match &enum_variant.fields {syn::Fields::Named(named_fields) => References {kind: ReferenceKind::EnumField,candidates: unique_named_fields(named_fields),},syn::Fields::Unnamed(_) => todo!(),syn::Fields::Unit => todo!(),};let arm_body = expr_for_message(&group, &variant_kebab_case, &references)?; - replacement in fluent_embed/src/parse_macro.rs at line 118
quote! {Ok(quote! { - replacement in fluent_embed/src/parse_macro.rs at line 122
}}) - edit in fluent_embed/src/parse_fluent.rs at line 1
use std::collections::HashSet; - replacement in fluent_embed/src/parse_fluent.rs at line 4
Expression, InlineExpression, Message, Pattern, PatternElement, Variant, VariantKey,Entry, Expression, InlineExpression, Message, Pattern, PatternElement, Resource, Variant,VariantKey, - replacement in fluent_embed/src/parse_fluent.rs at line 7
use heck::{ToPascalCase, ToSnakeCase};use heck::{ToLowerCamelCase, ToPascalCase, ToSnakeCase};use miette::{Diagnostic, NamedSource, SourceSpan}; - edit in fluent_embed/src/parse_fluent.rs at line 11
use thiserror::Error;#[derive(Diagnostic, Debug, Error)]#[error("Field doesn't exist")]pub struct InvalidReference {#[source_code]src: NamedSource<String>,#[label("Can't find any Rust fields with this name")]span: SourceSpan,#[help]help: String,} - edit in fluent_embed/src/parse_fluent.rs at line 24
#[derive(Diagnostic, Debug, Error)]#[diagnostic(transparent)]#[error(transparent)]pub enum FluentError {InvalidReference(#[from] InvalidReference),} - replacement in fluent_embed/src/parse_fluent.rs at line 37
pub(crate) fn pattern(pattern: &Pattern<String>, reference_kind: ReferenceKind) -> syn::Expr {#[derive(Clone, Debug)]pub struct References {pub kind: ReferenceKind,pub candidates: HashSet<String>,}pub(crate) fn pattern(message: &Message<String>,pattern: &Pattern<String>,references: &References,path: &str,) -> Result<syn::Expr, FluentError> { - replacement in fluent_embed/src/parse_fluent.rs at line 56
let expression = placeable_expression(&expression, reference_kind);let expression = match expression {Expression::Select { selector, variants } => {let target = inline_expression(message, selector, references, path)?;let arms: Vec<syn::Arm> = variants.iter().map(|item| variant(message, item, references, path)).collect::<Result<_, FluentError>>()?;parse_quote! {match plural_rules.category_for(#target) {#(#arms),*}}}Expression::Inline(expression) => {inline_expression(message, expression, references, path)?}}; - edit in fluent_embed/src/parse_fluent.rs at line 82
parse_quote!(format!(#format_body_literal, #(#format_arguments),*))} - replacement in fluent_embed/src/parse_fluent.rs at line 83[4.1019]→[4.3209:3312](∅→∅),[4.291]→[4.1133:1211](∅→∅),[4.356]→[4.1133:1211](∅→∅),[4.3312]→[4.1133:1211](∅→∅),[4.1133]→[4.1133:1211](∅→∅),[4.1211]→[4.3313:3490](∅→∅),[4.424]→[4.1367:1395](∅→∅),[4.3490]→[4.1367:1395](∅→∅),[4.1367]→[4.1367:1395](∅→∅),[4.1395]→[4.3491:3550](∅→∅),[4.3550]→[4.485:516](∅→∅),[4.485]→[4.485:516](∅→∅),[4.516]→[4.1498:1541](∅→∅),[4.1498]→[4.1498:1541](∅→∅),[4.1541]→[4.3551:3640](∅→∅),[4.590]→[4.1620:1626](∅→∅),[4.3640]→[4.1620:1626](∅→∅),[4.1620]→[4.1620:1626](∅→∅)
fn placeable_expression(expression: &Expression<String>, reference_kind: ReferenceKind) -> syn::Expr {match expression {Expression::Select { selector, variants } => {let target = inline_expression(selector, reference_kind);let arms: Vec<syn::Arm> = variants.iter().map(|item| variant(item, reference_kind)).collect();parse_quote! {match plural_rules.category_for(#target) {#(#arms),*}}}Expression::Inline(expression) => inline_expression(expression, reference_kind),}Ok(parse_quote!(format!(#format_body_literal, #(#format_arguments),*))) - replacement in fluent_embed/src/parse_fluent.rs at line 88[4.1629]→[4.3641:3747](∅→∅),[4.432]→[4.1746:1769](∅→∅),[4.3747]→[4.1746:1769](∅→∅),[4.1746]→[4.1746:1769](∅→∅)
fn inline_expression(expression: &InlineExpression<String>, reference_kind: ReferenceKind) -> syn::Expr {match expression {fn inline_expression(message: &Message<String>,expression: &InlineExpression<String>,references: &References,path: &str,) -> Result<syn::Expr, FluentError> {Ok(match expression { - replacement in fluent_embed/src/parse_fluent.rs at line 107
let ident = format_ident!("{}", id.name.to_snake_case());match reference_kind {// Make sure the referenced variable is in the set of valid variables// let ident = format_ident!("{}", id.name.to_snake_case());let ident = if let Some(variable) = references.candidates.get(&id.name.to_snake_case()){format_ident!("{variable}")} else {// Create a fake `fluent_syntax::ast::Resource` to serialize into a Stringlet error_resource = Resource {body: vec![Entry::Message(message.to_owned())],};let source_string = fluent_syntax::serializer::serialize_with_options(&error_resource,fluent_syntax::serializer::Options {// Make sure to include all source code in error snippet, even if marked as "junk"with_junk: true,},);let location = source_string.find(&format!("${}", id.name)).unwrap();return Err(FluentError::InvalidReference(InvalidReference {src: NamedSource::new(path, source_string.clone()),span: (location..location + id.name.len()).into(),help: format!("the following references are valid:\n{}",references.candidates.iter().map(|field| format!("- ${}", field.to_lower_camel_case())).collect::<Vec<String>>().join("\n")),}));};match references.kind { - edit in fluent_embed/src/parse_fluent.rs at line 146
- replacement in fluent_embed/src/parse_fluent.rs at line 151
}}) - replacement in fluent_embed/src/parse_fluent.rs at line 154
fn variant(variant: &Variant<String>, reference_kind: ReferenceKind) -> syn::Arm {fn variant(message: &Message<String>,variant: &Variant<String>,references: &References,path: &str,) -> Result<syn::Arm, FluentError> { - replacement in fluent_embed/src/parse_fluent.rs at line 161
let body = pattern(&variant.value, reference_kind);let body = pattern(message, &variant.value, references, path)?; - replacement in fluent_embed/src/parse_fluent.rs at line 172
parse_quote!(#pattern => #body)Ok(parse_quote!(#pattern => #body)) - replacement in fluent_embed/src/parse_fluent.rs at line 181
VariantKey::NumberLiteral { value } => todo!(),VariantKey::NumberLiteral { .. } => todo!(), - replacement in fluent_embed/src/parse_fluent.rs at line 185
pub(crate) fn message(message: &Message<String>, reference_kind: ReferenceKind) -> syn::Expr {pub(crate) fn message(message: &Message<String>,references: &References,path: &str,) -> Result<syn::Expr, FluentError> { - replacement in fluent_embed/src/parse_fluent.rs at line 191
pattern(value, reference_kind)pattern(message, value, references, path) - replacement in fluent_embed/src/parse_fluent.rs at line 193
parse_quote!(())Ok(parse_quote!(())) - edit in fluent_embed/src/lib.rs at line 6
use miette::Diagnostic; - edit in fluent_embed/src/lib.rs at line 10
use thiserror::Error; - replacement in fluent_embed/src/lib.rs at line 13
mod parse_fluent;pub mod parse_fluent; - replacement in fluent_embed/src/lib.rs at line 16
#[derive(thiserror::Error, Debug)]pub use parse_fluent::FluentError;#[derive(Diagnostic, Debug, Error)]#[error(transparent)] - edit in fluent_embed/src/lib.rs at line 21
#[error("{0}")] - edit in fluent_embed/src/lib.rs at line 22
#[error("{0}")] - replacement in fluent_embed/src/lib.rs at line 23
#[error(":(")]#[error("Directory could not be matched by glob in macro attribute")] - edit in fluent_embed/src/lib.rs at line 28
}#[derive(Diagnostic, Debug, Error)]#[diagnostic(transparent)]#[error(transparent)]pub enum MacroError {Attribute(#[from] AttributeError),#[error("Error in Fluent source code")]Fluent(#[from] FluentError), - edit in fluent_embed/src/lib.rs at line 45
let mut paths = HashMap::new(); - edit in fluent_embed/src/lib.rs at line 51
- replacement in fluent_embed/src/lib.rs at line 68
resources.insert(locale, resource);resources.insert(locale.clone(), resource);paths.insert(locale, entry.to_candidate_path().to_string()); - replacement in fluent_embed/src/lib.rs at line 72
Ok(Group::new(locale!("en-US"), resources))Ok(Group::new(locale!("en-US"), resources, paths)) - replacement in fluent_embed/src/lib.rs at line 75
pub fn localize(path: &syn::LitStr,derive_input: &DeriveInput,) -> Result<TokenStream, AttributeError> {pub fn localize(path: &syn::LitStr, derive_input: &DeriveInput) -> Result<TokenStream, MacroError> { - replacement in fluent_embed/src/lib.rs at line 79
syn::Data::Struct(_struct_data) => parse_macro::derive_struct(group, &derive_input.ident),syn::Data::Struct(struct_data) => {parse_macro::derive_struct(group, &derive_input.ident, &struct_data.fields)} - replacement in fluent_embed/src/lib.rs at line 84
};}?; - replacement in fluent_embed/src/group.rs at line 1
use crate::parse_fluent::{self, ReferenceKind};use crate::parse_fluent::{self, FluentError, References}; - edit in fluent_embed/src/group.rs at line 42
paths: HashMap<Locale, String>, - replacement in fluent_embed/src/group.rs at line 46
pub fn new(canonical_locale: Locale, mut resources: HashMap<Locale, Resource<String>>) -> Self {pub fn new(canonical_locale: Locale,mut resources: HashMap<Locale, Resource<String>>,paths: HashMap<Locale, String>,) -> Self { - edit in fluent_embed/src/group.rs at line 70
paths, - edit in fluent_embed/src/group.rs at line 72[4.553]→[4.6608:6614](∅→∅),[4.1896]→[4.6608:6614](∅→∅),[4.6608]→[4.6608:6614](∅→∅),[4.6614]→[4.1897:1898](∅→∅),[4.1898]→[4.6168:6373](∅→∅)
}fn message_column(&self, id: &str) -> usize {self.canonical_messages.iter().position(|message| message.id.name == id).expect("Message id must be valid") - replacement in fluent_embed/src/group.rs at line 78
pub fn canonical_message(&self, id: &str, reference_kind: ReferenceKind) -> syn::Expr {let message_column = self.message_column(id);let message = &self.canonical_messages[message_column];parse_fluent::message(message, reference_kind)pub fn canonical_message(&self,id: &str,references: &References,) -> Result<syn::Expr, FluentError> {let message = self.canonical_messages.iter().find(|message| message.id.name == id).expect("Message id must be valid");let path = self.paths.get(&self.canonical_locale).unwrap();parse_fluent::message(message, references, path) - replacement in fluent_embed/src/group.rs at line 96
reference_kind: ReferenceKind,) -> impl Iterator<Item = (&Locale, syn::Expr)> {let message_column = self.message_column(id);references: &References,) -> Result<Vec<(&Locale, syn::Expr)>, FluentError> {let mut messages = Vec::with_capacity(self.extra_locales.len());let message_column = self.canonical_messages.iter().position(|message| message.id.name == id).expect("Message id must be valid"); - replacement in fluent_embed/src/group.rs at line 105[4.6937]→[4.1458:1665](∅→∅),[4.1458]→[4.1458:1665](∅→∅),[4.1665]→[4.6938:7170](∅→∅),[4.7170]→[4.1729:1740](∅→∅),[4.1729]→[4.1729:1740](∅→∅)
self.extra_locales.iter().filter_map(move |locale_group| {locale_group.messages.get(message_column).unwrap().as_ref().map(|message: &Message<String>| {(&locale_group.locale,parse_fluent::message(message, reference_kind),)})})for locale_group in &self.extra_locales {// Include the message only if it exists in this localeif let Some(message) = &locale_group.messages[message_column] {let path = self.paths.get(&locale_group.locale).unwrap();let message_expr = parse_fluent::message(&message, references, path)?;messages.push((&locale_group.locale, message_expr))}}Ok(messages) - replacement in fluent_embed/Cargo.toml at line 10
fluent-syntax = "0.11.0"fluent-syntax = "0.11.1" - edit in fluent_embed/Cargo.toml at line 13
miette = "7.2.0" - edit in cli_macros/src/lib.rs at line 2
use fluent_embed::{AttributeError, FluentError, MacroError};use proc_macro::TokenStream;use proc_macro_error::{abort, emit_call_site_error, emit_error, proc_macro_error};use quote::{quote, ToTokens};use syn::parse_macro_input;fn attribute_error(error: AttributeError, derive_attribute: &syn::LitStr) {match error {AttributeError::Build(build_error) => {for location in build_error.locations() {// Create a token stream from the attribute's string literallet [proc_macro2::TokenTree::Literal(ref string_literal)] = derive_attribute.to_token_stream().into_iter().collect::<Vec<_>>()[..]else {abort!(derive_attribute, "unexpected macro attribute");};let (span_start, span_length) = location.span();let error_source = string_literal// Offset by 1 to skip the starting `"` double-quote character.subspan(span_start + 1..=span_start + span_length)// Fall back to the whole attribute if `subspan()` returns `None`// This will always happend on stable as subspan is nightly-only:// https://docs.rs/proc-macro2/latest/proc_macro2/struct.Literal.html#method.subspan.unwrap_or(derive_attribute.span());emit_error! { error_source, "invalid glob";note = location.to_string();};}}AttributeError::Walk(walk_error) => {// Generate help textlet help = if let Some(path) = walk_error.path() {let path_name = path.to_str().unwrap();// Might hit an error if file exists but insufficient permissionsmatch 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! { derive_attribute, "error at depth {} while walking path", walk_error.depth();help = help;};}AttributeError::NoMatches {path,complete_match,} => {// Validate the assumption that the user has provided an exact pathassert!(path.exists());assert_eq!(path.to_string_lossy(), complete_match);emit_error! { derive_attribute, "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";};}}} - replacement in cli_macros/src/lib.rs at line 72
use fluent_embed::AttributeError;use proc_macro::TokenStream;fn fluent_error(error: FluentError) {// It doesn't seem like you can reference non-Rust source files// in `proc_macro::Span`, so use Miette to pretty-print our own reports.// This includes setting up a global report handler with some extra optionsmiette::set_hook(Box::new(|_| {Box::new(miette::MietteHandlerOpts::new()// Force color output, even when printing using the debug formatter.color(true).build(),)})).unwrap();match error {FluentError::InvalidReference(invalid_reference) => {eprintln!("{:?}", miette::Error::new(invalid_reference));}}// Make sure compilation failsemit_call_site_error!("invalid Fluent source code, see above for details");} - replacement in cli_macros/src/lib.rs at line 104
Err(attribute_error) => {Err(macro_error) => { - replacement in cli_macros/src/lib.rs at line 106
match attribute_error {AttributeError::Build(build_error) => {for location in build_error.locations() {// Create a token stream from the attribute's string literallet [proc_macro2::TokenTree::Literal(ref string_literal)] =derive_attribute.to_token_stream().into_iter().collect::<Vec<_>>()[..]else {abort!(derive_attribute, "unexpected macro attribute");};match macro_error {MacroError::Attribute(error) => attribute_error(error, &derive_attribute),MacroError::Fluent(error) => fluent_error(error),} - edit in cli_macros/src/lib.rs at line 111[4.715]→[4.715:1576](∅→∅),[4.1576]→[3.932:986](∅→∅),[3.986]→[4.1007:1911](∅→∅),[4.1007]→[4.1007:1911](∅→∅),[4.1911]→[3.987:1759](∅→∅),[3.1759]→[4.1911:1926](∅→∅),[4.1911]→[4.1911:1926](∅→∅),[4.1926]→[4.1926:1927](∅→∅)
let (span_start, span_length) = location.span();let error_source = string_literal// Offset by 1 to skip the starting `"` double-quote character.subspan(span_start + 1..=span_start + span_length)// Fall back to the whole attribute if `subspan()` returns `None`// This will always happend on stable as subspan is nightly-only:// https://docs.rs/proc-macro2/latest/proc_macro2/struct.Literal.html#method.subspan.unwrap_or(derive_attribute.span());emit_error! { error_source, "invalid glob";note = location.to_string();};}}AttributeError::Walk(walk_error) => {// Generate help textlet help = if let Some(path) = walk_error.path() {let path_name = path.to_str().unwrap();// Might hit an error if file exists but insufficient permissionsmatch 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! { derive_attribute, "error at depth {} while walking path", walk_error.depth();help = help;}}AttributeError::NoMatches {path,complete_match,} => {// Validate the assumption that the user has provided an exact pathassert!(path.exists());assert_eq!(path.to_string_lossy(), complete_match);emit_error! { derive_attribute, "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";};}}; - edit in cli_macros/src/lib.rs at line 130
use proc_macro_error::{abort, emit_error, proc_macro_error};use quote::{quote, ToTokens};use syn::parse_macro_input; - resolve order conflict in cli_macros/src/lib.rs at line 130[4.675]
- edit in cli_macros/Cargo.toml at line 14
miette = { version = "7.2.0", features = ["fancy"] } - edit in Cargo.lock at line 4
[[package]]name = "addr2line"version = "0.22.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"dependencies = ["gimli",] - edit in Cargo.lock at line 15
name = "adler"version = "1.0.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"[[package]] - edit in Cargo.lock at line 28
[[package]]name = "backtrace"version = "0.3.73"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"dependencies = ["addr2line","cc","cfg-if","libc","miniz_oxide","object","rustc-demangle",][[package]]name = "backtrace-ext"version = "0.2.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"dependencies = ["backtrace",][[package]]name = "bitflags"version = "2.6.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - edit in Cargo.lock at line 70
[[package]]name = "cfg-if"version = "1.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - edit in Cargo.lock at line 85
"miette", - edit in Cargo.lock at line 131
name = "errno"version = "0.3.9"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"dependencies = ["libc","windows-sys 0.52.0",][[package]] - replacement in Cargo.lock at line 163
version = "0.11.0"version = "0.11.1" - replacement in Cargo.lock at line 165
checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78"checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" - edit in Cargo.lock at line 177
"miette", - edit in Cargo.lock at line 207
name = "gimli"version = "0.29.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"[[package]] - edit in Cargo.lock at line 302
[[package]]name = "is_ci"version = "1.2.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" - edit in Cargo.lock at line 329
[[package]]name = "linux-raw-sys"version = "0.4.14"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - edit in Cargo.lock at line 381
name = "miette"version = "7.2.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1"dependencies = ["backtrace","backtrace-ext","cfg-if","miette-derive","owo-colors","supports-color","supports-hyperlinks","supports-unicode","terminal_size","textwrap","thiserror","unicode-width",][[package]]name = "miette-derive"version = "7.2.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c"dependencies = ["proc-macro2","quote","syn 2.0.48",][[package]] - edit in Cargo.lock at line 416
[[package]]name = "miniz_oxide"version = "0.7.4"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"dependencies = ["adler",] - edit in Cargo.lock at line 466[4.3874][30.343]
name = "object"version = "0.36.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"dependencies = ["memchr",][[package]] - edit in Cargo.lock at line 484
name = "owo-colors"version = "4.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"[[package]] - edit in Cargo.lock at line 578
[[package]]name = "rustc-demangle"version = "0.1.24"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"[[package]]name = "rustix"version = "0.38.34"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"dependencies = ["bitflags","errno","libc","linux-raw-sys","windows-sys 0.52.0",] - edit in Cargo.lock at line 634
name = "smawk"version = "0.3.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"[[package]] - edit in Cargo.lock at line 644
[[package]]name = "supports-color"version = "3.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f"dependencies = ["is_ci",][[package]]name = "supports-hyperlinks"version = "3.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee"[[package]]name = "supports-unicode"version = "3.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" - edit in Cargo.lock at line 705
name = "terminal_size"version = "0.3.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"dependencies = ["rustix","windows-sys 0.48.0",][[package]]name = "textwrap"version = "0.16.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"dependencies = ["smawk","unicode-linebreak","unicode-width",][[package]] - edit in Cargo.lock at line 760
[[package]]name = "unicode-linebreak"version = "0.1.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - edit in Cargo.lock at line 772
[[package]]name = "unicode-width"version = "0.1.13"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" - edit in Cargo.lock at line 846
[[package]]name = "windows-sys"version = "0.48.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"dependencies = ["windows-targets 0.48.5",] - edit in Cargo.lock at line 857
name = "windows-sys"version = "0.52.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"dependencies = ["windows-targets 0.52.6",][[package]]name = "windows-targets"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"dependencies = ["windows_aarch64_gnullvm 0.48.5","windows_aarch64_msvc 0.48.5","windows_i686_gnu 0.48.5","windows_i686_msvc 0.48.5","windows_x86_64_gnu 0.48.5","windows_x86_64_gnullvm 0.48.5","windows_x86_64_msvc 0.48.5",][[package]]name = "windows-targets"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"dependencies = ["windows_aarch64_gnullvm 0.52.6","windows_aarch64_msvc 0.52.6","windows_i686_gnu 0.52.6","windows_i686_gnullvm","windows_i686_msvc 0.52.6","windows_x86_64_gnu 0.52.6","windows_x86_64_gnullvm 0.52.6","windows_x86_64_msvc 0.52.6",][[package]]name = "windows_aarch64_gnullvm"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"[[package]]name = "windows_aarch64_gnullvm"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"[[package]]name = "windows_aarch64_msvc"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"[[package]]name = "windows_aarch64_msvc"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"[[package]]name = "windows_i686_gnu"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"[[package]]name = "windows_i686_gnu"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"[[package]]name = "windows_i686_gnullvm"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"[[package]]name = "windows_i686_msvc"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"[[package]]name = "windows_i686_msvc"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"[[package]]name = "windows_x86_64_gnu"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"[[package]]name = "windows_x86_64_gnu"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"[[package]]name = "windows_x86_64_gnullvm"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"[[package]]name = "windows_x86_64_gnullvm"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"[[package]]name = "windows_x86_64_msvc"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"[[package]]name = "windows_x86_64_msvc"version = "0.52.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"[[package]]