cartulary 0.3.0-alpha.1

The knowledge layer of your project — decisions, issues, docs, all in one place.
Documentation
use crate::domain::model::query::{QueryIdentifier, QueryResult};

use super::runner::QueryRunner;
use super::store::QueryStore;

/// Look up `name` in the store, run its script through `runner`, and
/// return the result. An unknown identifier is an error (the CLI
/// surface is "execute *this* one"; absence is a usage mistake, not a
/// silent empty result).
pub fn execute_query(
    store: &dyn QueryStore,
    runner: &dyn QueryRunner,
    name: &QueryIdentifier,
) -> anyhow::Result<QueryResult> {
    let queries = store.list()?;
    let query = queries
        .get(name)
        .ok_or_else(|| anyhow::anyhow!("unknown query '{name}'"))?;
    runner.run(&query.script)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::domain::model::query::{Queries, Query, QuerySchema, QueryScript, QueryValue};
    use std::cell::RefCell;

    struct StubStore {
        listing: Queries,
    }

    impl QueryStore for StubStore {
        fn list(&self) -> anyhow::Result<Queries> {
            Ok(self.listing.clone())
        }
    }

    struct StubRunner {
        seen: RefCell<Option<String>>,
        result: QueryResult,
    }

    impl QueryRunner for StubRunner {
        fn run(&self, script: &QueryScript) -> anyhow::Result<QueryResult> {
            *self.seen.borrow_mut() = Some(script.as_str().to_owned());
            Ok(self.result.clone())
        }

        fn schema(&self) -> anyhow::Result<QuerySchema> {
            Ok(QuerySchema::default())
        }
    }

    fn name(s: &str) -> QueryIdentifier {
        QueryIdentifier::new(s).unwrap()
    }

    fn nq(n: &str, body: &str) -> Query {
        Query::new(name(n), QueryScript::new(body), None)
    }

    #[test]
    fn execute_runs_the_matched_script_through_the_runner() {
        let store = StubStore {
            listing: [nq("counts", "?[n] := n = 1")].into_iter().collect(),
        };
        let runner = StubRunner {
            seen: RefCell::new(None),
            result: QueryResult::new(vec!["n".to_owned()], vec![vec![QueryValue::Int(1)]]),
        };
        let out = execute_query(&store, &runner, &name("counts")).unwrap();
        assert_eq!(runner.seen.borrow().as_deref(), Some("?[n] := n = 1"));
        assert_eq!(out.rows.len(), 1);
    }

    #[test]
    fn execute_errors_when_the_identifier_is_unknown() {
        let store = StubStore {
            listing: Queries::new(),
        };
        let runner = StubRunner {
            seen: RefCell::new(None),
            result: QueryResult::default(),
        };
        let err = execute_query(&store, &runner, &name("missing")).unwrap_err();
        assert!(err.to_string().contains("missing"));
        assert!(runner.seen.borrow().is_none());
    }
}