cosmic_space/
substance.rs

1use core::str::FromStr;
2use nom::ExtendInto;
3use std::collections::HashMap;
4use std::ops::{Deref, DerefMut};
5use std::sync::Arc;
6
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9
10use cosmic_macros_primitive::Autobox;
11use cosmic_nom::Tw;
12
13use crate::command::{Command, RawCommand};
14use crate::err::ParseErrs;
15use crate::hyper::{Greet, HyperSubstance, Knock, ParticleLocation};
16use crate::loc::Meta;
17use crate::log::{AuditLog, Log, LogSpan, LogSpanEvent, PointlessLog};
18use crate::parse::model::Subst;
19use crate::parse::Env;
20use crate::particle::Particle;
21use crate::util::{ToResolved, ValueMatcher, ValuePattern};
22use crate::wave::core::cmd::CmdMethod;
23use crate::wave::core::ext::ExtMethod;
24use crate::wave::core::http2::HttpMethod;
25use crate::wave::core::hyp::HypMethod;
26use crate::wave::core::{DirectedCore, HeaderMap, ReflectedCore};
27use crate::wave::{Pong, UltraWave};
28use crate::{Details, SpaceErr, Status, Stub, Surface, util};
29use url::Url;
30use crate::point::{Point, PointCtx, PointVar};
31
32#[derive(
33    Debug,
34    Clone,
35    Serialize,
36    Deserialize,
37    Eq,
38    PartialEq,
39    strum_macros::Display,
40    strum_macros::EnumString,
41)]
42pub enum SubstanceKind {
43    Empty,
44    List,
45    Map,
46    Point,
47    Surface,
48    Text,
49    Boolean,
50    Int,
51    Meta,
52    Bin,
53    Stub,
54    Details,
55    Status,
56    Particle,
57    Location,
58    FormErrs,
59    Json,
60    MultipartForm,
61    RawCommand,
62    Command,
63    DirectedCore,
64    ReflectedCore,
65    Hyp,
66    Token,
67    UltraWave,
68    Knock,
69    Greet,
70    Log,
71    Err,
72}
73
74#[derive(
75    Debug,
76    Clone,
77    Serialize,
78    Deserialize,
79    Eq,
80    PartialEq,
81    strum_macros::Display,
82    Autobox,
83    cosmic_macros_primitive::ToSubstance,
84)]
85pub enum Substance {
86    Empty,
87    List(SubstanceList),
88    Map(SubstanceMap),
89    Point(Point),
90    Surface(Surface),
91    Text(String),
92    Stub(Stub),
93    Details(Details),
94    Meta(Meta),
95    Bin(Bin),
96    Boolean(bool),
97    Int(i64),
98    Status(Status),
99    Particle(Particle),
100    Location(ParticleLocation),
101    RawCommand(RawCommand),
102    Command(Box<Command>),
103    FormErrs(FormErrs),
104    Json(Value),
105    MultipartForm(MultipartForm),
106    DirectedCore(Box<DirectedCore>),
107    ReflectedCore(Box<ReflectedCore>),
108    Hyper(HyperSubstance),
109    Token(Token),
110    UltraWave(Box<UltraWave>),
111    Knock(Knock),
112    Greet(Greet),
113    Log(LogSubstance),
114    Err(SpaceErr),
115}
116
117impl Substance {
118    pub fn ultrawave(&self) -> Option<&UltraWave> {
119        if let Substance::UltraWave(wave) = self {
120            Some(wave.as_ref())
121        } else {
122            None
123        }
124    }
125
126    pub fn ultrawave_mut(&mut self) -> Option<&mut UltraWave> {
127        if let Substance::UltraWave(wave) = self {
128            Some(wave.as_mut())
129        } else {
130            None
131        }
132    }
133}
134
135pub trait ToSubstance<S> {
136    fn to_substance(self) -> Result<S, SpaceErr>;
137    fn to_substance_ref(&self) -> Result<&S, SpaceErr>;
138}
139
140pub trait ChildSubstance {}
141
142#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
143pub struct Token {
144    data: String,
145}
146
147impl Token {
148    pub fn new<D: ToString>(data: D) -> Self {
149        Self {
150            data: data.to_string(),
151        }
152    }
153
154    pub fn new_uuid() -> Self {
155        Self::new(util::uuid())
156    }
157}
158
159impl Deref for Token {
160    type Target = String;
161
162    fn deref(&self) -> &Self::Target {
163        &self.data
164    }
165}
166
167impl ToString for Token {
168    fn to_string(&self) -> String {
169        self.data.clone()
170    }
171}
172
173impl FromStr for Token {
174    type Err = SpaceErr;
175
176    fn from_str(s: &str) -> Result<Self, Self::Err> {
177        Ok(Token::new(s))
178    }
179}
180
181impl TryFrom<Pong> for Token {
182    type Error = SpaceErr;
183
184    fn try_from(response: Pong) -> Result<Self, Self::Error> {
185        response.core.body.try_into()
186    }
187}
188
189pub trait ToRequestCore {
190    type Method;
191    fn to_request_core(self) -> DirectedCore;
192}
193
194impl Default for Substance {
195    fn default() -> Self {
196        Substance::Empty
197    }
198}
199
200impl Substance {
201    pub fn to_text(self) -> Result<String, SpaceErr> {
202        if let Substance::Text(text) = self {
203            Ok(text)
204        } else {
205            Err("not a 'Text' payload".into())
206        }
207    }
208
209    pub fn is_some(&self) -> bool {
210        if let Self::Empty = self {
211            false
212        } else {
213            true
214        }
215    }
216
217    pub fn from_bin(bin: Bin) -> Self {
218        Self::Bin(bin)
219    }
220
221    pub fn kind(&self) -> SubstanceKind {
222        match self {
223            Substance::Empty => SubstanceKind::Empty,
224            Substance::List(_) => SubstanceKind::List,
225            Substance::Map(_) => SubstanceKind::Map,
226            Substance::Point(_) => SubstanceKind::Point,
227            Substance::Text(_) => SubstanceKind::Text,
228            Substance::Stub(_) => SubstanceKind::Stub,
229            Substance::Meta(_) => SubstanceKind::Meta,
230            Substance::Bin(_) => SubstanceKind::Bin,
231            Substance::Boolean(_) => SubstanceKind::Boolean,
232            Substance::Int(_) => SubstanceKind::Int,
233            Substance::Status(_) => SubstanceKind::Status,
234            Substance::Particle(_) => SubstanceKind::Particle,
235            Substance::FormErrs(_) => SubstanceKind::FormErrs,
236            Substance::Json(_) => SubstanceKind::Json,
237            Substance::RawCommand(_) => SubstanceKind::RawCommand,
238            Substance::Surface(_) => SubstanceKind::Surface,
239            Substance::Command(_) => SubstanceKind::Command,
240            Substance::DirectedCore(_) => SubstanceKind::DirectedCore,
241            Substance::ReflectedCore(_) => SubstanceKind::ReflectedCore,
242            Substance::Hyper(_) => SubstanceKind::Hyp,
243            Substance::MultipartForm(_) => SubstanceKind::MultipartForm,
244            Substance::Token(_) => SubstanceKind::Token,
245            Substance::UltraWave(_) => SubstanceKind::UltraWave,
246            Substance::Knock(_) => SubstanceKind::Knock,
247            Substance::Greet(_) => SubstanceKind::Greet,
248            Substance::Details(_) => SubstanceKind::Details,
249            Substance::Location(_) => SubstanceKind::Location,
250            Substance::Log(_) => SubstanceKind::Log,
251            Substance::Err(_) => SubstanceKind::Err,
252        }
253    }
254
255    pub fn to_bin(self) -> Result<Bin, SpaceErr> {
256        match self {
257            Substance::Empty => Ok(Arc::new(vec![])),
258            Substance::List(list) => list.to_bin(),
259            Substance::Map(map) => map.to_bin(),
260            Substance::Bin(bin) => Ok(bin),
261            Substance::Text(text) => Ok(Arc::new(text.as_bytes().to_vec())),
262            what => Err(format!("{}.to_bin() not supported", what.kind().to_string()).into()),
263        }
264    }
265}
266
267#[derive(
268    Debug,
269    Clone,
270    Serialize,
271    Deserialize,
272    Eq,
273    PartialEq,
274    Autobox,
275    cosmic_macros_primitive::ToSubstance,
276)]
277pub enum LogSubstance {
278    Log(Log),
279    Span(LogSpan),
280    Event(LogSpanEvent),
281    Audit(AuditLog),
282    Pointless(PointlessLog),
283}
284
285impl TryInto<HashMap<String, Substance>> for Substance {
286    type Error = SpaceErr;
287
288    fn try_into(self) -> Result<HashMap<String, Substance>, Self::Error> {
289        match self {
290            Substance::Map(map) => Ok(map.map),
291            _ => Err("Substance type must a Map".into()),
292        }
293    }
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
297pub struct SubstanceMap {
298    pub map: HashMap<String, Substance>,
299}
300
301impl Deref for SubstanceMap {
302    type Target = HashMap<String, Substance>;
303
304    fn deref(&self) -> &Self::Target {
305        &self.map
306    }
307}
308
309impl DerefMut for SubstanceMap {
310    fn deref_mut(&mut self) -> &mut Self::Target {
311        &mut self.map
312    }
313}
314
315impl Default for SubstanceMap {
316    fn default() -> Self {
317        Self {
318            map: Default::default(),
319        }
320    }
321}
322
323impl SubstanceMap {
324    /*
325    pub fn new(constraints: MapConstraints<KEY,ADDRESS,IDENTIFIER,KIND> ) -> Self {
326        Self{
327    //        constraints,
328            map: HashMap::new()
329        }
330    }
331
332     */
333    pub fn to_bin(self) -> Result<Bin, SpaceErr> {
334        Ok(Arc::new(bincode::serialize(&self)?))
335    }
336
337    pub fn new() -> Self {
338        Self {
339            map: HashMap::new(),
340        }
341    }
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
345pub struct FormErrs {
346    map: HashMap<String, String>,
347}
348
349impl FormErrs {
350    pub fn to_cosmic_err(&self) -> SpaceErr {
351        SpaceErr::new(500, self.to_string().as_str())
352    }
353
354    pub fn empty() -> Self {
355        Self {
356            map: HashMap::new(),
357        }
358    }
359
360    pub fn default<S: ToString>(message: S) -> Self {
361        let mut map = HashMap::new();
362        map.insert("default".to_string(), message.to_string());
363        Self { map }
364    }
365}
366
367impl From<SpaceErr> for FormErrs {
368    fn from(err: SpaceErr) -> Self {
369        match err {
370            SpaceErr::Status { status, message } => {
371                Self::default(format!("{} {}", status, message).as_str())
372            }
373            SpaceErr::ParseErrs(_) => Self::default("500: parse error"),
374        }
375    }
376}
377
378impl ToString for FormErrs {
379    fn to_string(&self) -> String {
380        let mut rtn = String::new();
381        for (index, (_, value)) in self.iter().enumerate() {
382            rtn.push_str(value.as_str());
383            if index == self.len() - 1 {
384                rtn.push_str("\n");
385            }
386        }
387        rtn
388    }
389}
390
391impl Deref for FormErrs {
392    type Target = HashMap<String, String>;
393
394    fn deref(&self) -> &Self::Target {
395        &self.map
396    }
397}
398
399#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
400pub struct SubstanceList {
401    pub list: Vec<Box<Substance>>,
402}
403
404impl ToString for SubstanceList {
405    fn to_string(&self) -> String {
406        "[]".to_string()
407    }
408}
409
410impl SubstanceList {
411    pub fn new() -> Self {
412        Self { list: vec![] }
413    }
414    pub fn to_bin(self) -> Result<Bin, SpaceErr> {
415        Ok(Arc::new(bincode::serialize(&self)?))
416    }
417}
418
419impl Deref for SubstanceList {
420    type Target = Vec<Box<Substance>>;
421
422    fn deref(&self) -> &Self::Target {
423        &self.list
424    }
425}
426
427impl DerefMut for SubstanceList {
428    fn deref_mut(&mut self) -> &mut Self::Target {
429        &mut self.list
430    }
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
434pub struct ListPattern {
435    pub primitive: SubstanceKind,
436    pub range: NumRange,
437}
438
439impl ListPattern {
440    pub fn is_match(&self, list: &SubstanceList) -> Result<(), SpaceErr> {
441        /*
442        for i in &list.list {
443            if self.primitive != i.primitive_type() {
444                return Err(format!(
445                    "Primitive List expected: {} found: {}",
446                    self.primitive.to_string(),
447                    i.primitive_type().to_string()
448                )
449                .into());
450            }
451        }
452
453        Ok(())
454
455         */
456        unimplemented!()
457    }
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
461pub enum NumRange {
462    MinMax { min: usize, max: usize },
463    Exact(usize),
464    Any,
465}
466
467pub type SubstanceTypePatternCtx = SubstanceTypePatternDef<PointCtx>;
468pub type SubstanceTypePatternVar = SubstanceTypePatternDef<PointVar>;
469
470#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
471pub enum SubstanceTypePatternDef<Pnt> {
472    Empty,
473    Primitive(SubstanceKind),
474    List(ListPattern),
475    Map(Box<MapPatternDef<Pnt>>),
476}
477
478impl ToResolved<SubstanceTypePatternDef<Point>> for SubstanceTypePatternDef<PointCtx> {
479    fn to_resolved(self, env: &Env) -> Result<SubstanceTypePatternDef<Point>, SpaceErr> {
480        match self {
481            SubstanceTypePatternDef::Empty => Ok(SubstanceTypePatternDef::Empty),
482            SubstanceTypePatternDef::Primitive(payload_type) => {
483                Ok(SubstanceTypePatternDef::Primitive(payload_type))
484            }
485            SubstanceTypePatternDef::List(list) => Ok(SubstanceTypePatternDef::List(list)),
486            SubstanceTypePatternDef::Map(map) => {
487                Err("MapPatternCtx resolution not supported yet...".into())
488            }
489        }
490    }
491}
492
493impl ToResolved<SubstanceTypePatternCtx> for SubstanceTypePatternVar {
494    fn to_resolved(self, env: &Env) -> Result<SubstanceTypePatternCtx, SpaceErr> {
495        match self {
496            SubstanceTypePatternVar::Empty => Ok(SubstanceTypePatternCtx::Empty),
497            SubstanceTypePatternVar::Primitive(payload_type) => {
498                Ok(SubstanceTypePatternCtx::Primitive(payload_type))
499            }
500            SubstanceTypePatternVar::List(list) => Ok(SubstanceTypePatternCtx::List(list)),
501            SubstanceTypePatternVar::Map(map) => {
502                Err("MapPatternCtx resolution not supported yet...".into())
503            }
504        }
505    }
506}
507
508impl<Pnt> SubstanceTypePatternDef<Pnt> {
509    pub fn is_match(&self, payload: &Substance) -> Result<(), ()> {
510        unimplemented!();
511        /*
512        match self {
513            SubstanceTypePattern::Empty => {
514                if payload.payload_type() == SubstanceType::Empty {
515                    Ok(())
516                } else {
517                    Err(format!(
518                        "Substance expected: Empty found: {}",
519                        payload.payload_type().to_string()
520                    )
521                    .into())
522                }
523            }
524            SubstanceTypePattern::Primitive(expected) => {
525                if let Substance::Primitive(found) = payload {
526                    if *expected == found.primitive_type() {
527                        Ok(())
528                    } else {
529                        Err(format!(
530                            "Substance Primitive expected: {} found: {}",
531                            expected.to_string(),
532                            found.primitive_type().to_string()
533                        )
534                        .into())
535                    }
536                } else {
537                    Err(format!(
538                        "Substance expected: {} found: {}",
539                        expected.to_string(),
540                        payload.payload_type().to_string()
541                    )
542                    .into())
543                }
544            }
545            SubstanceTypePattern::List(expected) => {
546                if let Substance::List(found) = payload {
547                    expected.is_match(found)
548                } else {
549                    Err(format!(
550                        "Substance expected: List found: {}",
551                        payload.payload_type().to_string()
552                    )
553                    .into())
554                }
555            }
556            SubstanceTypePattern::Map(expected) => {
557                if let Substance::Map(found) = payload {
558                    expected.is_match(found)
559                } else {
560                    Err(format!(
561                        "Substance expected: {} found: {}",
562                        expected.to_string(),
563                        payload.payload_type().to_string()
564                    )
565                    .into())
566                }
567            }
568        }
569
570         */
571    }
572}
573
574pub type SubstancePatternVar = SubstancePatternDef<PointVar>;
575pub type SubstancePatternCtx = SubstancePatternDef<PointCtx>;
576pub type SubstancePattern = SubstancePatternDef<Point>;
577
578#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
579pub struct SubstancePatternDef<Pnt> {
580    pub structure: SubstanceTypePatternDef<Pnt>,
581    pub format: Option<SubstanceFormat>,
582    pub validator: Option<CallWithConfigDef<Pnt>>,
583}
584
585impl ToResolved<SubstancePatternCtx> for SubstancePatternVar {
586    fn to_resolved(self, env: &Env) -> Result<SubstancePatternCtx, SpaceErr> {
587        let mut errs = vec![];
588        let structure = match self.structure.to_resolved(env) {
589            Ok(structure) => Some(structure),
590            Err(err) => {
591                errs.push(err);
592                None
593            }
594        };
595        let validator = match self.validator {
596            None => None,
597            Some(validator) => match validator.to_resolved(env) {
598                Ok(validator) => Some(validator),
599                Err(err) => {
600                    errs.push(err);
601                    None
602                }
603            },
604        };
605
606        if errs.is_empty() {
607            Ok(SubstancePatternCtx {
608                structure: structure.expect("structure"),
609                validator: validator,
610                format: self.format,
611            })
612        } else {
613            Err(ParseErrs::fold(errs).into())
614        }
615    }
616}
617
618impl ToResolved<SubstancePattern> for SubstancePatternCtx {
619    fn to_resolved(self, resolver: &Env) -> Result<SubstancePattern, SpaceErr> {
620        let mut errs = vec![];
621        let structure = match self.structure.to_resolved(resolver) {
622            Ok(structure) => Some(structure),
623            Err(err) => {
624                errs.push(err);
625                None
626            }
627        };
628        let validator = match self.validator {
629            None => None,
630            Some(validator) => match validator.to_resolved(resolver) {
631                Ok(validator) => Some(validator),
632                Err(err) => {
633                    errs.push(err);
634                    None
635                }
636            },
637        };
638
639        if errs.is_empty() {
640            Ok(SubstancePattern {
641                structure: structure.expect("structure"),
642                validator: validator,
643                format: self.format,
644            })
645        } else {
646            Err(ParseErrs::fold(errs).into())
647        }
648    }
649}
650
651impl<Pnt> ValueMatcher<Substance> for SubstancePatternDef<Pnt> {
652    fn is_match(&self, payload: &Substance) -> Result<(), ()> {
653        self.structure.is_match(&payload)?;
654
655        // more matching to come... not sure exactly how to match Format and Validation...
656        Ok(())
657    }
658}
659
660#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
661pub struct CallWithConfigDef<Pnt> {
662    pub call: CallDef<Pnt>,
663    pub config: Option<Pnt>,
664}
665
666pub type CallWithConfig = CallWithConfigDef<Point>;
667pub type CallWithConfigCtx = CallWithConfigDef<PointCtx>;
668pub type CallWithConfigVar = CallWithConfigDef<PointVar>;
669
670impl ToResolved<CallWithConfigCtx> for CallWithConfigVar {
671    fn to_resolved(self, resolver: &Env) -> Result<CallWithConfigCtx, SpaceErr> {
672        let mut errs = vec![];
673        let call = match self.call.to_resolved(resolver) {
674            Ok(call) => Some(call),
675            Err(err) => {
676                errs.push(err);
677                None
678            }
679        };
680        let config = match self.config {
681            None => None,
682            Some(config) => match config.to_resolved(resolver) {
683                Ok(config) => Some(config),
684                Err(err) => {
685                    errs.push(err);
686                    None
687                }
688            },
689        };
690
691        if errs.is_empty() {
692            Ok(CallWithConfigCtx {
693                call: call.expect("call"),
694                config,
695            })
696        } else {
697            Err(ParseErrs::fold(errs).into())
698        }
699    }
700}
701
702impl ToResolved<CallWithConfig> for CallWithConfigCtx {
703    fn to_resolved(self, resolver: &Env) -> Result<CallWithConfig, SpaceErr> {
704        let mut errs = vec![];
705        let call = match self.call.to_resolved(resolver) {
706            Ok(call) => Some(call),
707            Err(err) => {
708                errs.push(err);
709                None
710            }
711        };
712        let config = match self.config {
713            None => None,
714            Some(config) => match config.to_resolved(resolver) {
715                Ok(config) => Some(config),
716                Err(err) => {
717                    errs.push(err);
718                    None
719                }
720            },
721        };
722
723        if errs.is_empty() {
724            Ok(CallWithConfig {
725                call: call.expect("call"),
726                config,
727            })
728        } else {
729            Err(ParseErrs::fold(errs).into())
730        }
731    }
732}
733
734pub type Call = CallDef<Point>;
735pub type CallCtx = CallDef<PointCtx>;
736pub type CallVar = CallDef<PointVar>;
737
738impl ToResolved<Call> for CallCtx {
739    fn to_resolved(self, env: &Env) -> Result<Call, SpaceErr> {
740        Ok(Call {
741            point: self.point.to_resolved(env)?,
742            kind: self.kind,
743        })
744    }
745}
746
747impl ToResolved<CallCtx> for CallVar {
748    fn to_resolved(self, env: &Env) -> Result<CallCtx, SpaceErr> {
749        Ok(CallCtx {
750            point: self.point.to_resolved(env)?,
751            kind: self.kind,
752        })
753    }
754}
755
756impl ToResolved<Call> for CallVar {
757    fn to_resolved(self, env: &Env) -> Result<Call, SpaceErr> {
758        let call: CallCtx = self.to_resolved(env)?;
759        call.to_resolved(env)
760    }
761}
762
763#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
764pub struct CallDef<Pnt> {
765    pub point: Pnt,
766    pub kind: CallKind,
767}
768
769#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
770pub enum CallKind {
771    Cmd(CmdCall),
772    Hyp(HypCall),
773    Ext(ExtCall),
774    Http(HttpCall),
775}
776
777impl CallKind {
778    /*
779    pub fn core_with_body(self, body: Substance) -> Result<RequestCore, ExtErr> {
780        Ok(match self {
781            CallKind::Ext(msg) => RequestCore {
782                headers: Default::default(),
783                method: Method::Ext(ExtMethod::new(msg.method)?),
784                uri: Uri::from_str(msg.path.as_str())?,
785                body,
786            },
787            CallKind::Http(http) => RequestCore {
788                headers: Default::default(),
789                method: Method::Http(http.method),
790                uri: Uri::from_str(http.path.as_str())?,
791                body,
792            },
793        })
794    }
795     */
796}
797
798impl ToString for Call {
799    fn to_string(&self) -> String {
800        format!("{}^{}", self.point.to_string(), self.kind.to_string())
801    }
802}
803
804#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
805pub struct ExtCall {
806    pub path: Subst<Tw<String>>,
807    pub method: ExtMethod,
808}
809
810impl ExtCall {
811    pub fn new(method: ExtMethod, path: Subst<Tw<String>>) -> Self {
812        Self { method, path }
813    }
814}
815
816impl ToString for ExtCall {
817    fn to_string(&self) -> String {
818        format!("Ext<{}>{}", self.method.to_string(), self.path.to_string())
819    }
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
823pub struct CmdCall {
824    pub path: Subst<Tw<String>>,
825    pub method: CmdMethod,
826}
827
828impl CmdCall {
829    pub fn new(method: CmdMethod, path: Subst<Tw<String>>) -> Self {
830        Self { method, path }
831    }
832}
833
834impl ToString for CmdCall {
835    fn to_string(&self) -> String {
836        format!("Cmd<{}>{}", self.method.to_string(), self.path.to_string())
837    }
838}
839
840#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
841pub struct HypCall {
842    pub path: Subst<Tw<String>>,
843    pub method: HypMethod,
844}
845
846impl HypCall {
847    pub fn new(method: HypMethod, path: Subst<Tw<String>>) -> Self {
848        Self { method, path }
849    }
850}
851
852impl ToString for HypCall {
853    fn to_string(&self) -> String {
854        format!("Hyp<{}>{}", self.method.to_string(), self.path.to_string())
855    }
856}
857
858#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
859pub struct HttpCall {
860    pub path: Subst<Tw<String>>,
861
862    pub method: HttpMethod,
863}
864
865impl HttpCall {
866    pub fn new(method: HttpMethod, path: Subst<Tw<String>>) -> Self {
867        Self { method, path }
868    }
869}
870
871impl ToString for HttpCall {
872    fn to_string(&self) -> String {
873        format!("Http<{}>{}", self.method.to_string(), self.path.to_string())
874    }
875}
876
877impl ValueMatcher<HttpMethod> for HttpMethod {
878    fn is_match(&self, found: &HttpMethod) -> Result<(), ()> {
879        if *self == *found {
880            Ok(())
881        } else {
882            Err(())
883        }
884    }
885}
886
887impl ToString for CallKind {
888    fn to_string(&self) -> String {
889        match self {
890            CallKind::Ext(msg) => msg.to_string(),
891            CallKind::Http(http) => http.to_string(),
892            CallKind::Cmd(cmd) => cmd.to_string(),
893            CallKind::Hyp(sys) => sys.to_string(),
894        }
895    }
896}
897
898#[derive(
899    Debug,
900    Clone,
901    Eq,
902    PartialEq,
903    strum_macros::Display,
904    strum_macros::EnumString,
905    Serialize,
906    Deserialize,
907)]
908pub enum SubstanceFormat {
909    #[strum(serialize = "json")]
910    Json,
911    #[strum(serialize = "image")]
912    Image,
913}
914
915pub type MapPattern = MapPatternDef<Point>;
916pub type MapPatternCtx = MapPatternDef<PointCtx>;
917pub type MapPatternVar = MapPatternDef<PointVar>;
918
919#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
920pub struct MapPatternDef<Pnt> {
921    pub required: HashMap<String, ValuePattern<SubstancePatternDef<Pnt>>>,
922    pub allowed: ValuePattern<SubstancePatternDef<Pnt>>,
923}
924
925impl<Pnt> Default for MapPatternDef<Pnt> {
926    fn default() -> Self {
927        MapPatternDef {
928            required: Default::default(),
929            allowed: ValuePattern::Any,
930        }
931    }
932}
933
934impl<Pnt> ToString for MapPatternDef<Pnt> {
935    fn to_string(&self) -> String {
936        "Map?".to_string()
937    }
938}
939
940impl<Pnt> MapPatternDef<Pnt> {
941    pub fn new(
942        required: HashMap<String, ValuePattern<SubstancePatternDef<Pnt>>>,
943        allowed: ValuePattern<SubstancePatternDef<Pnt>>,
944    ) -> Self {
945        MapPatternDef { required, allowed }
946    }
947
948    pub fn empty() -> Self {
949        Self {
950            required: HashMap::new(),
951            allowed: ValuePattern::None,
952        }
953    }
954
955    pub fn any() -> Self {
956        Self {
957            required: HashMap::new(),
958            allowed: ValuePattern::Any,
959        }
960    }
961
962    pub fn is_match(&self, map: &SubstanceMap) -> Result<(), ()> {
963        // if Any keys are allowed then skip
964        for (key, payload) in &map.map {
965            if !self.required.contains_key(key) {
966                match &self.allowed {
967                    ValuePattern::Any => {}
968                    ValuePattern::None => {
969                        return Err(());
970                    }
971                    ValuePattern::Pattern(pattern) => {
972                        pattern.is_match(payload)?;
973                    }
974                }
975            }
976        }
977
978        // now make sure all required are present and meet constraints
979        for (key, constraint) in &self.required {
980            if !map.contains_key(key) {
981                return Err(());
982            }
983            constraint.is_match(
984                &map.get(key)
985                    .expect("expected map element after testing for it"),
986            )?;
987        }
988
989        Ok(())
990    }
991}
992
993#[derive(Debug, Clone, Serialize, Deserialize)]
994pub struct SubstanceRef<PAYLOAD_CLAIM, PAYLOAD_PATTERN> {
995    pub claim: PAYLOAD_CLAIM,
996    pub pattern: PAYLOAD_PATTERN,
997}
998
999#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
1000pub struct MultipartForm {
1001    data: String,
1002}
1003
1004impl TryInto<HashMap<String, String>> for MultipartForm {
1005    type Error = SpaceErr;
1006
1007    fn try_into(self) -> Result<HashMap<String, String>, Self::Error> {
1008        let map: HashMap<String, String> = serde_urlencoded::from_str(&self.data)?;
1009        Ok(map)
1010    }
1011}
1012
1013impl ToRequestCore for MultipartForm {
1014    type Method = HttpMethod;
1015
1016    fn to_request_core(self) -> DirectedCore {
1017        let mut headers = HeaderMap::new();
1018
1019        headers.insert(
1020            "Content-Type".to_string(),
1021            "application/x-www-form-urlencoded".to_string(),
1022        );
1023
1024        DirectedCore {
1025            headers,
1026            method: HttpMethod::Post.into(),
1027            uri: Url::parse("/").unwrap(),
1028            body: Substance::MultipartForm(self),
1029        }
1030    }
1031}
1032
1033impl MultipartForm {
1034    pub fn data(&self) -> &str {
1035        self.data.as_str()
1036    }
1037}
1038
1039impl Deref for MultipartForm {
1040    type Target = str;
1041
1042    fn deref(&self) -> &Self::Target {
1043        self.data()
1044    }
1045}
1046
1047impl ToString for MultipartForm {
1048    fn to_string(&self) -> String {
1049        self.data.clone()
1050    }
1051}
1052
1053pub struct MultipartFormBuilder {
1054    map: HashMap<String, String>,
1055}
1056
1057impl MultipartFormBuilder {
1058    pub fn new() -> Self {
1059        Self {
1060            map: HashMap::new(),
1061        }
1062    }
1063
1064    pub fn put<S: ToString>(&mut self, key: S, value: S) {
1065        self.insert(key.to_string(), value.to_string());
1066    }
1067
1068    pub fn get<S: ToString>(&self, key: S) -> Option<&String> {
1069        self.map.get(&key.to_string())
1070    }
1071}
1072
1073impl Deref for MultipartFormBuilder {
1074    type Target = HashMap<String, String>;
1075
1076    fn deref(&self) -> &Self::Target {
1077        &self.map
1078    }
1079}
1080
1081impl DerefMut for MultipartFormBuilder {
1082    fn deref_mut(&mut self) -> &mut Self::Target {
1083        &mut self.map
1084    }
1085}
1086
1087impl MultipartFormBuilder {
1088    pub fn build(self) -> Result<MultipartForm, SpaceErr> {
1089        let data = serde_urlencoded::to_string(&self.map)?;
1090        Ok(MultipartForm { data })
1091    }
1092}
1093
1094pub type Bin = Arc<Vec<u8>>;