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}