go up view raw 4027
// 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)?
        })
    }
}