1use core::fmt::Formatter;
2use core::str::FromStr;
3use std::collections::HashMap;
4use std::ops::{Deref, DerefMut};
5
6use convert_case::{Case, Casing};
7use nom::combinator::all_consuming;
8use serde::de::{Error, Visitor};
9use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
10
11use cosmic_nom::{new_span, Trace, Tw};
12
13use crate::err::ParseErrs;
14use crate::hyper::ChildRegistry;
15use crate::kind::KindParts;
16use crate::log::{SpanLogger, Trackable};
17use crate::parse::error::result;
18use crate::parse::{
19 CamelCase, consume_point, consume_point_ctx, Domain, Env,
20 kind_parts, parse_star_key, point_and_kind, point_route_segment, point_selector, point_var, ResolverErr,
21 SkewerCase,
22};
23use crate::particle::traversal::TraversalPlan;
24use crate::selector::{Pattern, Selector, SpecificSelector, VersionReq};
25use crate::util::{ToResolved, uuid, ValueMatcher, ValuePattern};
26use crate::wave::exchange::asynch::Exchanger;
27use crate::wave::{
28 DirectedWave, Ping, Pong, Recipients, ReflectedWave, SingularDirectedWave, ToRecipients,
29 UltraWave, Wave,
30};
31use crate::Agent::Anonymous;
32use crate::{Agent, ANONYMOUS, BaseKind, HYPERUSER, Kind, KindTemplate, ParticleRecord, SpaceErr};
33use crate::point::{Point, PointDef, PointSeg, PointSegCtx, PointSegKind, PointSegPairDef, PointSegVar, RouteSeg, RouteSegVar};
34
35lazy_static! {
36 pub static ref CENTRAL: Point = StarKey::central().to_point();
37 pub static ref GLOBAL_LOGGER: Point = Point::from_str("GLOBAL::logger").unwrap();
38 pub static ref GLOBAL_REGISTRY: Point = Point::from_str("GLOBAL::registry").unwrap();
39 pub static ref GLOBAL_EXEC: Point = Point::from_str("GLOBAL::executor").unwrap();
40 pub static ref LOCAL_STAR: Point = Point::from_str("LOCAL::star").unwrap();
41 pub static ref LOCAL_PORTAL: Point = Point::from_str("LOCAL::portal").unwrap();
42 pub static ref LOCAL_HYPERGATE: Point = Point::from_str("LOCAL::hypergate").unwrap();
43 pub static ref LOCAL_ENDPOINT: Point = Point::from_str("LOCAL::endpoint").unwrap();
44 pub static ref REMOTE_ENDPOINT: Point = Point::from_str("REMOTE::endpoint").unwrap();
45 pub static ref STD_WAVE_TRAVERSAL_PLAN: TraversalPlan =
46 TraversalPlan::new(vec![Layer::Field, Layer::Shell, Layer::Core]);
47 pub static ref MECHTRON_WAVE_TRAVERSAL_PLAN: TraversalPlan = TraversalPlan::new(vec![
48 Layer::Field,
49 Layer::Shell,
50 Layer::Portal,
51 Layer::Host,
52 Layer::Guest,
53 Layer::Core
54 ]);
55 pub static ref PORTAL_WAVE_TRAVERSAL_PLAN: TraversalPlan = TraversalPlan::new(vec![
56 Layer::Field,
57 Layer::Shell,
58 Layer::Portal,
59 Layer::Host,
60 Layer::Guest,
61 Layer::Core
62 ]);
63 pub static ref CONTROL_WAVE_TRAVERSAL_PLAN: TraversalPlan = TraversalPlan::new(vec![
64 Layer::Field,
65 Layer::Shell,
66 Layer::Portal,
67 Layer::Host,
68 Layer::Guest,
69 Layer::Core
70 ]);
71 pub static ref STAR_WAVE_TRAVERSAL_PLAN: TraversalPlan =
72 TraversalPlan::new(vec![Layer::Field, Layer::Shell, Layer::Core]);
73}
74
75pub trait ToBaseKind {
76 fn to_base(&self) -> BaseKind;
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
80pub struct Uuid {
81 uuid: String,
82}
83
84impl Uuid {
85 pub fn rnd() -> Self {
86 uuid()
88 }
89 pub fn from<S: ToString>(uuid: S) -> Result<Self, SpaceErr> {
98 Ok(Self {
100 uuid: uuid.to_string(),
101 })
102 }
103
104 pub fn from_unwrap<S: ToString>(uuid: S) -> Self {
105 Self {
106 uuid: uuid.to_string(),
107 }
108 }
109}
110
111impl ToString for Uuid {
112 fn to_string(&self) -> String {
113 self.uuid.clone()
114 }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash, strum_macros::Display)]
118pub enum ProvisionAffinity {
119 Local,
120 Wrangle,
121}
122
123pub type Meta = HashMap<String, String>;
124pub type HostKey = String;
125
126#[derive(Debug, Clone, Eq, PartialEq, Hash)]
127pub struct Version {
128 pub version: semver::Version,
129}
130
131impl Deref for Version {
132 type Target = semver::Version;
133
134 fn deref(&self) -> &Self::Target {
135 &self.version
136 }
137}
138
139impl Serialize for Version {
140 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141 where
142 S: Serializer,
143 {
144 serializer.serialize_str(self.version.to_string().as_str())
145 }
146}
147
148struct VersionVisitor;
149
150impl<'de> Visitor<'de> for VersionVisitor {
151 type Value = Version;
152
153 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
154 formatter.write_str("SemVer version")
155 }
156
157 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
158 where
159 E: de::Error,
160 {
161 match Version::from_str(v) {
162 Ok(version) => Ok(version),
163 Err(error) => {
164 Err(de::Error::invalid_type(de::Unexpected::Str(v), &self))
166 }
167 }
168 }
169}
170
171impl<'de> Deserialize<'de> for Version {
172 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173 where
174 D: Deserializer<'de>,
175 {
176 deserializer.deserialize_str(VersionVisitor)
177 }
178}
179
180impl ToString for Version {
181 fn to_string(&self) -> String {
182 self.version.to_string()
183 }
184}
185
186impl TryInto<semver::Version> for Version {
187 type Error = SpaceErr;
188
189 fn try_into(self) -> Result<semver::Version, Self::Error> {
190 Ok(self.version)
191 }
192}
193
194impl FromStr for Version {
195 type Err = SpaceErr;
196
197 fn from_str(s: &str) -> Result<Self, Self::Err> {
198 let version = semver::Version::from_str(s)?;
199 Ok(Self { version })
200 }
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
204pub struct Variable {
205 pub name: String,
206 pub trace: Trace,
207}
208
209impl Variable {
210 pub fn new(name: String, trace: Trace) -> Self {
211 Self { name, trace }
212 }
213}
214
215pub enum VarVal<V> {
216 Var(Tw<SkewerCase>),
217 Val(Tw<V>),
218}
219
220impl<V> ToResolved<V> for VarVal<V>
221where
222 V: FromStr<Err = SpaceErr>,
223{
224 fn to_resolved(self, env: &Env) -> Result<V, SpaceErr> {
225 match self {
226 VarVal::Var(var) => match env.val(var.as_str()) {
227 Ok(val) => {
228 let val: String = val.clone().try_into()?;
229 Ok(V::from_str(val.as_str())?)
230 }
231 Err(err) => {
232 let trace = var.trace.clone();
233 match err {
234 ResolverErr::NotAvailable => Err(ParseErrs::from_range(
235 "variables not available in this context",
236 "variables not available",
237 trace.range,
238 trace.extra,
239 )),
240 ResolverErr::NotFound => Err(ParseErrs::from_range(
241 format!("variable '{}' not found", var.unwrap().to_string()).as_str(),
242 "not found",
243 trace.range,
244 trace.extra,
245 )),
246 }
247 }
248 },
249 VarVal::Val(val) => Ok(val.unwrap()),
250 }
251 }
252}
253
254pub trait RouteSegQuery {
255 fn is_local(&self) -> bool;
256 fn is_global(&self) -> bool;
257}
258
259pub trait PointSegQuery {
260 fn is_filesystem_root(&self) -> bool;
261 fn kind(&self) -> PointSegKind;
262}
263
264pub trait PointSegment {}
265
266pub type PointSegPair = PointSegPairDef<PointSeg>;
267
268#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
269pub enum Topic {
270 None,
271 Not,
272 Any,
273 Cli,
274 Uuid(Uuid),
275 Path(Vec<SkewerCase>),
276}
277
278impl ToString for Topic {
279 fn to_string(&self) -> String {
280 match self {
281 Topic::None => "".to_string(),
282 Topic::Not => "Topic<!>".to_string(),
283 Topic::Any => "Topic<*>".to_string(),
284 Topic::Uuid(uuid) => format!("Topic<Uuid>({})", uuid.to_string()),
285 Topic::Path(segs) => {
286 let segments: Vec<String> = segs.into_iter().map(|s| s.to_string()).collect();
287 let mut rtn = String::new();
288 for (index, segment) in segments.iter().enumerate() {
289 rtn.push_str(segment.as_str());
290 if index < segments.len() - 1 {
291 rtn.push_str(":")
292 }
293 }
294 return format!("Topic<Path>({})", rtn);
295 }
296 Topic::Cli => "Topic<Cli>".to_string(),
297 }
298 }
299}
300
301impl Topic {
302 pub fn uuid() -> Self {
303 Topic::Uuid(Uuid::rnd())
304 }
305}
306
307impl ValueMatcher<Topic> for Topic {
308 fn is_match(&self, x: &Topic) -> Result<(), ()> {
309 if *x == *self {
310 Ok(())
311 } else {
312 Err(())
313 }
314 }
315}
316
317#[derive(
318 Debug,
319 Clone,
320 Serialize,
321 Deserialize,
322 Eq,
323 PartialEq,
324 Hash,
325 strum_macros::Display,
326 strum_macros::EnumString,
327 Ordinalize,
328)]
329#[repr(u8)]
330pub enum Layer {
331 Gravity = 0,
332 Field,
333 Shell,
334 Portal,
335 Host,
336 Guest,
337 Core,
338}
339
340impl Layer {
341 pub fn has_state(&self) -> bool {
342 match self {
343 Layer::Gravity => false,
344 Layer::Field => true,
345 Layer::Shell => true,
346 Layer::Portal => false,
347 Layer::Host => false,
348 Layer::Guest => false,
349 Layer::Core => false,
350 }
351 }
352}
353
354impl Default for Layer {
355 fn default() -> Self {
356 Layer::Core
357 }
358}
359
360impl Default for Topic {
361 fn default() -> Self {
362 Topic::None
363 }
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
367pub struct Surface {
368 pub point: Point,
369 pub layer: Layer,
370 pub topic: Topic,
371}
372
373impl Surface {
374 pub fn new(point: Point, layer: Layer, topic: Topic) -> Self {
375 Self {
376 point,
377 layer,
378 topic,
379 }
380 }
381
382 fn postfix(&self) -> String {
383 let point = self.clone().to_point();
384 match &self.topic {
385 Topic::None => {
386 format!("{}@{}", self.point.postfix(), self.layer.to_string())
387 }
388 topic => {
389 format!(
390 "{}@{}+{}",
391 self.point.postfix(),
392 self.layer.to_string(),
393 topic.to_string()
394 )
395 }
396 }
397 }
398}
399
400impl Into<Recipients> for Surface {
401 fn into(self) -> Recipients {
402 Recipients::Single(self)
403 }
404}
405
406impl ToRecipients for Surface {
407 fn to_recipients(self) -> Recipients {
408 Recipients::Single(self)
409 }
410}
411
412impl ToString for Surface {
413 fn to_string(&self) -> String {
414 let point = self.clone().to_point();
415 match &self.topic {
416 Topic::None => {
417 format!("{}@{}", self.point.to_string(), self.layer.to_string())
418 }
419 topic => {
420 format!(
421 "{}@{}+{}",
422 self.point.to_string(),
423 self.layer.to_string(),
424 topic.to_string()
425 )
426 }
427 }
428 }
429}
430
431#[derive(Clone, Serialize, Deserialize)]
432pub struct SurfaceSelector {
433 pub point: ValuePattern<Point>,
434 pub topic: ValuePattern<Topic>,
435 pub layer: ValuePattern<Layer>,
436}
437
438impl Into<SurfaceSelector> for Surface {
439 fn into(self) -> SurfaceSelector {
440 let point = ValuePattern::Pattern(self.point);
441 let topic = match self.topic {
442 Topic::None => ValuePattern::Any,
443 Topic::Not => ValuePattern::None,
444 Topic::Any => ValuePattern::Any,
445 Topic::Uuid(uuid) => ValuePattern::Pattern(Topic::Uuid(uuid)),
446 Topic::Path(path) => ValuePattern::Pattern(Topic::Path(path)),
447 Topic::Cli => ValuePattern::Pattern(Topic::Cli),
448 };
449 let layer = ValuePattern::Pattern(self.layer);
450 SurfaceSelector {
451 point,
452 topic,
453 layer,
454 }
455 }
456}
457
458impl ValueMatcher<Surface> for SurfaceSelector {
459 fn is_match(&self, surface: &Surface) -> Result<(), ()> {
460 match &self.point {
461 ValuePattern::Any => {}
462 ValuePattern::None => return Err(()),
463 ValuePattern::Pattern(point) if *point != surface.point => return Err(()),
464 _ => {}
465 }
466
467 match &self.layer {
468 ValuePattern::Any => {}
469 ValuePattern::None => return Err(()),
470 ValuePattern::Pattern(layer) if *layer != surface.layer => return Err(()),
471 _ => {}
472 }
473
474 match &self.topic {
475 ValuePattern::Any => {}
476 ValuePattern::None => return Err(()),
477 ValuePattern::Pattern(topic) if *topic != surface.topic => return Err(()),
478 _ => {}
479 }
480
481 Ok(())
482 }
483}
484
485impl Surface {
486 pub fn with_topic(&self, topic: Topic) -> Self {
487 Self {
488 point: self.point.clone(),
489 layer: self.layer.clone(),
490 topic,
491 }
492 }
493
494 pub fn with_layer(&self, layer: Layer) -> Self {
495 Self {
496 point: self.point.clone(),
497 layer,
498 topic: self.topic.clone(),
499 }
500 }
501}
502
503impl Deref for Surface {
504 type Target = Point;
505
506 fn deref(&self) -> &Self::Target {
507 &self.point
508 }
509}
510
511impl ToPoint for Surface {
512 fn to_point(&self) -> Point {
513 self.point.clone()
514 }
515}
516
517impl ToSurface for Surface {
518 fn to_surface(&self) -> Surface {
519 self.clone()
520 }
521}
522
523pub trait ToPoint {
524 fn to_point(&self) -> Point;
525}
526
527pub trait ToSurface {
528 fn to_surface(&self) -> Surface;
529}
530
531pub type MachineName = String;
532pub type ConstellationName = String;
533
534#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Debug, Clone, Serialize, Deserialize)]
535pub struct StarKey {
536 pub constellation: ConstellationName,
537 pub name: String,
538 pub index: u16,
539}
540
541impl StarKey {
542 pub fn sql_name(&self) -> String {
543 format!(
544 "star_{}_{}_{}",
545 self.constellation.to_lowercase().replace("-", "_"),
546 self.name.to_lowercase().replace("-", "_"),
547 self.index
548 )
549 }
550}
551
552impl Into<Point> for StarKey {
553 fn into(self) -> Point {
554 self.to_point()
555 }
556}
557
558impl Into<Surface> for StarKey {
559 fn into(self) -> Surface {
560 self.to_surface()
561 }
562}
563
564impl TryFrom<Point> for StarKey {
565 type Error = SpaceErr;
566
567 fn try_from(point: Point) -> Result<Self, Self::Error> {
568 match point.route {
569 RouteSeg::Star(star) => StarKey::from_str(star.as_str()),
570 _ => Err("can only extract StarKey from Mesh point routes".into()),
571 }
572 }
573}
574
575impl ToPoint for StarKey {
576 fn to_point(&self) -> Point {
577 Point::from_str(format!("<<{}>>::star", self.to_string()).as_str()).unwrap()
578 }
579}
580
581impl ToSurface for StarKey {
582 fn to_surface(&self) -> Surface {
583 self.clone().to_point().to_surface()
584 }
585}
586
587pub struct StarHandle {
588 pub name: String,
589 pub index: u16,
590}
591
592impl StarHandle {
593 pub fn name<S: ToString>(name: S) -> Self {
594 Self {
595 name: name.to_string(),
596 index: 0,
597 }
598 }
599
600 pub fn new<S: ToString>(name: S, index: u16) -> Self {
601 Self {
602 name: name.to_string(),
603 index,
604 }
605 }
606}
607
608impl StarKey {
609 pub fn new(constellation: &ConstellationName, handle: &StarHandle) -> Self {
610 Self {
611 constellation: constellation.clone(),
612 name: handle.name.clone(),
613 index: handle.index.clone(),
614 }
615 }
616
617 pub fn machine(machine_name: MachineName) -> Self {
618 StarKey::new(
619 &"machine".to_string(),
620 &StarHandle::name(machine_name.as_str()),
621 )
622 }
623
624 pub fn central() -> Self {
625 StarKey {
626 constellation: "central".to_string(),
627 name: "central".to_string(),
628 index: 0,
629 }
630 }
631}
632
633impl ToString for StarKey {
634 fn to_string(&self) -> String {
635 format!("STAR::{}:{}[{}]", self.constellation, self.name, self.index)
636 }
637}
638
639impl StarKey {
640 pub fn to_sql_name(&self) -> String {
641 format!("STAR::{}_{}_{}", self.constellation, self.name, self.index)
642 }
643}
644
645impl FromStr for StarKey {
646 type Err = SpaceErr;
647
648 fn from_str(s: &str) -> Result<Self, Self::Err> {
649 Ok(result(all_consuming(parse_star_key)(new_span(s)))?)
650 }
651}
652
653#[async_trait]
654pub trait PointFactory: Send + Sync {
655 async fn create(&self) -> Result<Point, SpaceErr>;
656}
657
658#[cfg(test)]
659pub mod test {
660 use crate::point::Point;
661 use core::str::FromStr;
662
663 #[test]
664 pub fn test_root_routes() {
665 let point = Point::from_str("GLOBAL::star").unwrap();
666 let parent = point.parent().unwrap();
667 assert!(!parent.is_local_root());
668 assert_eq!(parent.to_string(), "GLOBAL::ROOT".to_string())
669 }
670
671 #[test]
672 pub fn test_push_fs() {
673 let point = Point::from_str("repo:some:1.0.0:/blah").unwrap();
674 let bundle = point.to_bundle().unwrap();
675 println!("{}", bundle.to_string());
676 let root = bundle.clone().push(":/").unwrap();
677 println!("{}", root.to_string());
678 }
679}