diff --git a/crates/meilisearch/src/routes/indexes/render.rs b/crates/meilisearch/src/routes/indexes/render.rs index 31e23936a..ef1576ccf 100644 --- a/crates/meilisearch/src/routes/indexes/render.rs +++ b/crates/meilisearch/src/routes/indexes/render.rs @@ -202,6 +202,18 @@ use RenderError::*; impl From> for ResponseError { fn from(error: RenderError) -> Self { + fn format_span(span: &Span<'_>) -> String { + let base_column = span.get_utf8_column(); + let size = span.fragment().chars().count(); + format!("`{}` (cols {}:{})", span.fragment(), base_column, base_column + size) + } + + fn format_token(token: &Token<'_>) -> String { + let base_column = token.original_span().get_utf8_column(); + let size = token.original_span().fragment().chars().count(); + format!("`{}` (cols {}:{})", token.value(), base_column, base_column + size) + } + match error { MultipleTemplates => ResponseError::from_msg( String::from("Cannot provide both an inline template and a template ID."), @@ -216,7 +228,7 @@ impl From> for ResponseError { Code::InvalidRenderTemplateId, ), UnknownTemplateRoot(root) => ResponseError::from_msg( - format!("Template ID must start with `embedders` or `chatCompletions`, but found `{root}`."), + format!("Template ID must start with `embedders` or `chatCompletions`, but found {}.", format_token(&root)), Code::InvalidRenderTemplateId, ), MissingEmbedderName { mut available } => { @@ -230,26 +242,29 @@ impl From> for ResponseError { EmbedderDoesNotExist { embedder, mut available } => { available.sort_unstable(); ResponseError::from_msg( - format!("Embedder `{embedder}` does not exist.\n Hint: Available embedders are {}.", + format!("Embedder {} does not exist.\n Hint: Available embedders are {}.", + format_token(&embedder), available.iter().map(|s| format!("`{s}`")).collect::>().join(", ")), Code::InvalidRenderTemplateId, ) }, EmbedderUsesFragments { embedder } => ResponseError::from_msg( - format!("Requested document template for embedder `{embedder}` but it uses fragments.\n Hint: Use `indexingFragments` or `searchFragments` instead."), + format!("Requested document template for embedder {} but it uses fragments.\n Hint: Use `indexingFragments` or `searchFragments` instead.", format_token(&embedder)), Code::InvalidRenderTemplateId, ), MissingTemplateAfterEmbedder { embedder, mut indexing, mut search } => { if indexing.is_empty() && search.is_empty() { ResponseError::from_msg( - format!("Missing template id after embedder `{embedder}`.\n Hint: Available template: `documentTemplate`."), + format!("Missing template id after embedder {}.\n Hint: Available template: `documentTemplate`.", + format_token(&embedder)), Code::InvalidRenderTemplateId, ) } else { indexing.sort_unstable(); search.sort_unstable(); ResponseError::from_msg( - format!("Template ID configured with `embedders.{embedder}` but no template kind provided.\n Hint: Available fragments are {}.", + format!("Template ID configured with embedder {} but no template kind provided.\n Hint: Available fragments are {}.", + format_token(&embedder), indexing.iter().map(|s| format!("`indexingFragments.{s}`")).chain( search.iter().map(|s| format!("`searchFragments.{s}`"))).collect::>().join(", ")), Code::InvalidRenderTemplateId, @@ -259,14 +274,16 @@ impl From> for ResponseError { UnknownTemplatePrefix { embedder, found, mut indexing, mut search } => { if indexing.is_empty() && search.is_empty() { ResponseError::from_msg( - format!("Wrong template `{found}` after embedder `{embedder}`.\n Hint: Available template: `documentTemplate`."), + format!("Wrong template {} after embedder {}.\n Hint: Available template: `documentTemplate`.", format_token(&found), format_token(&embedder)), Code::InvalidRenderTemplateId, ) } else { indexing.sort_unstable(); search.sort_unstable(); ResponseError::from_msg( - format!("Wrong template `{found}` after embedder `{embedder}`.\n Hint: Available fragments are {}.", + format!("Wrong template {} after embedder {}.\n Hint: Available fragments are {}.", + format_token(&found), + format_token(&embedder), indexing.iter().map(|s| format!("`indexingFragments.{s}`")).chain( search.iter().map(|s| format!("`searchFragments.{s}`"))).collect::>().join(", ")), Code::InvalidRenderTemplateId, @@ -277,9 +294,10 @@ impl From> for ResponseError { MissingFragment { embedder, kind, mut available } => { available.sort_unstable(); ResponseError::from_msg( - format!("{} fragment name was not provided.\n Hint: Available {} fragments for embedder `{embedder}` are {}.", + format!("{} fragment name was not provided.\n Hint: Available {} fragments for embedder {} are {}.", kind.capitalized(), kind.as_str(), + format_token(&embedder), available.iter().map(|s| format!("`{s}`")).collect::>().join(", ")), Code::InvalidRenderTemplateId, ) @@ -287,15 +305,17 @@ impl From> for ResponseError { FragmentDoesNotExist { embedder, fragment, kind, mut available } => { available.sort_unstable(); ResponseError::from_msg( - format!("{} fragment `{fragment}` does not exist for embedder `{embedder}`.\n Hint: Available {} fragments are {}.", + format!("{} fragment {} does not exist for embedder {}.\n Hint: Available {} fragments are {}.", kind.capitalized(), + format_token(&fragment), + format_token(&embedder), kind.as_str(), available.iter().map(|s| format!("`{s}`")).collect::>().join(", ")), Code::InvalidRenderTemplateId, ) }, LeftOverToken(token) => ResponseError::from_msg( - format!("Leftover token `{token}` after parsing template ID"), + format!("Leftover token {} after parsing template ID", format_token(&token)), Code::InvalidRenderTemplateId, ), MissingChatCompletionTemplate => ResponseError::from_msg( @@ -303,7 +323,7 @@ impl From> for ResponseError { Code::InvalidRenderTemplateId, ), UnknownChatCompletionTemplate(id) => ResponseError::from_msg( - format!("Unknown chat completion template ID `{id}`. The only available template is `documentTemplate`."), + format!("Unknown chat completion template ID {}. The only available template is `documentTemplate`.", format_token(&id)), Code::InvalidRenderTemplateId, ), DocumentNotFound(doc_id) => ResponseError::from_msg( @@ -331,11 +351,11 @@ impl From> for ResponseError { Code::InvalidRenderInput, ), ExpectedDotAfterValue(span) => ResponseError::from_msg( - format!("Expected a dot after value, but found `{span}`."), + format!("Expected a dot after value, but found {}.", format_span(&span)), Code::InvalidRenderTemplateId, ), ExpectedValue(span) => ResponseError::from_msg( - format!("Expected a value, but found `{span}`."), + format!("Expected a value, but found {}.", format_span(&span)), Code::InvalidRenderTemplateId, ), } @@ -570,7 +590,7 @@ pub struct RenderQueryTemplate { #[deserr(default, error = DeserrJsonError)] pub id: Option, #[deserr(default, error = DeserrJsonError)] - pub inline: Option, + pub inline: Option, } #[derive(Debug, Clone, Default, PartialEq, Deserr, ToSchema)] @@ -579,11 +599,11 @@ pub struct RenderQueryInput { #[deserr(default, error = DeserrJsonError)] pub document_id: Option, #[deserr(default, error = DeserrJsonError)] - pub inline: Option>, + pub inline: Option>, } #[derive(Debug, Clone, Serialize, PartialEq, ToSchema)] pub struct RenderResult { - template: serde_json::Value, - rendered: serde_json::Value, + template: Value, + rendered: Value, } diff --git a/crates/meilisearch/tests/documents/render_documents.rs b/crates/meilisearch/tests/documents/render_documents.rs index 672f6ab42..c26dc9b64 100644 --- a/crates/meilisearch/tests/documents/render_documents.rs +++ b/crates/meilisearch/tests/documents/render_documents.rs @@ -26,7 +26,7 @@ async fn wrong_id_prefix() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Template ID must start with `embedders` or `chatCompletions`, but found `{wrong}`.", + "message": "Template ID must start with `embedders` or `chatCompletions`, but found `wrong` (cols 1:6).", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -59,7 +59,7 @@ async fn wrong_embedder() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Embedder `{wrong}` does not exist.\n Hint: Available embedders are `rest`.", + "message": "Embedder `wrong` (cols 11:16) does not exist.\n Hint: Available embedders are `rest`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -75,7 +75,7 @@ async fn missing_template_kind() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Template ID configured with `embedders.{rest}` but no template kind provided.\n Hint: Available fragments are `indexingFragments.basic`, `indexingFragments.withBreed`, `searchFragments.justBreed`, `searchFragments.justName`, `searchFragments.query`.", + "message": "Template ID configured with embedder `rest` (cols 11:15) but no template kind provided.\n Hint: Available fragments are `indexingFragments.basic`, `indexingFragments.withBreed`, `searchFragments.justBreed`, `searchFragments.justName`, `searchFragments.query`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -92,7 +92,7 @@ async fn wrong_template_kind() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Wrong template `{wrong}` after embedder `{rest}`.\n Hint: Available fragments are `indexingFragments.basic`, `indexingFragments.withBreed`, `searchFragments.justBreed`, `searchFragments.justName`, `searchFragments.query`.", + "message": "Wrong template `wrong` (cols 16:21) after embedder `rest` (cols 11:15).\n Hint: Available fragments are `indexingFragments.basic`, `indexingFragments.withBreed`, `searchFragments.justBreed`, `searchFragments.justName`, `searchFragments.query`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -109,7 +109,7 @@ async fn document_template_on_fragmented_index() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Requested document template for embedder `{rest}` but it uses fragments.\n Hint: Use `indexingFragments` or `searchFragments` instead.", + "message": "Requested document template for embedder `rest` (cols 11:15) but it uses fragments.\n Hint: Use `indexingFragments` or `searchFragments` instead.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -126,7 +126,7 @@ async fn missing_fragment_name() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Indexing fragment name was not provided.\n Hint: Available indexing fragments for embedder `{rest}` are `basic`, `withBreed`.", + "message": "Indexing fragment name was not provided.\n Hint: Available indexing fragments for embedder `rest` (cols 11:15) are `basic`, `withBreed`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -138,7 +138,7 @@ async fn missing_fragment_name() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Search fragment name was not provided.\n Hint: Available search fragments for embedder `{rest}` are `justBreed`, `justName`, `query`.", + "message": "Search fragment name was not provided.\n Hint: Available search fragments for embedder `rest` (cols 11:15) are `justBreed`, `justName`, `query`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -156,7 +156,7 @@ async fn wrong_fragment_name() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Indexing fragment `{wrong}` does not exist for embedder `{rest}`.\n Hint: Available indexing fragments are `basic`, `withBreed`.", + "message": "Indexing fragment `wrong` (cols 34:39) does not exist for embedder `rest` (cols 11:15).\n Hint: Available indexing fragments are `basic`, `withBreed`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -168,7 +168,7 @@ async fn wrong_fragment_name() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Search fragment `{wrong}` does not exist for embedder `{rest}`.\n Hint: Available search fragments are `justBreed`, `justName`, `query`.", + "message": "Search fragment `wrong` (cols 32:37) does not exist for embedder `rest` (cols 11:15).\n Hint: Available search fragments are `justBreed`, `justName`, `query`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -188,7 +188,7 @@ async fn leftover_tokens() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Leftover token `{leftover}` after parsing template ID", + "message": "Leftover token `leftover` (cols 44:52) after parsing template ID", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -201,7 +201,7 @@ async fn leftover_tokens() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Leftover token `{leftover}` after parsing template ID", + "message": "Leftover token `leftover` (cols 42:50) after parsing template ID", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -214,7 +214,7 @@ async fn leftover_tokens() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Leftover token `{leftover}` after parsing template ID", + "message": "Leftover token `leftover` (cols 34:42) after parsing template ID", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -274,7 +274,7 @@ async fn wrong_chat_completions_template() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Unknown chat completion template ID `{wrong}`. The only available template is `documentTemplate`.", + "message": "Unknown chat completion template ID `wrong` (cols 17:22). The only available template is `documentTemplate`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id" @@ -638,7 +638,7 @@ async fn embedder_document_template() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Wrong template `{wrong}` after embedder `{rest}`.\n Hint: Available template: `documentTemplate`.", + "message": "Wrong template `wrong` (cols 16:21) after embedder `rest` (cols 11:15).\n Hint: Available template: `documentTemplate`.", "code": "invalid_render_template_id", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"