Skip to main content

mant_engine/
query.rs

1//! Full request adapters compose view-independent loading and pure queries.
2use mant_ir::ResolvedContent;
3use mant_loader::{DocumentLoader, LoadError, LoadPolicy, LoadSpec, validate_load_spec};
4use mant_protocol::{
5    EntryProjection, MAX_NODE_SELECTORS, MAX_SEMANTIC_ENTRY_CHARS, QueryExcerpt, QueryInput,
6    QueryOutline, QueryRequest, QuerySearch, QueryView, ScopeTextError, SearchQuery,
7    validate_scope_text,
8};
9use mant_query::{
10    ProjectionError, SearchError, search_query, select_excerpt, validate_search_query,
11};
12use std::{error::Error, fmt};
13mod adapter;
14mod execution;
15mod prepared;
16pub use prepared::PreparedQueryRequest;
17mod validation;
18mod validation_error;
19pub use adapter::DocumentResolver;
20pub use execution::project_query_view;
21pub use validation::validate_query_request;
22pub use validation_error::QueryValidationError;
23/// Complete-request validation or local loading failure.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum QueryError {
26    /// Source selection or acquisition failed.
27    Load(LoadError),
28    /// The requested view cannot be executed.
29    QueryValidation(QueryValidationError),
30}
31
32impl From<LoadError> for QueryError {
33    fn from(error: LoadError) -> Self {
34        Self::Load(error)
35    }
36}
37impl From<QueryValidationError> for QueryError {
38    fn from(error: QueryValidationError) -> Self {
39        Self::QueryValidation(error)
40    }
41}
42impl fmt::Display for QueryError {
43    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44        match self {
45            Self::Load(error) => error.fmt(formatter),
46            Self::QueryValidation(error) => error.fmt(formatter),
47        }
48    }
49}
50impl Error for QueryError {
51    fn source(&self) -> Option<&(dyn Error + 'static)> {
52        match self {
53            Self::Load(error) => Some(error),
54            Self::QueryValidation(error) => Some(error),
55        }
56    }
57}
58
59/// Materialized result of the view carried by a [`QueryRequest`].
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub enum QueryViewResult {
62    /// Complete resolved content with no projection.
63    Full(Box<ResolvedContent>),
64    /// Lightweight structural outline.
65    Outline(QueryOutline),
66    /// One or more selected document nodes.
67    Excerpt(QueryExcerpt),
68    /// Independent semantic evidence; multiple and zero owners are normal.
69    Explanation(mant_protocol::QueryExplanation),
70    /// Paginatable structure-aware search result.
71    Search(QuerySearch),
72}
73
74/// A valid request could not be loaded or projected.
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum QueryExecutionError {
77    /// Input validation or document loading failure.
78    Query(QueryError),
79    /// Outline or selection projection failure.
80    Projection(ProjectionError),
81    /// Search compilation or execution failure.
82    Search(SearchError),
83}
84
85impl fmt::Display for QueryExecutionError {
86    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
87        match self {
88            Self::Query(error) => error.fmt(formatter),
89            Self::Projection(error) => error.fmt(formatter),
90            Self::Search(error) => error.fmt(formatter),
91        }
92    }
93}
94
95impl Error for QueryExecutionError {
96    fn source(&self) -> Option<&(dyn Error + 'static)> {
97        match self {
98            Self::Query(error) => Some(error),
99            Self::Projection(error) => Some(error),
100            Self::Search(error) => Some(error),
101        }
102    }
103}
104
105/// Query the local man database and optional offline tldr caches.
106///
107/// # Errors
108///
109/// Returns [`QueryError`] for invalid input or when neither source can produce
110/// readable content.
111pub fn resolve_query(request: &QueryRequest) -> Result<ResolvedContent, QueryError> {
112    resolve_query_with_policy(request, LoadPolicy::default())
113}
114
115/// Query with an explicit input-resolution policy.
116///
117/// The entire request is validated before capturing local source configuration.
118///
119/// # Errors
120///
121/// Returns [`QueryError`] under the same conditions as [`resolve_query`].
122pub fn resolve_query_with_policy(
123    request: &QueryRequest,
124    policy: LoadPolicy,
125) -> Result<ResolvedContent, QueryError> {
126    let (prepared, resolver) = validated_resolver(request, policy, DocumentResolver::from_system)?;
127    prepared.resolve(&resolver)
128}
129
130/// Load and materialize the view encoded in one native request.
131///
132/// Invalid input or view bounds are rejected before local source discovery.
133///
134/// # Errors
135///
136/// Returns a typed loading, projection, or search failure.
137pub fn execute_query(
138    request: &QueryRequest,
139    policy: LoadPolicy,
140) -> Result<QueryViewResult, QueryExecutionError> {
141    let (prepared, resolver) = validated_resolver(request, policy, DocumentResolver::from_system)
142        .map_err(QueryExecutionError::Query)?;
143    prepared.execute(&resolver)
144}
145
146// Capturing a system snapshot already reads manual configuration. Validation
147// therefore precedes construction, not merely the loader's first lookup.
148fn validated_resolver<T>(
149    request: &QueryRequest,
150    policy: LoadPolicy,
151    factory: impl FnOnce() -> T,
152) -> Result<(PreparedQueryRequest<'_>, T), QueryError> {
153    let prepared = PreparedQueryRequest::new(request, policy)?;
154    Ok((prepared, factory()))
155}
156
157#[cfg(test)]
158mod tests;