pub mod combinators;
pub mod error;
pub mod plan;
pub(crate) mod validation;
use std::collections::HashMap;
use crate::corpus::{Corpus, ValueProvenance};
pub use combinators::RuleExpr;
pub use error::AethelError;
pub use error::{PlanError, PlanErrorReport};
pub use plan::{CompiledPlan, PlanBuilder, PoolRef, RuleKey, ValidatedPlan};
#[derive(Debug, Clone)]
pub struct ComposedValue {
pub value: String,
pub provenance: Vec<ValueProvenance>,
}
impl ComposedValue {
pub fn merge(mut self, other: ComposedValue) -> Self {
self.value.push_str(&other.value);
self.provenance.extend(other.provenance);
self
}
}
pub struct GenerationContext<'a> {
pub corpus: &'a Corpus,
pub history: HashMap<RuleKey, ComposedValue>,
}
impl<'a> GenerationContext<'a> {
pub fn new(corpus: &'a Corpus) -> Self {
Self {
corpus,
history: HashMap::new(),
}
}
pub fn get(&self, key: impl AsRef<RuleKey>) -> Option<&ComposedValue> {
self.history.get(key.as_ref())
}
pub fn get_str(&self, key: &str) -> Option<&ComposedValue> {
let key = RuleKey::new(key).ok()?;
self.history.get(&key)
}
pub fn require(&self, key: impl AsRef<RuleKey>) -> Result<&ComposedValue, AethelError> {
let key = key.as_ref();
self.history
.get(key)
.ok_or_else(|| AethelError::MissingDependency(key.as_str().to_string()))
}
pub(crate) fn insert_typed(&mut self, key: RuleKey, value: ComposedValue) {
self.history.insert(key, value);
}
}
#[cfg(test)]
mod tests {
use crate::{
corpus::ValueProvenance,
engine::{ComposedValue, GenerationContext, RuleKey},
};
#[test]
fn composed_value_merge_appends_string_and_provenance() {
fn provenance(source_id: &str) -> ValueProvenance {
ValueProvenance {
source_id: source_id.to_string(),
document_title: "doc".to_string(),
section: "name".to_string(),
field: "first".to_string(),
}
}
let left = ComposedValue {
value: "A".to_string(),
provenance: vec![provenance("left")],
};
let right = ComposedValue {
value: "B".to_string(),
provenance: vec![provenance("right")],
};
let merged = left.merge(right);
assert_eq!(merged.value, "AB");
assert_eq!(merged.provenance.len(), 2);
assert_eq!(merged.provenance[0].source_id, "left");
assert_eq!(merged.provenance[1].source_id, "right");
}
#[test]
fn generation_context_get_previous_returns_inserted_value() {
let corpus = crate::corpus::Corpus::builder("weapon")
.add_str(
"ctx-test",
r#"
[header]
title = "ctx"
target = "weapon"
[name]
first = ["ash"]
"#,
)
.build()
.expect("corpus should build");
let mut ctx = GenerationContext::new(&corpus);
let key = RuleKey::new("greeting").expect("rule key should be valid");
ctx.insert_typed(
key.clone(),
ComposedValue {
value: "hello".to_string(),
provenance: vec![],
},
);
let found = ctx.get(&key).expect("key should exist");
assert_eq!(found.value, "hello");
assert!(ctx.get_str("greeting").is_some());
assert!(ctx.get_str("missing").is_none());
}
}