// 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 { consts: PatternConstants, } impl Pattern { /// Matches the pattern against an input. pub fn deserialize<'de, Der>( &self, der: Der, ) -> Result, 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)) } } pub struct PatternBuilder<'s, PKey=&'static str, OKey=&'static str, O=()> { input: &'s str, preds: Option>>, objs: Option>, } 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 + Ord, OKey: Borrow + Ord, O: Serialize, { /// Compiles the pattern. pub fn compile(self) -> Result, PatternError<'s>> { Ok(Pattern { consts: parse(self.input, self.preds, self.objs)? }) } }