cosmic_space/
selector.rs

1use core::fmt::Formatter;
2use core::str::FromStr;
3use std::ops::Deref;
4
5use nom::combinator::all_consuming;
6use serde::de::{Error, Visitor};
7use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
8
9use cosmic_nom::{new_span, Trace};
10use specific::{ProductSelector, ProviderSelector, VariantSelector, VendorSelector};
11
12use crate::kind::{BaseKind, Kind, KindParts, Specific};
13use crate::loc::{
14    Layer, ToBaseKind, Topic, Variable, VarVal, Version,
15};
16use crate::parse::error::result;
17use crate::parse::{
18    CamelCase, consume_hierarchy, Env, kind_selector, point_segment_selector,
19    point_selector, specific_selector,
20};
21use crate::substance::{
22    CallWithConfigDef, Substance, SubstanceFormat, SubstanceKind, SubstancePattern,
23    SubstancePatternCtx, SubstancePatternDef,
24};
25use crate::util::{ToResolved, ValueMatcher, ValuePattern};
26use crate::SpaceErr;
27use crate::point::{Point, PointCtx, PointSeg, PointVar, RouteSeg};
28
29pub type KindSelector = KindSelectorDef<KindBaseSelector, SubKindSelector, SpecificSelector>;
30pub type KindSelectorVar =
31    KindSelectorDef<VarVal<KindBaseSelector>, VarVal<SubKindSelector>, VarVal<SpecificSelector>>;
32
33#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
34pub struct KindSelectorDef<GenericKindSelector, GenericSubKindSelector, SpecificSelector> {
35    pub base: GenericKindSelector,
36    pub sub: GenericSubKindSelector,
37    pub specific: ValuePattern<SpecificSelector>,
38}
39
40impl KindSelector {
41    pub fn new(
42        kind: KindBaseSelector,
43        sub_kind: SubKindSelector,
44        specific: ValuePattern<SpecificSelector>,
45    ) -> Self {
46        Self {
47            base: kind,
48            sub: sub_kind,
49            specific,
50        }
51    }
52
53    pub fn from_base(base: BaseKind) -> Self {
54        Self {
55            base: Pattern::Exact(base),
56            sub: SubKindSelector::Any,
57            specific: ValuePattern::Any,
58        }
59    }
60
61    pub fn matches(&self, kind: &Kind) -> bool
62    where
63        KindParts: Eq + PartialEq,
64    {
65        /*
66        self.base.matches(&kind.to_base())
67            && self.sub.matches(&kind.sub().into())
68            && self.specific.is_match_opt(kind.specific().as_ref()).is_ok()
69
70         */
71
72        // HACKED waiting to be refactored when we actually need to match on subtypes
73        self.base.matches(&kind.to_base())
74    }
75
76    pub fn as_point_segments(&self) -> Result<String, SpaceErr> {
77        match &self.base {
78            KindBaseSelector::Any => Err(SpaceErr::server_error(
79                "cannot turn a base wildcard kind into point segments",
80            )),
81            KindBaseSelector::Exact(e) => Ok(e.to_skewer().to_string()),
82        }
83    }
84}
85
86impl FromStr for KindSelector {
87    type Err = SpaceErr;
88
89    fn from_str(s: &str) -> Result<Self, Self::Err> {
90        Ok(result(kind_selector(new_span(s)))?)
91    }
92}
93
94impl ToString for KindSelector {
95    fn to_string(&self) -> String {
96        format!(
97            "{}<{}<{}>>",
98            self.base.to_string(),
99            match &self.sub {
100                SubKindSelector::Any => "*".to_string(),
101                SubKindSelector::Exact(sub) => {
102                    sub.as_ref().unwrap().to_string()
103                }
104            },
105            self.specific.to_string()
106        )
107    }
108}
109
110impl KindSelector {
111    pub fn any() -> Self {
112        Self {
113            base: KindBaseSelector::Any,
114            sub: SubKindSelector::Any,
115            specific: ValuePattern::Any,
116        }
117    }
118}
119
120pub type SubKindSelector = Pattern<Option<CamelCase>>;
121
122#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
123pub struct SelectorDef<Hop> {
124    pub hops: Vec<Hop>,
125}
126
127pub type Selector = SelectorDef<Hop>;
128pub type SelectorCtx = SelectorDef<Hop>;
129pub type SelectorVar = SelectorDef<Hop>;
130
131impl ToResolved<Selector> for Selector {
132    fn to_resolved(self, env: &Env) -> Result<Selector, SpaceErr> {
133        Ok(self)
134    }
135}
136
137impl FromStr for Selector {
138    type Err = SpaceErr;
139
140    fn from_str(s: &str) -> Result<Self, Self::Err> {
141        let (_, rtn) = all_consuming(point_selector)(new_span(s))?;
142        Ok(rtn)
143    }
144}
145
146impl Selector {
147    fn consume(&self) -> Option<Selector> {
148        if self.hops.is_empty() {
149            Option::None
150        } else {
151            let mut hops = self.hops.clone();
152            hops.remove(0);
153            Option::Some(Selector { hops })
154        }
155    }
156
157    pub fn matches_root(&self) -> bool {
158        if self.hops.is_empty() {
159            true
160        } else if self.hops.len() == 1 {
161            let hop = self.hops.first().unwrap();
162            if PointSegSelector::InclusiveAny == hop.segment_selector
163                || PointSegSelector::InclusiveRecursive == hop.segment_selector
164            {
165                hop.kind_selector.matches(&Kind::Root)
166            } else {
167                false
168            }
169        } else {
170            false
171        }
172    }
173
174    pub fn is_root(&self) -> bool {
175        self.hops.is_empty()
176    }
177
178    pub fn is_final(&self) -> bool {
179        self.hops.len() == 1
180    }
181
182    pub fn query_root(&self) -> Point {
183        let mut segments = vec![];
184        for hop in &self.hops {
185            if let PointSegSelector::Exact(exact) = &hop.segment_selector {
186                if hop.inclusive {
187                    break;
188                }
189                match exact {
190                    ExactPointSeg::PointSeg(seg) => {
191                        segments.push(seg.clone());
192                    }
193                    ExactPointSeg::Version(version) => {
194                        segments.push(PointSeg::Version(version.clone()));
195                    }
196                }
197            } else {
198                break;
199            }
200        }
201
202        Point {
203            route: RouteSeg::This,
204            segments,
205        }
206    }
207
208    pub fn sub_select_hops(&self) -> Vec<Hop> {
209        let mut hops = self.hops.clone();
210        let query_root_segments = self.query_root().segments.len();
211        for _ in 0..query_root_segments {
212            hops.remove(0);
213        }
214        hops
215    }
216
217    pub fn matches(&self, hierarchy: &PointHierarchy) -> bool
218    where
219        BaseKind: Clone,
220        KindParts: Clone,
221    {
222        if hierarchy.is_root() && self.is_root() {
223            return true;
224        }
225
226        if hierarchy.segments.is_empty() || self.hops.is_empty() {
227            return false;
228        }
229
230        let hop = self.hops.first().expect("hop");
231        let seg = hierarchy.segments.first().expect("segment");
232
233        /*
234                    if hierarchy.segments.len() < self.hops.len() {
235                        // if a hop is 'inclusive' then this will match to true.  We do this for cases like:
236                        // localhost+:**   // Here we want everything under localhost INCLUDING localhost to be matched
237        println!("hop: {}", hop.to_string());
238        println!("seg: {}", seg.to_string());
239                        if hop.inclusive && hop.matches(&seg) {
240                            return true;
241                        } else {
242                            return false;
243                        }
244                    }
245
246                     */
247
248        if hierarchy.is_final() && self.is_final() {
249            // this is the final hop & segment if they match, everything matches!
250            hop.matches(seg)
251        } else if hierarchy.is_root() {
252            false
253        } else if self.is_root() {
254            false
255        } else if hierarchy.is_final() {
256            // we still have hops that haven't been matched and we are all out of path... but we have a weird rule
257            // if a hop is 'inclusive' then this will match to true.  We do this for cases like:
258            // localhost+:**   // Here we want everything under localhost INCLUDING localhost to be matched
259            if hop.inclusive && hop.matches(&seg) {
260                true
261            } else {
262                false
263            }
264        }
265        // special logic is applied to recursives **
266        else if hop.segment_selector.is_recursive() && self.hops.len() >= 2 {
267            // a Recursive is similar to an Any in that it will match anything, however,
268            // it is not consumed until the NEXT segment matches...
269            let next_hop = self.hops.get(1).expect("next<Hop>");
270            if next_hop.matches(seg) {
271                // since the next hop after the recursive matches, we consume the recursive and continue hopping
272                // this allows us to make matches like:
273                // space.org:**:users ~ space.org:many:silly:dirs:users
274                self.consume()
275                    .expect("PointSelector")
276                    .matches(&hierarchy.consume().expect("AddressKindPath"))
277            } else {
278                // the NEXT hop does not match, therefore we do NOT consume() the current hop
279                self.matches(&hierarchy.consume().expect("AddressKindPath"))
280            }
281        } else if hop.segment_selector.is_recursive() && hierarchy.is_final() {
282            hop.matches(hierarchy.segments.last().expect("segment"))
283        } else if hop.segment_selector.is_recursive() {
284            hop.matches(hierarchy.segments.last().expect("segment"))
285                && self.matches(&hierarchy.consume().expect("hierarchy"))
286        } else if hop.matches(seg) {
287            // in a normal match situation, we consume the hop and move to the next one
288            self.consume()
289                .expect("AddressTksPattern")
290                .matches(&hierarchy.consume().expect("AddressKindPath"))
291        } else {
292            false
293        }
294    }
295}
296
297impl ToString for Selector {
298    fn to_string(&self) -> String {
299        let mut rtn = String::new();
300        for (index, hop) in self.hops.iter().enumerate() {
301            rtn.push_str(hop.to_string().as_str());
302            if index < self.hops.len() - 1 {
303                rtn.push_str(":");
304            }
305        }
306        rtn
307    }
308}
309
310#[derive(Debug, Clone, Eq, PartialEq, Hash)]
311pub struct VersionReq {
312    pub version: semver::VersionReq,
313}
314
315impl Deref for VersionReq {
316    type Target = semver::VersionReq;
317
318    fn deref(&self) -> &Self::Target {
319        &self.version
320    }
321}
322
323impl Serialize for VersionReq {
324    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
325    where
326        S: Serializer,
327    {
328        serializer.serialize_str(self.version.to_string().as_str())
329    }
330}
331
332impl<'de> Deserialize<'de> for VersionReq {
333    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
334    where
335        D: Deserializer<'de>,
336    {
337        deserializer.deserialize_str(VersionReqVisitor)
338    }
339}
340
341struct VersionReqVisitor;
342
343impl<'de> Visitor<'de> for VersionReqVisitor {
344    type Value = VersionReq;
345
346    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
347        formatter.write_str("SemVer version requirement")
348    }
349
350    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
351    where
352        E: de::Error,
353    {
354        match VersionReq::from_str(v) {
355            Ok(version) => Ok(version),
356            Err(error) => {
357                //Err(de::Error::custom(error.to_string() ))
358                Err(de::Error::invalid_type(de::Unexpected::Str(v), &self))
359            }
360        }
361    }
362}
363
364impl ToString for VersionReq {
365    fn to_string(&self) -> String {
366        self.version.to_string()
367    }
368}
369
370impl TryInto<semver::VersionReq> for VersionReq {
371    type Error = SpaceErr;
372
373    fn try_into(self) -> Result<semver::VersionReq, Self::Error> {
374        Ok(self.version)
375    }
376}
377
378impl FromStr for VersionReq {
379    type Err = SpaceErr;
380
381    fn from_str(s: &str) -> Result<Self, Self::Err> {
382        let version = semver::VersionReq::from_str(s)?;
383        Ok(Self { version })
384    }
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
388pub enum PointSegSelector {
389    InclusiveAny,       // +:*  // includes Root if it's the first segment
390    InclusiveRecursive, // +:** // includes Root if its the first segment
391    Any,                // *
392    Recursive,          // **
393    Exact(ExactPointSeg),
394    Version(VersionReq),
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
398pub enum PointSegSelectorVar {
399    InclusiveAny,       // +:*  // includes Root if it's the first segment
400    InclusiveRecursive, // +:** // includes Root if its the first segment
401    Any,                // *
402    Recursive,          // **
403    Exact(ExactPointSeg),
404    Version(VersionReq),
405    Var(Variable),
406    Working(Trace),
407    Pop(Trace),
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
411pub enum PointSegSelectorCtx {
412    InclusiveAny,       // +:*  // includes Root if it's the first segment
413    InclusiveRecursive, // +:** // includes Root if its the first segment
414    Any,                // *
415    Recursive,          // **
416    Exact(ExactPointSeg),
417    Version(VersionReq),
418    Working(Trace),
419    Pop(Trace),
420}
421
422impl FromStr for PointSegSelector {
423    type Err = SpaceErr;
424
425    fn from_str(s: &str) -> Result<Self, Self::Err> {
426        result(all_consuming(point_segment_selector)(new_span(s)))
427    }
428}
429
430impl PointSegSelector {
431    pub fn is_exact(&self) -> bool {
432        match self {
433            PointSegSelector::Exact(_) => true,
434            _ => false,
435        }
436    }
437
438    pub fn matches(&self, segment: &PointSeg) -> bool {
439        match self {
440            PointSegSelector::InclusiveAny => true,
441            PointSegSelector::InclusiveRecursive => true,
442            PointSegSelector::Any => true,
443            PointSegSelector::Recursive => true,
444            PointSegSelector::Exact(exact) => {
445                match exact {
446                ExactPointSeg::PointSeg(pattern) => {
447                    pattern.to_string() == segment.to_string()
448                },
449                ExactPointSeg::Version(a) => {
450                    if let PointSeg::Version(b) = segment {
451                        *a == *b
452                    } else {
453                        false
454                    }
455                }
456            }},
457            PointSegSelector::Version(req) => {
458                if let PointSeg::Version(b) = segment {
459                    req.matches(b)
460                } else {
461                    false
462                }
463            }
464        }
465    }
466
467    pub fn is_recursive(&self) -> bool {
468        match self {
469            PointSegSelector::InclusiveAny => false,
470            PointSegSelector::InclusiveRecursive => true,
471            PointSegSelector::Any => false,
472            PointSegSelector::Recursive => true,
473            PointSegSelector::Exact(_) => false,
474            PointSegSelector::Version(_) => false,
475        }
476    }
477}
478
479impl ToString for PointSegSelector {
480    fn to_string(&self) -> String {
481        match self {
482            PointSegSelector::InclusiveAny => "+:*".to_string(),
483            PointSegSelector::InclusiveRecursive => "+:**".to_string(),
484            PointSegSelector::Any => "*".to_string(),
485            PointSegSelector::Recursive => "**".to_string(),
486            PointSegSelector::Exact(exact) => exact.to_string(),
487            PointSegSelector::Version(version) => version.to_string(),
488        }
489    }
490}
491
492pub type KeySegment = String;
493
494#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
495pub enum ExactPointSeg {
496    PointSeg(PointSeg),
497    Version(Version),
498}
499
500impl ExactPointSeg {
501    pub fn matches(&self, segment: &PointSeg) -> bool {
502        match self {
503            ExactPointSeg::PointSeg(s) => *s == *segment,
504            ExactPointSeg::Version(a) => {
505                if let PointSeg::Version(b) = segment {
506                    *a == *b
507                } else {
508                    false
509                }
510            }
511        }
512    }
513}
514
515impl ToString for ExactPointSeg {
516    fn to_string(&self) -> String {
517        match self {
518            ExactPointSeg::PointSeg(point) => point.to_string(),
519            ExactPointSeg::Version(version) => version.to_string(),
520        }
521    }
522}
523
524/// Provides ability to Select on a Specific.  This means wildcards can be applied when any match will do:
525/// `mechtronhub.io:postgres.org:postgres:*:(9.0.0)` will select ANY variant of postgres version 9.0.0.
526/// (notice the version MUST be delimited by Parenthesis.
527/// A more useful example is when performing some type of version selection it follows SemVer Req rules:
528/// `mechtronhub.io:postgres.org:postgres:gis:(>=10.2.3 <12.3.0)`
529/// which would match on any version of postgres:gis with a version in that range
530pub type SpecificSelector = SpecificSelectorDef<
531    ProviderSelector,
532    VendorSelector,
533    ProductSelector,
534    VariantSelector,
535    VersionReq,
536>;
537
538impl FromStr for SpecificSelector {
539    type Err = SpaceErr;
540
541    fn from_str(s: &str) -> Result<Self, Self::Err> {
542        result(all_consuming(specific_selector)(new_span(s)))
543    }
544}
545
546#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
547pub struct SpecificSelectorDef<
548    ProviderSelector,
549    VendorSelector,
550    ProductSelector,
551    VariantSelector,
552    VersionReq,
553> {
554    pub provider: ProviderSelector,
555    pub vendor: VendorSelector,
556    pub product: ProductSelector,
557    pub variant: VariantSelector,
558    pub version: VersionReq,
559}
560
561impl ValueMatcher<Specific> for SpecificSelector {
562    fn is_match(&self, specific: &Specific) -> Result<(), ()> {
563        if self.provider.matches(&specific.provider)
564            && self.vendor.matches(&specific.vendor)
565            && self.product.matches(&specific.product)
566            && self.variant.matches(&specific.variant)
567            && self.version.matches(&specific.version)
568        {
569            Ok(())
570        } else {
571            Err(())
572        }
573    }
574}
575
576impl ToString for SpecificSelector {
577    fn to_string(&self) -> String {
578        format!(
579            "{}:{}:{}:({})",
580            self.vendor.to_string(),
581            self.product.to_string(),
582            self.variant.to_string(),
583            self.version.to_string()
584        )
585    }
586}
587
588pub mod specific {
589    use alloc::string::String;
590    use core::ops::Deref;
591    use core::str::FromStr;
592
593    use crate::err::SpaceErr;
594    use crate::parse::{Domain, SkewerCase};
595    use crate::selector::Pattern;
596
597    pub struct VersionReq {
598        pub req: semver::VersionReq,
599    }
600
601    impl Deref for VersionReq {
602        type Target = semver::VersionReq;
603
604        fn deref(&self) -> &Self::Target {
605            &self.req
606        }
607    }
608
609    impl FromStr for VersionReq {
610        type Err = SpaceErr;
611
612        fn from_str(s: &str) -> Result<Self, Self::Err> {
613            Ok(VersionReq {
614                req: semver::VersionReq::from_str(s)?,
615            })
616        }
617    }
618
619    pub type ProviderSelector = Pattern<Domain>;
620    pub type VendorSelector = Pattern<Domain>;
621    pub type ProductSelector = Pattern<SkewerCase>;
622    pub type VariantSelector = Pattern<SkewerCase>;
623    pub type VersionPattern = Pattern<VersionReq>;
624}
625
626pub type LabeledPrimitiveType = LabeledPrimitiveTypeDef<Point>;
627pub type LabeledPrimitiveTypeCtx = LabeledPrimitiveTypeDef<PointCtx>;
628pub type LabeledPrimitiveTypeVar = LabeledPrimitiveTypeDef<PointVar>;
629
630pub struct LabeledPrimitiveTypeDef<Pnt> {
631    pub label: String,
632    pub def: PayloadType2Def<Pnt>,
633}
634
635pub type PayloadType2 = PayloadType2Def<Point>;
636pub type PayloadType2Ctx = PayloadType2Def<PointCtx>;
637pub type PayloadType2Var = PayloadType2Def<PointVar>;
638
639pub struct PayloadType2Def<Pnt> {
640    pub primitive: SubstanceKind,
641    pub format: Option<SubstanceFormat>,
642    pub verifier: Option<CallWithConfigDef<Pnt>>,
643}
644
645#[derive(Debug, Clone, strum_macros::Display, strum_macros::EnumString, Eq, PartialEq)]
646pub enum Format {
647    #[strum(serialize = "json")]
648    Json,
649    #[strum(serialize = "image")]
650    Image,
651}
652
653#[derive(Debug, Clone, strum_macros::Display, strum_macros::EnumString, Eq, PartialEq)]
654pub enum PipelineKind {
655    Rc,
656    Ext,
657    Http,
658}
659
660pub struct ParsedPipelineBlock {}
661
662pub type MapEntryPatternVar = MapEntryPatternDef<PointVar>;
663pub type MapEntryPatternCtx = MapEntryPatternDef<PointCtx>;
664pub type MapEntryPattern = MapEntryPatternDef<Point>;
665
666#[derive(Clone)]
667pub struct MapEntryPatternDef<Pnt> {
668    pub key: String,
669    pub payload: ValuePattern<SubstancePatternDef<Pnt>>,
670}
671
672pub type Hop = HopDef<PointSegSelector, KindSelector>;
673pub type HopCtx = HopDef<PointSegSelectorCtx, KindSelector>;
674pub type HopVar = HopDef<PointSegSelectorVar, KindSelectorVar>;
675
676#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
677pub struct HopDef<Segment, KindSelector> {
678    pub inclusive: bool,
679    pub segment_selector: Segment,
680    pub kind_selector: KindSelector,
681}
682
683impl Hop {
684    pub fn matches(&self, point_kind_segment: &PointKindSeg) -> bool {
685/*println!("point-kind_segment: {}",point_kind_segment.segment.to_string());
686println!("segment_selector: {}",self.segment_selector.to_string());
687println!("\tmatch {}",self.segment_selector.matches(&point_kind_segment.segment));
688
689 */
690        self.segment_selector.matches(&point_kind_segment.segment)
691            && self.kind_selector.matches(&point_kind_segment.kind)
692    }
693}
694
695impl ToString for Hop {
696    fn to_string(&self) -> String {
697        let mut rtn = String::new();
698        rtn.push_str(self.segment_selector.to_string().as_str());
699
700        if let Pattern::Exact(base) = &self.kind_selector.base {
701            rtn.push_str(format!("<{}", base.to_string()).as_str());
702            if let Pattern::Exact(sub) = &self.kind_selector.sub {
703                rtn.push_str(format!("<{}", sub.as_ref().unwrap().to_string()).as_str());
704                if let ValuePattern::Pattern(specific) = &self.kind_selector.specific {
705                    rtn.push_str(format!("<{}", specific.to_string()).as_str());
706                    rtn.push_str(">");
707                }
708                rtn.push_str(">");
709            }
710            rtn.push_str(">");
711        }
712
713        if self.inclusive {
714            rtn.push_str("+");
715        }
716
717        rtn
718    }
719}
720
721#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
722pub enum Pattern<P> {
723    Any,
724    Exact(P),
725}
726
727impl<I: ToString> Pattern<I> {
728    pub fn to_string_version(self) -> Pattern<String> {
729        match self {
730            Pattern::Any => Pattern::Any,
731            Pattern::Exact(exact) => Pattern::Exact(exact.to_string()),
732        }
733    }
734}
735
736impl<P> Pattern<P>
737where
738    P: Eq + PartialEq,
739{
740    pub fn is_any(&self) -> bool {
741        match self {
742            Pattern::Any => true,
743            Pattern::Exact(_) => false,
744        }
745    }
746
747    pub fn matches(&self, t: &P) -> bool {
748        match self {
749            Self::Any => true,
750            Self::Exact(p) => *p == *t,
751        }
752    }
753    pub fn matches_opt(&self, other: Option<&P>) -> bool {
754        match self {
755            Self::Any => true,
756            Self::Exact(exact) => {
757                if let Option::Some(other) = other {
758                    *exact == *other
759                } else {
760                    false
761                }
762            }
763        }
764    }
765
766    pub fn convert<To>(self) -> Result<Pattern<To>, SpaceErr>
767    where
768        P: TryInto<To, Error = SpaceErr> + Eq + PartialEq,
769    {
770        Ok(match self {
771            Pattern::Any => Pattern::Any,
772            Pattern::Exact(exact) => Pattern::Exact(exact.try_into()?),
773        })
774    }
775}
776
777impl<P> ToString for Pattern<P>
778where
779    P: ToString,
780{
781    fn to_string(&self) -> String {
782        match self {
783            Pattern::Any => "*".to_string(),
784            Pattern::Exact(exact) => exact.to_string(),
785        }
786    }
787}
788
789#[derive(Debug, Clone, Serialize, Deserialize)]
790pub enum EmptyPattern<P> {
791    Any,
792    Pattern(P),
793}
794
795impl<P> EmptyPattern<P>
796where
797    P: Eq + PartialEq,
798{
799    pub fn matches(&self, t: &P) -> bool {
800        match self {
801            Self::Any => true,
802            Self::Pattern(p) => *p == *t,
803        }
804    }
805    pub fn matches_opt(&self, other: Option<&P>) -> bool {
806        match self {
807            Self::Any => true,
808            Self::Pattern(exact) => {
809                if let Option::Some(other) = other {
810                    *exact == *other
811                } else {
812                    false
813                }
814            }
815        }
816    }
817
818    pub fn convert<To>(self) -> Result<EmptyPattern<To>, SpaceErr>
819    where
820        P: TryInto<To, Error = SpaceErr> + Eq + PartialEq,
821    {
822        Ok(match self {
823            EmptyPattern::Any => EmptyPattern::Any,
824            EmptyPattern::Pattern(exact) => EmptyPattern::Pattern(exact.try_into()?),
825        })
826    }
827}
828
829impl Into<EmptyPattern<String>> for EmptyPattern<&str> {
830    fn into(self) -> EmptyPattern<String> {
831        match self {
832            EmptyPattern::Any => EmptyPattern::Any,
833            EmptyPattern::Pattern(f) => EmptyPattern::Pattern(f.to_string()),
834        }
835    }
836}
837
838impl<P> ToString for EmptyPattern<P>
839where
840    P: ToString,
841{
842    fn to_string(&self) -> String {
843        match self {
844            EmptyPattern::Any => "".to_string(),
845            EmptyPattern::Pattern(exact) => exact.to_string(),
846        }
847    }
848}
849
850pub type KindBaseSelector = Pattern<BaseKind>;
851
852#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
853pub struct PortHierarchy {
854    pub topic: Topic,
855    pub layer: Layer,
856    pub point_hierarchy: PointHierarchy,
857}
858
859impl PortHierarchy {
860    pub fn new(point_hierarchy: PointHierarchy, layer: Layer, topic: Topic) -> Self {
861        Self {
862            topic,
863            layer,
864            point_hierarchy,
865        }
866    }
867}
868
869#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
870pub struct PointHierarchy {
871    pub route: RouteSeg,
872    pub segments: Vec<PointKindSeg>,
873}
874
875impl PointHierarchy {
876    pub fn new(route: RouteSeg, segments: Vec<PointKindSeg>) -> Self {
877        Self { route, segments }
878    }
879}
880
881impl PointHierarchy {
882    pub fn push(&self, segment: PointKindSeg) -> PointHierarchy
883    where
884        KindParts: Clone,
885        BaseKind: Clone,
886    {
887        if let PointSeg::Root = segment.segment {
888            println!("pushing ROOT");
889        }
890        let mut segments = self.segments.clone();
891        segments.push(segment);
892        Self {
893            route: self.route.clone(),
894            segments,
895        }
896    }
897}
898
899impl PointHierarchy {
900    pub fn consume(&self) -> Option<PointHierarchy> {
901        if self.segments.len() <= 1 {
902            return Option::None;
903        }
904        let mut segments = self.segments.clone();
905        segments.remove(0);
906        Option::Some(PointHierarchy {
907            route: self.route.clone(),
908            segments,
909        })
910    }
911
912    pub fn is_root(&self) -> bool {
913        self.segments.is_empty()
914    }
915
916    pub fn is_final(&self) -> bool {
917        self.segments.len() == 1
918    }
919}
920
921impl ToString for PointHierarchy {
922    fn to_string(&self) -> String {
923        let mut rtn = String::new();
924        match &self.route {
925            RouteSeg::This => {}
926            route => {
927                rtn.push_str(route.to_string().as_str());
928                rtn.push_str("::");
929            }
930        }
931
932        let mut post_fileroot = false;
933        for (index, segment) in self.segments.iter().enumerate() {
934            if let PointSeg::FilesystemRootDir = segment.segment {
935                post_fileroot = true;
936            }
937            rtn.push_str(segment.segment.preceding_delim(post_fileroot));
938            rtn.push_str(segment.to_string().as_str());
939        }
940
941        rtn
942    }
943}
944
945#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
946pub struct PointKindSeg {
947    pub segment: PointSeg,
948    pub kind: Kind,
949}
950
951impl ToString for PointKindSeg {
952    fn to_string(&self) -> String {
953        format!("{}<{}>", self.segment.to_string(), self.kind.to_string())
954    }
955}
956
957impl FromStr for PointHierarchy {
958    type Err = SpaceErr;
959
960    fn from_str(s: &str) -> Result<Self, Self::Err> {
961        Ok(consume_hierarchy(new_span(s))?)
962    }
963}
964
965pub type PayloadBlock = PayloadBlockDef<Point>;
966pub type PayloadBlockCtx = PayloadBlockDef<PointCtx>;
967pub type PayloadBlockVar = PayloadBlockDef<PointVar>;
968
969impl ToResolved<PayloadBlockCtx> for PayloadBlockVar {
970    fn to_resolved(self, env: &Env) -> Result<PayloadBlockCtx, SpaceErr> {
971        match self {
972            PayloadBlockVar::DirectPattern(block) => Ok(PayloadBlockCtx::DirectPattern(
973                block.modify(move |block| {
974                    let block: SubstancePatternCtx = block.to_resolved(env)?;
975                    Ok(block)
976                })?,
977            )),
978            PayloadBlockVar::ReflectPattern(block) => Ok(PayloadBlockCtx::ReflectPattern(
979                block.modify(move |block| block.to_resolved(env))?,
980            )),
981        }
982    }
983}
984
985#[derive(Debug, Clone, Serialize, Deserialize)]
986pub struct UploadBlock {
987    pub name: String,
988}
989
990#[derive(Debug, Clone, Serialize, Deserialize)]
991pub struct CreateBlock {
992    pub payload: Substance,
993}
994
995pub type PatternBlock = PatternBlockDef<Point>;
996pub type PatternBlockCtx = PatternBlockDef<PointCtx>;
997pub type PatternBlockVar = PatternBlockDef<PointVar>;
998pub type PatternBlockDef<Pnt> = ValuePattern<SubstancePatternDef<Pnt>>;
999
1000impl ToResolved<PatternBlock> for PatternBlockCtx {
1001    fn to_resolved(self, env: &Env) -> Result<PatternBlock, SpaceErr> {
1002        match self {
1003            PatternBlockCtx::Any => Ok(PatternBlock::Any),
1004            PatternBlockCtx::None => Ok(PatternBlock::None),
1005            PatternBlockCtx::Pattern(pattern) => {
1006                Ok(PatternBlock::Pattern(pattern.to_resolved(env)?))
1007            }
1008        }
1009    }
1010}
1011
1012impl ToResolved<PatternBlockCtx> for PatternBlockVar {
1013    fn to_resolved(self, env: &Env) -> Result<PatternBlockCtx, SpaceErr> {
1014        match self {
1015            PatternBlockVar::Any => Ok(PatternBlockCtx::Any),
1016            PatternBlockVar::None => Ok(PatternBlockCtx::None),
1017            PatternBlockVar::Pattern(pattern) => {
1018                Ok(PatternBlockCtx::Pattern(pattern.to_resolved(env)?))
1019            }
1020        }
1021    }
1022}
1023
1024#[derive(Debug, Clone)]
1025pub enum PayloadBlockDef<Pnt> {
1026    DirectPattern(PatternBlockDef<Pnt>),
1027    ReflectPattern(PatternBlockDef<Pnt>),
1028}
1029
1030impl ToResolved<PayloadBlock> for PayloadBlockCtx {
1031    fn to_resolved(self, env: &Env) -> Result<PayloadBlock, SpaceErr> {
1032        match self {
1033            PayloadBlockCtx::DirectPattern(block) => {
1034                Ok(PayloadBlock::DirectPattern(block.modify(move |block| {
1035                    let block: SubstancePattern = block.to_resolved(env)?;
1036                    Ok(block)
1037                })?))
1038            }
1039            PayloadBlockCtx::ReflectPattern(block) => Ok(PayloadBlock::ReflectPattern(
1040                block.modify(move |block| block.to_resolved(env))?,
1041            )),
1042        }
1043    }
1044}
1045
1046impl ToResolved<PayloadBlock> for PayloadBlockVar {
1047    fn to_resolved(self, env: &Env) -> Result<PayloadBlock, SpaceErr> {
1048        let block: PayloadBlockCtx = self.to_resolved(env)?;
1049        block.to_resolved(env)
1050    }
1051}