sapio_base/effects/
path_fragment.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//! Path  Fragments
8use crate::reverse_path::ReversePath;
9use crate::serialization_helpers::SArc;
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13use std::convert::TryFrom;
14use std::str::FromStr;
15use std::sync::Arc;
16
17/// The derivation path fragments allowed, including user-generated
18#[derive(
19    Serialize, Deserialize, Debug, Hash, Eq, PartialEq, JsonSchema, Clone, PartialOrd, Ord,
20)]
21#[serde(into = "String")]
22#[serde(try_from = "&str")]
23pub enum PathFragment {
24    /// The start of a compilation process
25    Root,
26    /// A Clone of some executing context
27    Cloned,
28    /// An Action Branch
29    Action,
30    /// A Finish Function
31    FinishFn,
32    /// A Conditional Compilation Guard
33    CondCompIf,
34    /// A (Clause Generating) Guard
35    Guard,
36    /// A Next Clause
37    Next,
38    /// Suggested Transaction
39    Suggested,
40    /// The Default Effect passed into a Continuation
41    DefaultEffect,
42    /// All the Effects for a Coninuation
43    Effects,
44    /// Metadata Creation
45    Metadata,
46    /// A numbered branch at this level
47    Branch(u64),
48    /// a named branch at this level
49    Named(SArc<String>),
50}
51
52impl From<PathFragment> for String {
53    fn from(a: PathFragment) -> Self {
54        Self::from(&a)
55    }
56}
57impl From<&PathFragment> for String {
58    fn from(a: &PathFragment) -> Self {
59        match a {
60            PathFragment::Root => "@root".into(),
61            PathFragment::Cloned => "@cloned".into(),
62            PathFragment::Action => "@action".into(),
63            PathFragment::FinishFn => "@finish_fn".into(),
64            PathFragment::CondCompIf => "@cond_comp_if".into(),
65            PathFragment::Guard => "@guard".into(),
66            PathFragment::Next => "@next".into(),
67            PathFragment::Suggested => "@suggested".into(),
68            PathFragment::DefaultEffect => "@default_effect".into(),
69            PathFragment::Effects => "@effects".into(),
70            PathFragment::Metadata => "@metadata".into(),
71            PathFragment::Branch(u) => format!("#{}", u),
72            PathFragment::Named(SArc(a)) => a.as_ref().clone(),
73        }
74    }
75}
76
77/// Error for parsing a fragment
78#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, JsonSchema, Clone)]
79pub enum ValidFragmentError {
80    /// branch could not be parsed (must be alphanumeric for user generated)
81    BranchParseError,
82    /// name was using some reserved characters
83    BadName(SArc<String>),
84    /// Other error
85    InvalidReversePath(&'static str),
86}
87
88impl std::error::Error for ValidFragmentError {}
89impl std::fmt::Display for ValidFragmentError {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
91        std::fmt::Debug::fmt(self, f)
92    }
93}
94use std::num::ParseIntError;
95impl From<ParseIntError> for ValidFragmentError {
96    fn from(_u: ParseIntError) -> ValidFragmentError {
97        ValidFragmentError::BranchParseError
98    }
99}
100
101impl TryFrom<Arc<String>> for PathFragment {
102    type Error = ValidFragmentError;
103    fn try_from(s: Arc<String>) -> Result<Self, Self::Error> {
104        Self::try_from(s.as_ref().as_str())
105    }
106}
107impl TryFrom<&str> for PathFragment {
108    type Error = ValidFragmentError;
109    fn try_from(s: &str) -> Result<Self, Self::Error> {
110        Ok(match s {
111            "@root" => PathFragment::Root,
112            "@cloned" => PathFragment::Cloned,
113            "@action" => PathFragment::Action,
114            "@finish_fn" => PathFragment::FinishFn,
115            "@cond_comp_if" => PathFragment::CondCompIf,
116            "@guard" => PathFragment::Guard,
117            "@next" => PathFragment::Next,
118            "@suggested" => PathFragment::Suggested,
119            "@default_effect" => PathFragment::DefaultEffect,
120            "@effects" => PathFragment::Effects,
121            "@metadata" => PathFragment::Metadata,
122            n if n.starts_with('#') => PathFragment::Branch(FromStr::from_str(&n[1..])?),
123            n if n.chars().all(|x| x.is_ascii_alphanumeric() || x == '_') => {
124                PathFragment::Named(SArc(Arc::new(s.into())))
125            }
126            _ => return Err(ValidFragmentError::BadName(SArc(Arc::new(s.into())))),
127        })
128    }
129}
130
131impl From<ReversePath<PathFragment>> for String {
132    fn from(r: ReversePath<PathFragment>) -> String {
133        let mut v: Vec<String> = r.iter().cloned().map(String::from).collect();
134        v.reverse();
135        v.join("/")
136    }
137}
138
139impl TryFrom<&str> for ReversePath<PathFragment> {
140    type Error = ValidFragmentError;
141    fn try_from(r: &str) -> Result<ReversePath<PathFragment>, Self::Error> {
142        let frags = r
143            .split('/')
144            .map(PathFragment::try_from)
145            .collect::<Result<Vec<_>, _>>()?;
146        ReversePath::try_from(frags).map_err(ValidFragmentError::InvalidReversePath)
147    }
148}
149
150impl TryFrom<String> for ReversePath<PathFragment> {
151    type Error = ValidFragmentError;
152    fn try_from(r: String) -> Result<ReversePath<PathFragment>, Self::Error> {
153        Self::try_from(r.as_ref())
154    }
155}