Skip to main content

mq_db/
mq_engine.rs

1//! mq query engine integration for mq-db.
2//!
3//! Provides [`MqEngine`] which runs [`mq-lang`] programs against Markdown
4//! files on disk or against file-backed documents in a [`DocumentStore`].
5//!
6//! # Example
7//!
8//! ```rust,no_run
9//! use mq_db::mq_engine::MqEngine;
10//! use std::path::Path;
11//!
12//! // Run an mq program against a file
13//! let results = MqEngine::eval_files(".h1", &[Path::new("README.md")]).unwrap();
14//! for r in &results {
15//!     println!("{}", r);
16//! }
17//! ```
18
19use std::path::Path;
20
21use mq_lang::{DefaultEngine, parse_markdown_input};
22
23use crate::{DocumentStore, MqdbError};
24
25/// Thin wrapper around [`mq_lang::DefaultEngine`] for evaluating mq programs
26/// over Markdown content.
27pub struct MqEngine;
28
29impl MqEngine {
30    /// Run an mq program against one or more files on disk.
31    ///
32    /// Each file is parsed independently; results from all files are
33    /// concatenated in order.
34    ///
35    /// # Errors
36    ///
37    /// Returns [`MqdbError::Io`] if a file cannot be read,
38    /// [`MqdbError::Mq`] if parsing or evaluation fails.
39    pub fn eval_files(code: &str, paths: &[&Path]) -> Result<Vec<String>, MqdbError> {
40        let mut engine = DefaultEngine::default();
41        engine.load_builtin_module();
42
43        let mut results = Vec::new();
44        for path in paths {
45            let content = std::fs::read_to_string(path)?;
46            let input = parse_markdown_input(&content).map_err(|e| MqdbError::Mq(e.to_string()))?;
47            let output = engine
48                .eval(code, input.into_iter())
49                .map_err(|e| MqdbError::Mq(e.to_string()))?;
50            for v in output.compact() {
51                results.push(v.to_string());
52            }
53        }
54        Ok(results)
55    }
56
57    /// Run an mq program against all file-backed documents in a store.
58    ///
59    /// Documents that were added via [`DocumentStore::add_str`] (no path)
60    /// are silently skipped.
61    pub fn eval_store(code: &str, store: &DocumentStore) -> Result<Vec<String>, MqdbError> {
62        let paths: Vec<&Path> = store
63            .documents()
64            .iter()
65            .filter_map(|d| d.path.as_deref())
66            .collect();
67        Self::eval_files(code, &paths)
68    }
69}