sapio_base/effects/
mod.rs

1// Copyright Judica, Inc 2021
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4//  License, v. 2.0. If a copy of the MPL was not distributed with this
5//  file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7//! general non-parameter compilation state required by all contracts
8
9use crate::reverse_path::ReversePath;
10use crate::serialization_helpers::SArc;
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14
15use std::sync::Arc;
16pub mod path_fragment;
17pub use path_fragment::*;
18pub mod reverse_path;
19pub use reverse_path::*;
20
21/// Convenience type name for an EffectPath
22pub type EffectPath = ReversePath<PathFragment>;
23
24/// Error types for EffectDB Accesses
25#[derive(Debug)]
26pub enum EffectDBError {
27    /// Error was from Deserialization
28    SerializationError(serde_json::Error),
29}
30
31impl From<serde_json::Error> for EffectDBError {
32    fn from(e: serde_json::Error) -> Self {
33        EffectDBError::SerializationError(e)
34    }
35}
36/// A Generic Trait for EffectDB Functionality
37pub trait EffectDB {
38    /// internal implementation to retrieve a JSON for the path
39    fn get_value<'a>(
40        &'a self,
41        at: &Arc<EffectPath>,
42    ) -> Box<dyn Iterator<Item = (&'a Arc<String>, &'a serde_json::Value)> + 'a>;
43}
44/// #  Effects
45/// Map of all effects to process during compilation.  Each Key represents a
46/// path, each sub-key represents the sub-path name and value.
47#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
48pub struct MapEffectDB {
49    /// # The set of all effects
50    /// List of effects to include while compiling.
51    #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
52    effects: BTreeMap<SArc<EffectPath>, BTreeMap<SArc<String>, serde_json::Value>>,
53    #[serde(skip, default)]
54    empty: BTreeMap<SArc<String>, serde_json::Value>,
55}
56
57/// # Editable Effects
58/// A to/from container mirroring [`MapEffectsDB`], except the inner fields are
59/// public. This allows us to edit [`MapEffectsDB`] in certain contexts where we
60/// can fully replace it, without the concern of mutability for contract authors.
61pub struct EditableMapEffectDB {
62    /// All Effects currently in the set of effects
63    pub effects: BTreeMap<SArc<EffectPath>, BTreeMap<SArc<String>, serde_json::Value>>,
64    /// Catch-all for extra data for future extension
65    pub empty: BTreeMap<SArc<String>, serde_json::Value>,
66}
67
68impl From<MapEffectDB> for EditableMapEffectDB {
69    fn from(MapEffectDB { effects, empty }: MapEffectDB) -> Self {
70        Self { effects, empty }
71    }
72}
73impl From<EditableMapEffectDB> for MapEffectDB {
74    fn from(EditableMapEffectDB { effects, empty }: EditableMapEffectDB) -> Self {
75        Self { effects, empty }
76    }
77}
78
79impl MapEffectDB {
80    /// helper function for when effectdb should not be serialized
81    pub fn skip_serializing(&self) -> bool {
82        self.effects.is_empty()
83    }
84}
85
86impl EffectDB for MapEffectDB {
87    fn get_value<'a>(
88        &'a self,
89        at: &Arc<EffectPath>,
90    ) -> Box<dyn Iterator<Item = (&'a Arc<String>, &'a serde_json::Value)> + 'a> {
91        let r: &BTreeMap<_, _> = self.effects.get(&SArc(at.clone())).unwrap_or(&self.empty);
92        Box::new(r.iter().map(|(a, b)| (&a.0, b)))
93    }
94}
95
96#[cfg(test)]
97mod test {
98    use super::*;
99    use std::convert::{TryFrom, TryInto};
100    #[test]
101    fn test_string() {
102        let v: Vec<PathFragment> = vec![
103            "hello".try_into().unwrap(),
104            "#123".try_into().unwrap(),
105            PathFragment::FinishFn,
106        ];
107        let r = EffectPath::try_from(v).unwrap();
108        assert_eq!(String::from(r.clone()), "hello/#123/@finish_fn");
109        assert_eq!(Ok(r), EffectPath::try_from("hello/#123/@finish_fn"));
110    }
111    #[test]
112    fn test_serde() {
113        let v: Vec<PathFragment> = vec![
114            "hello".try_into().unwrap(),
115            PathFragment::Branch(100),
116            PathFragment::FinishFn,
117        ];
118        let r = EffectPath::try_from(v).unwrap();
119        assert_eq!(
120            serde_json::to_string(&r).unwrap(),
121            "\"hello/#100/@finish_fn\""
122        );
123        assert_eq!(
124            Ok(r),
125            serde_json::from_str("\"hello/#100/@finish_fn\"").map_err(|_| ())
126        );
127    }
128}