Skip to main content

mant_engine/query/
validation_error.rs

1//! Query validation failures cannot carry host acquisition errors.
2use mant_protocol::ScopeTextError;
3use mant_query::SearchError;
4use std::{error::Error, fmt};
5
6/// Invalid query view, independent of source acquisition.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum QueryValidationError {
9    /// Excerpt projection was requested without selectors.
10    EmptySelection,
11    /// Excerpt projection exceeded the closed selector-count bound.
12    TooManySelections {
13        /// Maximum selectors accepted by one focused request.
14        maximum: usize,
15    },
16    /// An excerpt selector was empty.
17    EmptySelector,
18    /// An explicit local selector violated its path/ID grammar or byte limit.
19    InvalidContentSelector,
20    /// Reference inventory policy violated its closed bounds.
21    InvalidReferenceProjection(&'static str),
22    /// A role-filtered outline contained no kinds or exceeded the closed kind family.
23    InvalidEntryKinds,
24    /// An explanation entry name was empty.
25    EmptyEntry,
26    /// A node or semantic-entry selector violated the bounded request contract.
27    InvalidViewSelector {
28        /// User-facing field name.
29        field: &'static str,
30        /// Precise bound or character violation.
31        error: ScopeTextError,
32    },
33    /// Search configuration failed validation.
34    InvalidSearch(SearchError),
35    /// Explanation configuration failed validation or had no readable content.
36    InvalidExplanation(mant_query::ExplanationError),
37}
38
39impl fmt::Display for QueryValidationError {
40    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            Self::EmptySelection => formatter.write_str("at least one outline node is required"),
43            Self::TooManySelections { maximum } => {
44                write!(
45                    formatter,
46                    "outline nodes must not contain more than {maximum} values"
47                )
48            }
49            Self::EmptySelector => formatter.write_str("outline node must not be empty"),
50            Self::InvalidContentSelector => mant_protocol::InvalidContentSelector.fmt(formatter),
51            Self::InvalidReferenceProjection(reason) => formatter.write_str(reason),
52            Self::InvalidEntryKinds => {
53                formatter.write_str("outline entry kinds must contain between 1 and 9 values")
54            }
55            Self::EmptyEntry => formatter.write_str("semantic entry must not be empty"),
56            Self::InvalidViewSelector { field, error } => {
57                write!(formatter, "{field} {}", view_selector_error_message(*error))
58            }
59            Self::InvalidSearch(error) => error.fmt(formatter),
60            Self::InvalidExplanation(error) => error.fmt(formatter),
61        }
62    }
63}
64impl Error for QueryValidationError {
65    fn source(&self) -> Option<&(dyn Error + 'static)> {
66        match self {
67            Self::InvalidSearch(error) => Some(error),
68            Self::InvalidExplanation(error) => Some(error),
69            _ => None,
70        }
71    }
72}
73fn view_selector_error_message(error: ScopeTextError) -> String {
74    match error {
75        ScopeTextError::Empty => "must not be empty".to_owned(),
76        ScopeTextError::ControlCharacter => "must not contain control characters".to_owned(),
77        ScopeTextError::TooLong { maximum } => {
78            format!("must not exceed {maximum} Unicode scalar values")
79        }
80    }
81}