use crate::{ManualIndex, discover_manual_roots};
use mant_protocol::{CatalogQuery, DocumentCatalog, MAX_CATALOG_PATTERN_CHARS};
use mant_sources::{SourceConfigError, list_registered_documents};
use std::{error::Error, fmt, path::PathBuf};
mod inventory;
mod selection;
#[cfg(test)]
mod tests;
pub(crate) use inventory::list_available_documents_from;
pub use selection::{PreparedCatalogQuery, query_available_documents};
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum AvailableDocumentKind {
Markdown,
Manual,
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum AvailableDocumentOrigin {
Documents,
Source(String),
ManualPath,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AvailableDocument {
pub name: String,
pub logical_path: String,
pub kind: AvailableDocumentKind,
pub manual_section: Option<String>,
pub path: PathBuf,
pub origin: AvailableDocumentOrigin,
pub source_priority: Option<i32>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CatalogError {
EmptyPattern,
PatternTooLong,
InvalidLimit,
ConflictingSelectors,
InvalidPattern(String),
}
impl fmt::Display for CatalogError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyPattern => formatter.write_str("catalog pattern must not be empty"),
Self::PatternTooLong => {
write!(
formatter,
"catalog pattern exceeds the {MAX_CATALOG_PATTERN_CHARS}-character limit"
)
}
Self::InvalidLimit => formatter.write_str("catalog limit must be between 1 and 10000"),
Self::ConflictingSelectors => {
formatter.write_str("catalog source and manual-section filters cannot be combined")
}
Self::InvalidPattern(message) => {
write!(formatter, "invalid catalog pattern: {message}")
}
}
}
}
impl Error for CatalogError {}
pub fn list_available_documents() -> Result<Vec<AvailableDocument>, SourceConfigError> {
let manuals = ManualIndex::from_roots(discover_manual_roots());
Ok(list_available_documents_from(
list_registered_documents()?,
manuals.pages(),
))
}
pub fn discover_documents(query: &CatalogQuery) -> Result<DocumentCatalog, String> {
discover_with(query, list_available_documents)
}
fn discover_with(
query: &CatalogQuery,
inventory: impl FnOnce() -> Result<Vec<AvailableDocument>, SourceConfigError>,
) -> Result<DocumentCatalog, String> {
let plan = PreparedCatalogQuery::new(query).map_err(|error| error.to_string())?;
let documents = inventory().map_err(|error| error.to_string())?;
Ok(plan.apply(&documents))
}