diff --git a/editoast/editoast_derive/src/view_error/args.rs b/editoast/editoast_derive/src/view_error/args.rs index e946b973179..4f38efc57c9 100644 --- a/editoast/editoast_derive/src/view_error/args.rs +++ b/editoast/editoast_derive/src/view_error/args.rs @@ -37,9 +37,9 @@ pub(super) struct Args { #[darling(with = ErrorAttrs::parse)] attrs: ErrorAttrs, - /// Changes the base name of the error label + /// Changes the base path of the error label #[darling(default)] - name: Option, + path: Option, /// Whether to include type data into the error `context` field /// @@ -71,7 +71,7 @@ struct VariantArgs { #[darling(default)] context: bool, #[darling(default)] - name: Option, + path: Option, } #[derive(Debug, FromField)] @@ -121,11 +121,14 @@ impl Args { ident, data, attrs: ErrorAttrs { thiserror }, - name, + path, context: context_on_type, args: TypeArgs { status }, } = self; - let label = name.unwrap_or_else(|| normalize_label(&ident)); + let label = path + .as_ref() + .map(normalize_path) + .unwrap_or_else(|| normalize_label(&ident)); let mut view_error_impl = ViewErrorImpl::new(ident); match data { @@ -166,9 +169,12 @@ impl Args { attrs: ErrorAttrs { thiserror }, status, context: context_on_variant, - name, + path, } = variant; - let variant_label = name.unwrap_or_else(|| normalize_label(&variant_ident)); + let variant_label = path + .as_ref() + .map(normalize_path) + .unwrap_or_else(|| normalize_label(&variant_ident)); let pattern = fields.pattern(Some(&variant_ident)); if let Some(ForwardedField { binding, ty }) = fields.forwarded_view_error() { view_error_impl.forward_view_error(pattern, binding, ty); @@ -208,6 +214,14 @@ impl Args { } } +fn normalize_path(path: &syn::Path) -> String { + path.segments + .iter() + .map(|segment| normalize_label(&segment.ident)) + .collect::>() + .join(":") +} + /// Converts to snake_case, stripping any `_errors` or `_error` suffix fn normalize_label(ident: &syn::Ident) -> String { let label = ident.unraw().to_string().to_snek_case(); @@ -413,3 +427,16 @@ fn reject_unsupported_from(attrs: &[syn::Attribute]) -> Result<()> { Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn path_normalization() { + assert_eq!( + normalize_path(&syn::parse_quote! { DocumentErrors::NotFoundError }), + "document:not_found" + ); + } +} diff --git a/editoast/openapi.yaml b/editoast/openapi.yaml index a9017774ce7..8fdbf4e247b 100644 --- a/editoast/openapi.yaml +++ b/editoast/openapi.yaml @@ -541,7 +541,7 @@ paths: type: type: string enum: - - editoast:document::not_found + - editoast:document:not_found '500': description: '' content: @@ -643,7 +643,7 @@ paths: type: type: string enum: - - editoast:document::not_found + - editoast:document:not_found '500': description: '' content: diff --git a/editoast/src/views/documents.rs b/editoast/src/views/documents.rs index 352572c76d1..9aa8b55b204 100644 --- a/editoast/src/views/documents.rs +++ b/editoast/src/views/documents.rs @@ -29,7 +29,7 @@ pub(in crate::views) enum DatabaseError { #[derive(Debug, thiserror::Error, ViewError)] #[error("Document '{document_key}' not found")] -#[view_error(status = NOT_FOUND, context, name = "document::not_found")] +#[view_error(status = NOT_FOUND, context, path = document::not_found)] pub(in crate::views) struct DocumentNotFound { document_key: i64, } diff --git a/editoast/src/views/error.rs b/editoast/src/views/error.rs index a5f6b4efad8..5604a94a007 100644 --- a/editoast/src/views/error.rs +++ b/editoast/src/views/error.rs @@ -184,7 +184,7 @@ mod tests { fn attributes() { #[derive(Debug, thiserror::Error, ViewError)] #[error("ohno")] - #[view_error(name = "custom", status = NOT_FOUND, context)] + #[view_error(path = custom, status = NOT_FOUND, context)] struct Unit; assert_eq!( @@ -203,7 +203,7 @@ mod tests { fn attributes_and_context() { #[derive(Debug, thiserror::Error, ViewError)] #[error("value: {0}")] - #[view_error(name = "custom", status = UNAUTHORIZED, context)] + #[view_error(path = custom, status = UNAUTHORIZED, context)] struct Newtype(String); assert_eq!( @@ -229,7 +229,7 @@ mod tests { fn forwarded_view_error() { #[derive(Debug, thiserror::Error, ViewError)] #[error("inner error: {detail}")] - #[view_error(name = "inner", status = IM_A_TEAPOT, context)] + #[view_error(path = inner, status = IM_A_TEAPOT, context)] struct InnerError { detail: String, } @@ -275,7 +275,7 @@ mod tests { fn context() { #[derive(Debug, thiserror::Error, ViewError)] #[error("tuple: {0}, {1}")] - #[view_error(name = "custom", status = BAD_REQUEST, context)] + #[view_error(path = custom, status = BAD_REQUEST, context)] struct Tuple(String, u32); assert_eq!( @@ -307,7 +307,7 @@ mod tests { fn forwarded_view_error_with_additional_field() { #[derive(Debug, thiserror::Error, ViewError)] #[error("inner error: {detail}")] - #[view_error(name = "inner", status = IM_A_TEAPOT, context)] + #[view_error(path = inner, status = IM_A_TEAPOT, context)] struct InnerError { detail: String, } @@ -338,7 +338,7 @@ mod tests { fn attributes_and_context() { #[derive(Debug, thiserror::Error, ViewError)] #[error("incident {incident_id}: {cause}; fix: {fix}")] - #[view_error(name = "custom", status = SERVICE_UNAVAILABLE, context)] + #[view_error(path = custom, status = SERVICE_UNAVAILABLE, context)] struct Named { cause: String, fix: String, @@ -382,7 +382,7 @@ mod tests { fn forwarded_view_error_with_additional_field() { #[derive(Debug, thiserror::Error, ViewError)] #[error("inner error: {detail}")] - #[view_error(name = "inner", status = IM_A_TEAPOT, context)] + #[view_error(path = inner, status = IM_A_TEAPOT, context)] struct InnerError { detail: String, } @@ -419,10 +419,10 @@ mod tests { #[test] fn variants() { #[derive(Debug, thiserror::Error, ViewError)] - #[view_error(name = "ColorErrors")] + #[view_error(path = ColorErrors)] enum Color { #[error("red")] - #[view_error(name = "RED_ERROR")] + #[view_error(path = RED_ERROR)] Red, #[error("green")] Green, @@ -432,15 +432,15 @@ mod tests { assert_eq!( EditoastError::from(Color::Red), - EditoastError::new("editoast:ColorErrors:RED_ERROR", 500, "red") + EditoastError::new("editoast:color:red", 500, "red") ); assert_eq!( EditoastError::from(Color::Green), - EditoastError::new("editoast:ColorErrors:green", 500, "green") + EditoastError::new("editoast:color:green", 500, "green") ); assert_eq!( EditoastError::from(Color::BlueError), - EditoastError::new("editoast:ColorErrors:blue", 500, "blue") + EditoastError::new("editoast:color:blue", 500, "blue") ); } } @@ -453,10 +453,10 @@ mod tests { #[test] fn variants() { #[derive(Debug, thiserror::Error, ViewError)] - #[view_error(name = "mixed", context)] + #[view_error(path = mixed, context)] enum Mixed { #[error("unit")] - #[view_error(name = "custom_unit")] + #[view_error(path = custom_unit)] Unit, #[error("newtype: {0}")] #[view_error(status = BAD_REQUEST)] @@ -578,7 +578,7 @@ mod tests { fn forwarded_view_errors() { #[derive(Debug, thiserror::Error, ViewError)] #[error("inner error: {detail}")] - #[view_error(name = "inner", status = IM_A_TEAPOT, context)] + #[view_error(path = inner, status = IM_A_TEAPOT, context)] struct InnerError { detail: String, }