cosmic_space/wave/
core.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use serde::{Deserialize, Serialize};
5
6use cosmic_macros_primitive::Autobox;
7
8use crate::command::Command;
9use crate::err::StatusErr;
10use crate::loc::ToSurface;
11use crate::substance::FormErrs;
12use crate::util::{ValueMatcher, ValuePattern};
13use crate::wave::core::cmd::CmdMethod;
14use crate::wave::core::ext::ExtMethod;
15use crate::wave::core::http2::{HttpMethod, StatusCode};
16use crate::wave::core::hyp::HypMethod;
17use crate::wave::{Bounce, Ping, Pong, ToRecipients, WaveId};
18use crate::{Bin, SpaceErr, Substance, Surface, ToSubstance};
19use url::Url;
20
21pub mod cmd;
22pub mod ext;
23pub mod http2;
24pub mod hyp;
25
26impl From<Result<ReflectedCore, SpaceErr>> for ReflectedCore {
27    fn from(result: Result<ReflectedCore, SpaceErr>) -> Self {
28        match result {
29            Ok(response) => response,
30            Err(err) => err.into(),
31        }
32    }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
36pub struct ReflectedCore {
37    pub headers: HeaderMap,
38    pub status: StatusCode,
39    pub body: Substance,
40}
41
42impl<S> ToSubstance<S> for ReflectedCore
43where
44    Substance: ToSubstance<S>,
45{
46    fn to_substance(self) -> Result<S, SpaceErr> {
47        self.body.to_substance()
48    }
49
50    fn to_substance_ref(&self) -> Result<&S, SpaceErr> {
51        self.body.to_substance_ref()
52    }
53}
54
55impl ReflectedCore {
56    pub fn to_err(&self) -> SpaceErr {
57        if self.status.is_success() {
58            "cannot convert a success into an error".into()
59        } else {
60            if let Substance::FormErrs(errors) = &self.body {
61                errors.to_cosmic_err()
62            } else {
63                self.status.to_string().into()
64            }
65        }
66    }
67
68    pub fn ok_html(html: &str) -> Self {
69        let bin = Arc::new(html.to_string().into_bytes());
70        ReflectedCore::ok_body(Substance::Bin(bin))
71    }
72
73    pub fn new() -> Self {
74        ReflectedCore {
75            headers: HeaderMap::new(),
76            status: StatusCode::from_u16(200u16).unwrap(),
77            body: Substance::Empty,
78        }
79    }
80
81    pub fn result(result: Result<ReflectedCore, SpaceErr>) -> ReflectedCore {
82        match result {
83            Ok(core) => core,
84            Err(err) => {
85                let mut core = ReflectedCore::status(err.status());
86                core.body = Substance::FormErrs(FormErrs::from(err));
87                core
88            }
89        }
90    }
91
92    pub fn ok() -> Self {
93        Self::ok_body(Substance::Empty)
94    }
95
96    pub fn ok_body(body: Substance) -> Self {
97        Self {
98            headers: HeaderMap::new(),
99            status: StatusCode::from_u16(200u16).unwrap(),
100            body,
101        }
102    }
103
104    pub fn timeout() -> Self {
105        Self {
106            headers: HeaderMap::new(),
107            status: StatusCode::from_u16(408u16).unwrap(),
108            body: Substance::Empty,
109        }
110    }
111
112    pub fn server_error() -> Self {
113        Self {
114            headers: HeaderMap::new(),
115            status: StatusCode::from_u16(500u16).unwrap(),
116            body: Substance::Empty,
117        }
118    }
119
120    pub fn status(status: u16) -> Self {
121        Self {
122            headers: HeaderMap::new(),
123            status: StatusCode::from_u16(status).unwrap_or(StatusCode::from_u16(500).unwrap()),
124            body: Substance::Empty,
125        }
126    }
127
128    pub fn not_found() -> Self {
129        Self {
130            headers: HeaderMap::new(),
131            status: StatusCode::from_u16(404u16).unwrap(),
132            body: Substance::Empty,
133        }
134    }
135
136    pub fn forbidden() -> Self {
137        Self {
138            headers: HeaderMap::new(),
139            status: StatusCode::from_u16(403u16).unwrap(),
140            body: Substance::Empty,
141        }
142    }
143
144    pub fn bad_request() -> Self {
145        Self {
146            headers: HeaderMap::new(),
147            status: StatusCode::from_u16(400u16).unwrap(),
148            body: Substance::Empty,
149        }
150    }
151
152    pub fn fail<S: ToString>(status: u16, message: S) -> Self {
153        let errors = FormErrs::default(message);
154        Self {
155            headers: HeaderMap::new(),
156            status: StatusCode::from_u16(status)
157                .or_else(|_| StatusCode::from_u16(500u16))
158                .unwrap(),
159            body: Substance::FormErrs(errors),
160        }
161    }
162
163    pub fn err(err: SpaceErr) -> Self {
164        let errors = FormErrs::default(err.to_string().as_str());
165        Self {
166            headers: HeaderMap::new(),
167            status: StatusCode::from_u16(err.status())
168                .unwrap_or(StatusCode::from_u16(500u16).unwrap()),
169            body: Substance::FormErrs(errors),
170        }
171    }
172
173    pub fn with_new_substance(self, substance: Substance) -> Self {
174        Self {
175            headers: self.headers,
176            status: self.status,
177            body: substance,
178        }
179    }
180
181    pub fn is_ok(&self) -> bool {
182        self.status.is_success()
183    }
184
185    pub fn into_reflection<P>(self, intended: Surface, to: P, reflection_of: WaveId) -> Pong
186    where
187        P: ToSurface,
188    {
189        Pong {
190            to: to.to_surface(),
191            intended: intended.to_recipients(),
192            core: self,
193            reflection_of: reflection_of,
194        }
195    }
196}
197
198impl ReflectedCore {
199    pub fn as_result<E: From<&'static str>, P: TryFrom<Substance>>(self) -> Result<P, E> {
200        if self.status.is_success() {
201            match P::try_from(self.body) {
202                Ok(substance) => Ok(substance),
203                Err(err) => Err(E::from("error")),
204            }
205        } else {
206            Err(E::from("error"))
207        }
208    }
209
210    pub fn ok_or(&self) -> Result<(), SpaceErr> {
211        if self.is_ok() {
212            Ok(())
213        } else if let  Substance::Err(err) = &self.body {
214            Err(err.clone())
215        }
216        else
217        {
218            Err(SpaceErr::new(self.status.as_u16(), "error"))
219        }
220    }
221}
222
223/*
224impl TryInto<http::response::Builder> for ReflectedCore {
225    type Error = UniErr;
226
227    fn try_into(self) -> Result<http::response::Builder, Self::Error> {
228        let mut builder = http::response::Builder::new();
229
230        for (name, value) in self.headers {
231            match name {
232                Some(name) => {
233                    builder = builder.header(name.as_str(), value.to_str()?.to_string().as_str());
234                }
235                None => {}
236            }
237        }
238
239        Ok(builder.status(self.status))
240    }
241}
242
243impl TryInto<http::Response<Bin>> for ReflectedCore {
244    type Error = UniErr;
245
246    fn try_into(self) -> Result<http::Response<Bin>, Self::Error> {
247        let mut builder = http::response::Builder::new();
248
249        for (name, value) in self.headers {
250            match name {
251                Some(name) => {
252                    builder = builder.header(name.as_str(), value.to_str()?.to_string().as_str());
253                }
254                None => {}
255            }
256        }
257
258        let response = builder.status(self.status).body(self.body.to_bin()?)?;
259        Ok(response)
260    }
261}
262
263 */
264
265#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Autobox)]
266pub enum Method {
267    Hyp(HypMethod),
268    Cmd(CmdMethod),
269    Http(HttpMethod),
270    Ext(ExtMethod),
271}
272
273impl Method {
274    pub fn to_deep_string(&self) -> String {
275        match self {
276            Method::Hyp(x) => format!("Hyp<{}>", x.to_string()),
277            Method::Cmd(x) => format!("Cmd<{}>", x.to_string()),
278            Method::Http(x) => format!("Http<{}>", x.to_string()),
279            Method::Ext(x) => format!("Ext<{}>", x.to_string()),
280        }
281    }
282}
283
284#[derive(Debug, Clone, Eq, PartialEq)]
285pub enum MethodPattern {
286    Hyp(ValuePattern<HypMethod>),
287    Cmd(ValuePattern<CmdMethod>),
288    Http(ValuePattern<HttpMethod>),
289    Ext(ValuePattern<ExtMethod>),
290}
291
292impl ToString for MethodPattern {
293    fn to_string(&self) -> String {
294        match self {
295            MethodPattern::Cmd(c) => {
296                format!("Cmd<{}>", c.to_string())
297            }
298            MethodPattern::Http(c) => {
299                format!("Http<{}>", c.to_string())
300            }
301            MethodPattern::Ext(c) => {
302                format!("Ext<{}>", c.to_string())
303            }
304            MethodPattern::Hyp(c) => {
305                format!("Hyp<{}>", c.to_string())
306            }
307        }
308    }
309}
310
311impl ValueMatcher<Method> for MethodPattern {
312    fn is_match(&self, x: &Method) -> Result<(), ()> {
313        match self {
314            MethodPattern::Hyp(pattern) => {
315                if let Method::Hyp(v) = x {
316                    pattern.is_match(v)
317                } else {
318                    Err(())
319                }
320            }
321            MethodPattern::Cmd(pattern) => {
322                if let Method::Cmd(v) = x {
323                    pattern.is_match(v)
324                } else {
325                    Err(())
326                }
327            }
328            MethodPattern::Http(pattern) => {
329                if let Method::Http(v) = x {
330                    pattern.is_match(v)
331                } else {
332                    Err(())
333                }
334            }
335            MethodPattern::Ext(pattern) => {
336                if let Method::Ext(v) = x {
337                    pattern.is_match(v)
338                } else {
339                    Err(())
340                }
341            }
342        }
343    }
344}
345
346impl ValueMatcher<Method> for Method {
347    fn is_match(&self, x: &Method) -> Result<(), ()> {
348        if x == self {
349            Ok(())
350        } else {
351            Err(())
352        }
353    }
354}
355
356impl Method {
357    pub fn kind(&self) -> MethodKind {
358        match self {
359            Method::Cmd(_) => MethodKind::Cmd,
360            Method::Http(_) => MethodKind::Http,
361            Method::Ext(_) => MethodKind::Ext,
362            Method::Hyp(_) => MethodKind::Hyp,
363        }
364    }
365}
366
367impl ToString for Method {
368    fn to_string(&self) -> String {
369        match self {
370            Method::Cmd(cmd) => format!("Cmd<{}>", cmd.to_string()),
371            Method::Http(method) => format!("Http<{}>", method.to_string()),
372            Method::Ext(msg) => format!("Ext<{}>", msg.to_string()),
373            Method::Hyp(sys) => format!("Hyp<{}>", sys.to_string()),
374        }
375    }
376}
377
378impl Into<DirectedCore> for Method {
379    fn into(self) -> DirectedCore {
380        DirectedCore {
381            headers: Default::default(),
382            method: self,
383            uri: Url::parse("http://localhost/").unwrap(),
384            body: Substance::Empty,
385        }
386    }
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
390pub struct DirectedCore {
391    pub headers: HeaderMap,
392    pub method: Method,
393    pub uri: Url,
394    pub body: Substance,
395}
396
397impl<S> ToSubstance<S> for DirectedCore
398where
399    Substance: ToSubstance<S>,
400{
401    fn to_substance(self) -> Result<S, SpaceErr> {
402        self.body.to_substance()
403    }
404
405    fn to_substance_ref(&self) -> Result<&S, SpaceErr> {
406        self.body.to_substance_ref()
407    }
408}
409
410impl DirectedCore {
411    pub fn new(method: Method) -> Self {
412        Self {
413            method,
414            headers: HeaderMap::new(),
415            uri: Url::parse("http://localhost/").unwrap(),
416            body: Default::default(),
417        }
418    }
419
420    pub fn to_selection_str(&self) -> String {
421        format!(
422            "{}{} -[{}]->",
423            self.method.to_deep_string(),
424            self.uri.path(),
425            self.body.kind().to_string()
426        )
427    }
428
429    pub fn ext<M: Into<ExtMethod>>(method: M) -> Self {
430        let method: ExtMethod = method.into();
431        let method: Method = method.into();
432        Self::new(method)
433    }
434
435    pub fn http<M: Into<HttpMethod>>(method: M) -> Self {
436        let method: HttpMethod = method.into();
437        let method: Method = method.into();
438        Self::new(method)
439    }
440
441    pub fn cmd<M: Into<CmdMethod>>(method: M) -> Self {
442        let method: CmdMethod = method.into();
443        let method: Method = method.into();
444        Self::new(method)
445    }
446}
447
448impl TryFrom<Ping> for DirectedCore {
449    type Error = SpaceErr;
450
451    fn try_from(request: Ping) -> Result<Self, Self::Error> {
452        Ok(request.core)
453    }
454}
455
456impl DirectedCore {
457    pub fn kind(&self) -> MethodKind {
458        self.method.kind()
459    }
460}
461
462impl Into<DirectedCore> for Command {
463    fn into(self) -> DirectedCore {
464        DirectedCore {
465            body: Substance::Command(Box::new(self)),
466            method: Method::Cmd(CmdMethod::Command),
467            ..Default::default()
468        }
469    }
470}
471
472/*
473impl TryFrom<http::Request<Bin>> for DirectedCore {
474    type Error = UniErr;
475
476    fn try_from(request: http::Request<Bin>) -> Result<Self, Self::Error> {
477        Ok(Self {
478            headers: request.headers().clone(),
479            method: Method::Http(request.method().clone().try_into()?),
480            uri: request.uri().clone(),
481            body: Substance::Bin(request.body().clone()),
482        })
483    }
484}
485
486impl TryInto<http::Request<Bin>> for DirectedCore {
487    type Error = UniErr;
488
489    fn try_into(self) -> Result<http::Request<Bin>, UniErr> {
490        let mut builder = http::Request::builder();
491        for (name, value) in self.headers {
492            match name {
493                Some(name) => {
494                    builder = builder.header(name.as_str(), value.to_str()?.to_string().as_str());
495                }
496                None => {}
497            }
498        }
499        match self.method {
500            Method::Http(method) => {
501                builder = builder.method(method).uri(self.uri);
502                Ok(builder.body(self.body.to_bin()?)?)
503            }
504            _ => Err("cannot convert to http response".into()),
505        }
506    }
507}
508
509 */
510
511impl Default for DirectedCore {
512    fn default() -> Self {
513        Self {
514            headers: Default::default(),
515            method: Method::Http(HttpMethod::Get),
516            uri: Url::parse("http://localhost/").unwrap(),
517            body: Substance::Empty,
518        }
519    }
520}
521
522impl DirectedCore {
523    pub fn with_body(self, body: Substance) -> Self {
524        Self {
525            headers: self.headers,
526            uri: self.uri,
527            method: self.method,
528            body,
529        }
530    }
531
532    pub fn server_error(&self) -> ReflectedCore {
533        ReflectedCore {
534            headers: Default::default(),
535            status: StatusCode::from_u16(500u16).unwrap(),
536            body: Substance::Empty,
537        }
538    }
539
540    pub fn timeout(&self) -> ReflectedCore {
541        ReflectedCore {
542            headers: Default::default(),
543            status: StatusCode::from_u16(408u16).unwrap(),
544            body: Substance::Empty,
545        }
546    }
547
548    pub fn not_found(&self) -> ReflectedCore {
549        ReflectedCore {
550            headers: Default::default(),
551            status: StatusCode::from_u16(404u16).unwrap(),
552            body: Substance::Empty,
553        }
554    }
555
556    pub fn forbidden(&self) -> ReflectedCore {
557        ReflectedCore {
558            headers: Default::default(),
559            status: StatusCode::from_u16(403u16).unwrap(),
560            body: Substance::Empty,
561        }
562    }
563
564    pub fn bad_request(&self) -> ReflectedCore {
565        ReflectedCore {
566            headers: Default::default(),
567            status: StatusCode::from_u16(400u16).unwrap(),
568            body: Substance::Empty,
569        }
570    }
571
572    pub fn substance(method: Method, body: Substance) -> DirectedCore {
573        DirectedCore {
574            method,
575            body,
576            ..Default::default()
577        }
578    }
579
580    pub fn ok(&self) -> ReflectedCore {
581        ReflectedCore {
582            headers: Default::default(),
583            status: StatusCode::from_u16(200u16).unwrap(),
584            body: Substance::Empty,
585        }
586    }
587
588    pub fn ok_body(&self, body: Substance) -> ReflectedCore {
589        ReflectedCore {
590            headers: Default::default(),
591            status: StatusCode::from_u16(200u16).unwrap(),
592            body,
593        }
594    }
595
596    pub fn fail<M: ToString>(&self, status: u16, message: M) -> ReflectedCore {
597        let errors = FormErrs::default(message.to_string().as_str());
598        ReflectedCore {
599            headers: Default::default(),
600            status: StatusCode::from_u16(status)
601                .or_else(|_| StatusCode::from_u16(500u16))
602                .unwrap(),
603            body: Substance::FormErrs(errors),
604        }
605    }
606
607    pub fn err<E: StatusErr>(&self, error: E) -> ReflectedCore {
608        let errors = FormErrs::default(error.message().as_str());
609        let status = match StatusCode::from_u16(error.status()) {
610            Ok(status) => status,
611            Err(_) => StatusCode::from_u16(500u16).unwrap(),
612        };
613        println!("----->   returning STATUS of {}", status.to_string());
614        ReflectedCore {
615            headers: Default::default(),
616            status,
617            body: Substance::FormErrs(errors),
618        }
619    }
620}
621
622impl From<()> for ReflectedCore {
623    fn from(_: ()) -> Self {
624        ReflectedCore::ok()
625    }
626}
627
628impl Into<ReflectedCore> for Surface {
629    fn into(self) -> ReflectedCore {
630        ReflectedCore::ok_body(Substance::Surface(self))
631    }
632}
633
634impl TryFrom<ReflectedCore> for Surface {
635    type Error = SpaceErr;
636
637    fn try_from(core: ReflectedCore) -> Result<Self, Self::Error> {
638        if !core.status.is_success() {
639            Err(SpaceErr::new(core.status.as_u16(), "error"))
640        } else {
641            match core.body {
642                Substance::Surface(surface) => Ok(surface),
643                substance => Err(format!(
644                    "expecting Surface received {}",
645                    substance.kind().to_string()
646                )
647                .into()),
648            }
649        }
650    }
651}
652
653pub type CoreBounce = Bounce<ReflectedCore>;
654
655#[derive(
656    Debug,
657    Clone,
658    Serialize,
659    Deserialize,
660    strum_macros::Display,
661    strum_macros::EnumString,
662    Eq,
663    PartialEq,
664)]
665pub enum MethodKind {
666    Hyp,
667    Cmd,
668    Ext,
669    Http,
670}
671
672impl ValueMatcher<MethodKind> for MethodKind {
673    fn is_match(&self, x: &MethodKind) -> Result<(), ()> {
674        if self == x {
675            Ok(())
676        } else {
677            Err(())
678        }
679    }
680}
681
682pub type HeaderMap = HashMap<String, String>;