// Copyright (C) 2021-2022 Soni L.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Datafu Patterns.
use std::borrow::Borrow;
use std::collections::BTreeMap;
use serde::de::Deserialize;
use serde::de::DeserializeSeed as _;
use serde::de::Deserializer;
use serde::ser::Serialize;
use crate::Predicate;
use crate::errors::PatternError;
use crate::graph::Graph;
use crate::parser::parse;
use crate::vm;
use crate::vm::PatternConstants;
use crate::vm::MAX_CALLS;
/// A compiled Datafu pattern.
///
/// # Examples
///
/// ```
/// use datafu::PatternBuilder;
///
/// let pattern = PatternBuilder::for_pattern(
/// "->['value']'hello'",
/// ).compile().expect("failed to compile pattern");
/// ```
pub struct Pattern<O: Serialize> {
consts: PatternConstants<O>,
}
impl<O: Serialize> Pattern<O> {
/// Matches the pattern against an input.
pub fn deserialize<'de, Der>(
&self,
der: Der,
) -> Result<Graph<'_, 'de>, Der::Error>
where
Der: Deserializer<'de>,
{
let mut err = Default::default();
let mut frames = Default::default();
//let mut output = Default::default();
let interp = vm::Interpreter::new(
&self.consts,
&mut err,
&mut frames,
//&mut output,
);
let (mut packs, obj) = vm::Packer::new(
interp,
MAX_CALLS,
).deserialize(der)?;
// this should always be None
debug_assert!(obj.is_none());
debug_assert!(packs.len() <= 1);
let pack = packs.pop();
Ok(Graph(pack))
}
}
/// Builder for [`Pattern`].
pub struct PatternBuilder<'s, PKey=&'static str, OKey=&'static str, O=()> {
input: &'s str,
preds: Option<BTreeMap<PKey, Box<Predicate>>>,
objs: Option<BTreeMap<OKey, O>>,
}
impl<'s> PatternBuilder<'s> {
/// Creates a PatternBuilder for a given pattern.
///
/// # Examples
///
/// ```
/// use datafu::PatternBuilder;
///// use serde::Deserialize;
///// use charx;
/////
///// let preds = vec![
///// ("dict", datafu::pred(|v| { todo!() })),
///// ("str", datafu::pred(|v| { String::deserialize(v).is_ok() })),
///// ("commit", datafu::pred(|v| {
///// if let Ok(v) = String::deserialize(v) {
///// v.len() == 40 && v.trim_start_matches(
///// charx::is_ascii_hexdigit
///// ).is_empty()
///// } else {
///// false
///// }
///// })),
///// ("uri", datafu::pred(|v| { todo!() })),
///// ("bool", datafu::pred(|v| { todo!() })),
///// ].into_iter().collect();
///// let pattern = PatternBuilder::for_pattern("
///// ->'projects':$dict
///// ->commit[:?$str:?$commit]:?$dict
///// ->url[:?$str:?$uri]:?$dict
///// ->branch:?$dict
///// (->active'active'?:?$bool)
///// (->federate'federate'?:?$bool)?"
///// ).compile().expect("failed to compile pattern");
/// let pattern = PatternBuilder::for_pattern("
/// :map
/// ->['projects'?]:map
/// ->[commit:?str]:?map
/// ->[url:?str]:?map
/// ->[branch:?str]:?map
/// (->['active'?]active:?bool)?
/// (->['federate'?]?federate:?bool)?
/// (->['pinned'?]?pinned:?bool)?
/// ").compile().expect("failed to compile pattern");
/// ```
pub fn for_pattern(pattern: &'s str) -> Self {
Self {
input: pattern,
preds: None,
objs: None,
}
}
}
impl<'s, PKey, OKey, O> PatternBuilder<'s, PKey, OKey, O>
where
PKey: Borrow<str> + Ord,
OKey: Borrow<str> + Ord,
O: Serialize,
{
/// Compiles the pattern.
pub fn compile(self) -> Result<Pattern<O>, PatternError<'s>> {
Ok(Pattern {
consts: parse(self.input, self.preds, self.objs)?
})
}
}