summary refs log tree commit diff stats
path: root/src/pattern.rs
blob: 2f7166a8ce149485752555705056381821dbbaab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// 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::parser::parse;
use crate::vm;
use crate::vm::PatternConstants;
use crate::vm::MAX_CALLS;

/// A compiled Datafu pattern.
///
/// # Examples
///
/// ```
/// use datafu::Pattern;
///
/// let pattern = Pattern::<()>::compile::<&str, &str>(
///     "->['value']'hello'",
///     None, None
/// ).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, De>(&self, der: Der) -> Result<De, Der::Error>
    where
        Der: Deserializer<'de>,
        De: Deserialize<'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().unwrap();
        let de = De::deserialize(vm::Unpacker::new(pack, MAX_CALLS));
        todo!()
    }
}

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::Pattern;
    /// 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 = Pattern::<()>::compile::<&str, &str>("
    ///        ->'projects':$dict
    ///          ->commit[:?$str:?$commit]:?$dict
    ///            ->url[:?$str:?$uri]:?$dict
    ///              ->branch:?$dict
    ///                (->active'active'?:?$bool)
    ///                (->federate'federate'?:?$bool)?",
    ///     Some(preds), None
    /// ).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)?
        })
    }
}