Skip to main content

eure_mark/
query.rs

1//! Query-flow queries for eure-mark.
2
3use std::sync::Arc;
4
5use eure::query::{ParseCst, ParseDocument, TextFile};
6use eure::report::{ErrorReports, Origin, format_error_reports};
7use eure_document::document::EureDocument;
8use eure_document::parse::ParseError;
9use eure_tree::prelude::Cst;
10use eure_tree::tree::InputSpan;
11use query_flow::{Db, QueryError, query};
12
13use crate::document::EumdDocument;
14use crate::report::EumdReportContext;
15use crate::{check_references_with_spans, report_check_errors};
16
17/// Parsed EumdDocument with CST and OriginMap for error reporting.
18#[derive(Clone, PartialEq)]
19pub struct ParsedEumd {
20    /// The parsed EumdDocument
21    pub doc: Arc<EumdDocument>,
22    /// The underlying EureDocument (for span resolution)
23    pub eure_doc: Arc<EureDocument>,
24    /// CST for error formatting
25    pub cst: Arc<Cst>,
26    /// Origin map for error formatting
27    pub origins: Arc<eure::document::OriginMap>,
28}
29
30/// Parse an EumdDocument from a file.
31///
32/// This query combines:
33/// - ParseDocument (which internally handles CST parsing)
34/// - EumdDocument parsing from the document
35///
36/// Returns errors via ErrorReports if parsing fails.
37#[query]
38pub fn parse_eumd_document(db: &impl Db, file: TextFile) -> Result<ParsedEumd, QueryError> {
39    // Parse the document
40    let parsed_doc = db.query(ParseDocument::new(file.clone()))?;
41
42    // Get CST for error formatting (cached from ParseDocument's internal call)
43    let parsed_cst = db.query(ParseCst::new(file.clone()))?;
44
45    // Parse EumdDocument from the document
46    let root_id = parsed_doc.doc.get_root_id();
47    let eumd_doc: EumdDocument = parsed_doc.doc.parse(root_id).map_err(|e: ParseError| {
48        // Convert parse error to ErrorReports
49        // FIXME: Fallback to EMPTY span when node span resolution fails.
50        // Should set is_fallback flag on Origin when span is missing.
51        let span = parsed_doc
52            .origins
53            .get_value_span(e.node_id, &parsed_cst.cst)
54            .unwrap_or(InputSpan::EMPTY);
55        let origin = Origin::new(file.clone(), span);
56        ErrorReports::from(vec![eure::report::ErrorReport::error(
57            e.to_string(),
58            origin,
59        )])
60    })?;
61
62    Ok(ParsedEumd {
63        doc: Arc::new(eumd_doc),
64        eure_doc: parsed_doc.doc.clone(),
65        cst: Arc::new(parsed_cst.cst.clone()),
66        origins: parsed_doc.origins.clone(),
67    })
68}
69
70/// Check references in an EumdDocument and return errors.
71///
72/// This query combines:
73/// - ParseEumdDocument
74/// - Reference checking
75///
76/// Returns ErrorReports with any reference errors found.
77#[query]
78pub fn check_eumd_references(db: &impl Db, file: TextFile) -> Result<ErrorReports, QueryError> {
79    let parsed = db.query(ParseEumdDocument::new(file.clone()))?;
80
81    // Check references (explicit dereference for proper type coercion)
82    let result = check_references_with_spans(&parsed.doc, &parsed.eure_doc);
83
84    if result.is_ok() {
85        Ok(ErrorReports::new())
86    } else {
87        // Convert check errors to ErrorReports
88        let ctx = EumdReportContext {
89            file,
90            cst: &parsed.cst,
91            origins: &parsed.origins,
92        };
93        Ok(report_check_errors(&result, &ctx))
94    }
95}
96
97/// Check references in an EumdDocument and return formatted error strings.
98///
99/// This query combines:
100/// - CheckEumdReferences
101/// - Error formatting
102///
103/// Returns formatted error strings for each reference error found.
104#[query]
105pub fn check_eumd_references_formatted(
106    db: &impl Db,
107    file: TextFile,
108) -> Result<Vec<String>, QueryError> {
109    let reports = db.query(CheckEumdReferences::new(file))?;
110
111    // Format each error individually
112    let mut formatted = Vec::new();
113    for r in reports.iter() {
114        let single = ErrorReports::from(vec![r.clone()]);
115        let s = format_error_reports(db, &single, false)?;
116        formatted.push(s.trim().to_string());
117    }
118    Ok(formatted)
119}