kompact 0.11.3

Kompact is a Rust implementation of the Kompics component model combined with the Actor model.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
use super::*;
use crate::{
    messaging::{DispatchData, DispatchEnvelope, MsgEnvelope, SerialisedFrame},
    net::buffers::ChunkRef,
};
use std::{
    convert::TryFrom,
    error::Error,
    fmt::{self, Debug},
    net::{AddrParseError, IpAddr, SocketAddr},
    ops::Div,
    str::FromStr,
};
use uuid::Uuid;

/// Transport protocol to use for delivering messages
/// sent to an [ActorPath](ActorPath)
///
/// # Note
///
/// Dispatcher implementations are not required to implement all protocols.
/// Check your concrete implementation, before selecting an arbitrary protocol.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(u8)]
pub enum Transport {
    /// Local reflection only, no network messages involved
    Local = 0b00,
    /// Send messages over TCP
    Tcp = 0b01,
    /// Send messages as UDP datagrams
    Udp = 0b10,
}

impl Transport {
    /// Returns `true` if this is an instance of [Transport::Local](Transport::Local)
    pub fn is_local(&self) -> bool {
        matches!(*self, Transport::Local)
    }

    /// Returns `true` if this is *not* an instance of [Transport::Local](Transport::Local)
    pub fn is_remote(&self) -> bool {
        !self.is_local()
    }
}

impl fmt::Display for Transport {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            &Transport::Local => write!(fmt, "local"),
            &Transport::Tcp => write!(fmt, "tcp"),
            &Transport::Udp => write!(fmt, "udp"),
        }
    }
}

impl FromStr for Transport {
    type Err = TransportParseError;

    fn from_str(s: &str) -> Result<Transport, TransportParseError> {
        match s {
            "local" => Ok(Transport::Local),
            "tcp" => Ok(Transport::Tcp),
            "udp" => Ok(Transport::Udp),
            _ => Err(TransportParseError),
        }
    }
}

/// Error type for parsing the [Transport](Transport) from a string
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct TransportParseError;

impl fmt::Display for TransportParseError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.write_str("TransportParseError")
    }
}

impl Error for TransportParseError {
    fn description(&self) -> &str {
        "Transport must be one of [local,tcp,udp]"
    }
}

/// Error type for parsing [paths](ActorPath) from a string
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PathParseError {
    /// The format is wrong
    Form(String),
    /// The transport protocol was invalid
    Transport(TransportParseError),
    /// The network address was invalid
    Addr(AddrParseError),
    /// The path contains a disallowed character
    IllegalCharacter(char),
}

impl fmt::Display for PathParseError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            PathParseError::Form(s) => write!(fmt, "Invalid formatting: {}", s),
            PathParseError::Transport(e) => write!(fmt, "Could not parse transport: {}", e),
            PathParseError::Addr(e) => write!(fmt, "Could not parse address: {}", e),
            PathParseError::IllegalCharacter(c) => {
                write!(fmt, "The path contains an illegal character: {}", c)
            }
        }
    }
}

impl Error for PathParseError {
    fn description(&self) -> &str {
        "Path could not be parsed"
    }

    fn cause(&self) -> Option<&dyn Error> {
        match self {
            PathParseError::Form(_) => None,
            PathParseError::Transport(e) => Some(e),
            PathParseError::Addr(e) => Some(e),
            PathParseError::IllegalCharacter(_) => None,
        }
    }
}

impl From<TransportParseError> for PathParseError {
    fn from(e: TransportParseError) -> PathParseError {
        PathParseError::Transport(e)
    }
}

impl From<AddrParseError> for PathParseError {
    fn from(e: AddrParseError) -> PathParseError {
        PathParseError::Addr(e)
    }
}

/// The part of an [ActorPath](ActorPath) that refers to the [KompactSystem](KompactSystem)
///
/// As a URI, a `SystemPath` looks like `"tcp://127.0.0.1:8080"`, for example.
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SystemPath {
    protocol: Transport,
    // TODO address could be IPv4, IPv6, or a domain name (not supported yet)
    address: IpAddr,
    port: u16,
}

impl SystemPath {
    /// Construct a new system path from individual parts
    pub fn new(protocol: Transport, address: IpAddr, port: u16) -> SystemPath {
        SystemPath {
            protocol,
            address,
            port,
        }
    }

    /// Construct a new system path from individual parts using a [SocketAddr](std::net::SocketAddr)
    pub fn with_socket(protocol: Transport, socket: SocketAddr) -> SystemPath {
        SystemPath {
            protocol,
            address: socket.ip(),
            port: socket.port(),
        }
    }

    /// Returns a reference to the [Transport](Transport) protocol associated with with this system path
    pub fn protocol(&self) -> Transport {
        self.protocol
    }

    /// Returns a reference to the IP address associated with with this system path
    pub fn address(&self) -> &IpAddr {
        &self.address
    }

    /// Returns the port associated with with this system path
    pub fn port(&self) -> u16 {
        self.port
    }

    /// Create a named path starting with this system path and ending with the given string
    ///
    /// Paths created with this function will be validated to be a valid lookup path,
    /// and an error will be returned if they are not.
    pub fn into_named_with_string(self, path: &str) -> Result<NamedPath, PathParseError> {
        let parsed = parse_path(path);
        self.into_named_with_vec(parsed)
    }

    /// Returns the SocketAddr corresponding to the SystemPath
    pub fn socket_address(&self) -> SocketAddr {
        SocketAddr::new(self.address, self.port)
    }

    /// Create a named path starting with this system path and ending with the sequence of path segments
    ///
    /// Paths created with this function will be validated to be a valid lookup path,
    /// and an error will be returned if they are not.
    pub fn into_named_with_vec(self, path: Vec<String>) -> Result<NamedPath, PathParseError> {
        validate_lookup_path(&path)?;
        let named = NamedPath::with_system(self, path);
        Ok(named)
    }

    /// Create a unique path starting with this system path and ending with the given `id`
    pub fn into_unique(self, id: Uuid) -> UniquePath {
        UniquePath::with_system(self, id)
    }
}

impl fmt::Display for SystemPath {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{}://{}:{}", self.protocol, self.address, self.port)
    }
}

/// Methods for things that contain a [SystemPath](SystemPath)
pub trait SystemField {
    /// Returns a reference to the system path
    fn system(&self) -> &SystemPath;

    /// Returns the transport protocol used in the system path
    fn protocol(&self) -> Transport {
        self.system().protocol()
    }

    /// Returns the address used in the system path
    fn address(&self) -> &IpAddr {
        self.system().address()
    }

    /// Returns the port used in the system path
    fn port(&self) -> u16 {
        self.system().port()
    }
}

impl SystemField for SystemPath {
    fn system(&self) -> &SystemPath {
        self
    }
}

/// A factory trait for things that have associated [actor paths](ActorPath)
pub trait ActorPathFactory {
    /// Returns the associated actor path
    fn actor_path(&self) -> ActorPath;
}

/// A temporary combination of an [ActorPath](ActorPath)
/// and something that can [dispatch](Dispatching) stuff
///
/// This can be used when you want to use a different actor path
/// than the one of the current component for a [tell](ActorPath::tell)
/// but still need to use the component's dispatcher for the message.
/// This is useful for forwarding components, for example, when trying to
/// preserve the original sender.
///
/// See also [using_dispatcher](ActorPath::using_dispatcher).
pub struct DispatchingPath<'a, 'b> {
    path: &'a ActorPath,
    ctx: &'b dyn Dispatching,
}

impl Dispatching for DispatchingPath<'_, '_> {
    fn dispatcher_ref(&self) -> DispatcherRef {
        self.ctx.dispatcher_ref()
    }
}

impl ActorPathFactory for DispatchingPath<'_, '_> {
    fn actor_path(&self) -> ActorPath {
        self.path.clone()
    }
}

/// An actor path is a serialisable, possibly remote reference to
/// an actor
///
/// Any message sent via an actor path *might* go over the network,
/// and must be treated as fallible.
/// It must also be [serialisable](Serialisable).
#[derive(Clone, Debug)]
#[repr(u8)]
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ActorPath {
    /// A unique actor path identifies a concrete instance of an actor
    ///
    /// Unique actor paths use the component's unique id internally.
    ///
    /// Unique actor paths become invalid when a component is
    /// replaced with a new instance of the same type.
    ///
    /// A unique path may look something like `"tcp://127.0.0.1:8080#1e555f40-de1d-4aee-8202-64fdc27edfa8"`, for example.
    Unique(UniquePath),
    /// A named actor path identifies a service, rather than a concrete actor instance
    ///
    /// Named paths must be [registered](KompactSystem::register_by_alias) to a particular actor,
    /// and their registration can be changed over time,
    /// as actors fail and are replaced, for example.
    ///
    /// Named paths may be described hierarchically, similar to URLs.
    ///
    /// A named path may look something like `"tcp://127.0.0.1:8080/my-actor-group/my-actor"`, for example.
    Named(NamedPath),
}

impl ActorPath {
    /// Send message `m` to the actor designated by this path
    ///
    /// The `from` field is used as a source,
    /// and the `ActorPath` it resolved to will be supplied at the destination.
    ///
    /// This function ensures that the protocol of the source path matches the
    /// protocol of this path. If you would like the sender path to have a different
    /// transport protocol, use [tell_with_sender](ActorPath::tell_with_sender) instead.
    ///
    /// Serialisation of `m` happens lazily in the dispatcher,
    /// and only if it really goes over the network. If this actor path
    /// turns out to be local, `m` will be moved to the heap and send locally instead.
    ///
    /// In fact, this method always moves `m` onto the heap (unless it already is allocated there),
    /// to facilitate this lazy serialisation.
    /// As this method has some overhead in the case where it is certain
    /// that `m` will definitely go over the network, you can use
    /// [tell_serialised](ActorPath::tell_serialised) to force eager serialisation instead.
    pub fn tell<S, B>(&self, m: B, from: &S) -> ()
    where
        S: ActorPathFactory + Dispatching,
        B: Into<Box<dyn Serialisable>>,
    {
        let mut src = from.actor_path();
        src.set_protocol(self.protocol());
        self.tell_with_sender(m, from, src)
    }

    /// Send message `m` to the actor designated by this path
    ///
    /// The `from` field is used as a source and will be supplied at the destination.
    ///
    /// Serialisation of `m` happens lazily in the dispatcher,
    /// and only if it really goes over the network. If this actor path
    /// turns out to be local, `m` will be moved to the heap and send locally instead.
    ///
    /// In fact, this method always moves `m` onto the heap (unless it already is allocated there),
    /// to facilitate this lazy serialisation.
    /// As this method has some overhead in the case where it is certain
    /// that `m` will definitely go over the network, you can use
    /// [tell_serialised](ActorPath::tell_serialised) to force eager serialisation instead.
    pub fn tell_with_sender<B, D>(&self, m: B, dispatch: &D, from: ActorPath) -> ()
    where
        B: Into<Box<dyn Serialisable>>,
        D: Dispatching,
    {
        let msg: Box<dyn Serialisable> = m.into();
        let dst = self.clone();
        let env = DispatchEnvelope::Msg {
            src: from.clone(),
            dst: dst.clone(),
            msg: DispatchData::Lazy(msg, from, dst),
        };
        dispatch.dispatcher_ref().enqueue(MsgEnvelope::Typed(env))
    }

    /// Send message `m` to the actor designated by this path
    ///
    /// This function has the same effect as [tell](ActorPath::tell),
    /// but serialises eagerly into a Pooled buffer (pre-allocated and bounded).
    pub fn tell_serialised<CD, B>(&self, m: B, from: &CD) -> Result<(), SerError>
    where
        CD: ComponentTraits + ComponentLifecycle,
        B: Serialisable + 'static,
    {
        let mut src = from.actor_path();
        src.set_protocol(self.protocol());
        self.tell_serialised_with_sender(m, from, src)
    }

    /// Send message `m` to the actor designated by this path
    ///
    /// This function has the same effect as [tell](ActorPath::tell),
    /// but serialises eagerly into a Pooled buffer (pre-allocated and bounded).
    pub fn tell_serialised_with_sender<CD, B>(
        &self,
        m: B,
        dispatch: &CD,
        from: ActorPath,
    ) -> Result<(), SerError>
    where
        CD: ComponentTraits + ComponentLifecycle,
        B: Serialisable + 'static,
    {
        if self.protocol() == Transport::Local {
            // No need to serialize!
            self.tell_with_sender(m, dispatch, from);
            Ok(())
        } else {
            dispatch.ctx().with_buffer(|buffer| {
                let mut buf = buffer.get_buffer_encoder()?;
                let msg =
                    crate::serialisation::ser_helpers::serialise_msg(&from, self, &m, &mut buf)?;
                let env = DispatchEnvelope::Msg {
                    src: from,
                    dst: self.clone(),
                    msg: DispatchData::Serialised(SerialisedFrame::ChunkLease(msg)),
                };
                dispatch.dispatcher_ref().enqueue(MsgEnvelope::Typed(env));
                Ok(())
            })
        }
    }

    /// Send prepared message `content` to the actor designated by this path
    ///
    /// This function has the same effect as [tell_serialised](ActorPath::tell_serialised), but
    /// requires the content to be pre-serialised into a [ChunkRef](net::buffers::ChunkRef).
    ///
    /// Use [self.ctx.preserialise(m)](component::ComponentContext#preserialise)
    /// to preserialise data into a `ChunkRef`
    pub fn tell_preserialised<CD>(&self, content: ChunkRef, from: &CD) -> Result<(), SerError>
    where
        CD: ComponentTraits + ComponentLifecycle,
    {
        let mut src = from.actor_path();
        src.set_protocol(self.protocol());
        self.tell_preserialised_with_sender(content, from, src)
    }

    /// Send preserialised message `content` to the actor designated by this path with specified `from`
    ///
    /// This function has the same effect as [tell_serialised](ActorPath::tell_serialised), but
    /// requires the content to be pre-serialised into a [ChunkRef](net::buffers::ChunkRef).
    ///
    /// Use [self.ctx.preserialise(m)](component::ComponentContext#preserialise) to preserialise data into a `ChunkRef`
    pub fn tell_preserialised_with_sender<CD: ComponentTraits + ComponentLifecycle>(
        &self,
        content: ChunkRef,
        dispatch: &CD,
        from: ActorPath,
    ) -> Result<(), SerError> {
        dispatch.ctx().with_buffer(|buffer| {
            let mut buf = buffer.get_buffer_encoder()?;
            let msg = crate::serialisation::ser_helpers::serialise_msg_with_preserialised(
                &from, self, content, &mut buf,
            )?;
            let env = DispatchEnvelope::Msg {
                src: from,
                dst: self.clone(),
                msg: DispatchData::Serialised(SerialisedFrame::ChunkRef(msg)),
            };
            dispatch.dispatcher_ref().enqueue(MsgEnvelope::Typed(env));
            Ok(())
        })
    }

    /// Forwards the still serialised message to this path without changing the sender
    ///
    /// This can be used for routing protocls where the final recipient is supposed to reply
    /// to the original sender, not the intermediaries.
    pub fn forward_with_original_sender<D>(
        &self,
        mut serialised_message: NetMessage,
        dispatcher: &D,
    ) -> ()
    where
        D: Dispatching,
    {
        serialised_message.receiver = self.clone();
        let env = DispatchEnvelope::ForwardedMsg {
            msg: serialised_message,
        };
        dispatcher.dispatcher_ref().enqueue(MsgEnvelope::Typed(env));
    }

    /// Forwards the still serialised message to this path replacing the sender with the given one
    pub fn forward_with_sender<D>(
        &self,
        mut serialised_message: NetMessage,
        dispatch: &D,
        from: ActorPath,
    ) -> ()
    where
        D: Dispatching,
    {
        serialised_message.receiver = self.clone();
        serialised_message.sender = from;
        let env = DispatchEnvelope::ForwardedMsg {
            msg: serialised_message,
        };
        dispatch.dispatcher_ref().enqueue(MsgEnvelope::Typed(env));
    }

    /// Returns a temporary combination of an [ActorPath](ActorPath)
    /// and something that can [dispatch](Dispatching) stuff
    ///
    /// This can be used when you want to use a different actor path
    /// than the one of the current component for a [tell](ActorPath::tell)
    /// but still need to use the component's dispatcher for the message.
    /// This is useful for forwarding components, for example, when trying to
    /// preserve the original sender.
    pub fn using_dispatcher<'a, 'b>(
        &'a self,
        disp: &'b dyn Dispatching,
    ) -> DispatchingPath<'a, 'b> {
        DispatchingPath {
            path: self,
            ctx: disp,
        }
    }

    fn system_mut(&mut self) -> &mut SystemPath {
        match self {
            ActorPath::Unique(ref mut up) => up.system_mut(),
            ActorPath::Named(ref mut np) => np.system_mut(),
        }
    }

    /// Change the transport protocol for this actor path
    pub fn set_protocol(&mut self, proto: Transport) {
        self.system_mut().protocol = proto;
    }

    /// Sets the transport protocol for this actor path to UDP
    pub fn via_udp(&mut self) {
        self.set_protocol(Transport::Udp);
    }

    /// Sets the transport protocol for this actor path to TCP
    pub fn via_tcp(&mut self) {
        self.set_protocol(Transport::Tcp);
    }

    /// Sets the transport protocol for this actor path to LOCAL
    pub fn via_local(&mut self) {
        self.set_protocol(Transport::Local);
    }

    /// If this path is a named path, return the underlying named path
    ///
    /// Otherwise return `None`.
    pub fn named(self) -> Option<NamedPath> {
        match self {
            ActorPath::Named(p) => Some(p),
            _ => None,
        }
    }

    /// Return the underlying named path
    ///
    /// Panics if this is not actually a named path variant.
    pub fn unwrap_named(self) -> NamedPath {
        self.named().unwrap()
    }

    /// If this path is a unique path, return the underlying unique path
    ///
    /// Otherwise return `None`.
    pub fn unique(self) -> Option<UniquePath> {
        match self {
            ActorPath::Unique(p) => Some(p),
            _ => None,
        }
    }

    /// Return the underlying unique path
    ///
    /// Panics if this is not actually a uniqe path variant.
    pub fn unwrap_unique(self) -> UniquePath {
        self.unique().unwrap()
    }
}

impl SystemField for ActorPath {
    fn system(&self) -> &SystemPath {
        match self {
            ActorPath::Unique(up) => up.system(),
            ActorPath::Named(np) => np.system(),
        }
    }
}

impl From<(SystemPath, Uuid)> for ActorPath {
    fn from(t: (SystemPath, Uuid)) -> ActorPath {
        ActorPath::Unique(UniquePath {
            system: t.0,
            id: t.1,
        })
    }
}

impl From<UniquePath> for ActorPath {
    fn from(p: UniquePath) -> ActorPath {
        ActorPath::Unique(p)
    }
}

impl From<NamedPath> for ActorPath {
    fn from(p: NamedPath) -> ActorPath {
        ActorPath::Named(p)
    }
}

/// Separator for segments in named actor paths
///
/// # Example
///
/// `tcp://127.0.0.1:8080/segment1/segment2`
pub const PATH_SEP: char = '/';
/// Separator for the id in unique actor paths
///
/// # Example
///
/// `tcp://127.0.0.1:8080#1e555f40-de1d-4aee-8202-64fdc27edfa8`
pub const UNIQUE_PATH_SEP: char = '#';
/// Marker for broadcast routing
///
/// # Example
///
/// `tcp://127.0.0.1:8080/segment1/*`
/// will route to all actors whose path starts with `segment1`.
pub const BROADCAST_MARKER: char = '*';
/// Marker for select routing
///
/// # Example
///
/// `tcp://127.0.0.1:8080/segment1/?`
/// will route to some actor whose path starts with `segment1`.
pub const SELECT_MARKER: char = '?';

impl fmt::Display for ActorPath {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ActorPath::Named(np) => {
                let path = np.path.iter().fold(String::new(), |mut acc, arg| {
                    acc.push(PATH_SEP);
                    acc.push_str(arg);
                    acc
                });
                write!(fmt, "{}{}", np.system, path)
            }
            ActorPath::Unique(up) => write!(fmt, "{}{}{}", up.system, UNIQUE_PATH_SEP, up.id),
        }
    }
}

impl TryFrom<String> for ActorPath {
    type Error = PathParseError;

    fn try_from(s: String) -> Result<Self, Self::Error> {
        let p = ActorPath::from_str(&s)?;
        Ok(p)
    }
}

impl FromStr for ActorPath {
    type Err = PathParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.contains(UNIQUE_PATH_SEP) {
            let p = UniquePath::from_str(s)?;
            Ok(ActorPath::Unique(p))
        } else {
            let p = NamedPath::from_str(s)?;
            Ok(ActorPath::Named(p))
        }
    }
}

/// A unique actor path identifies a concrete instance of an actor
///
/// Unique actor paths use the component's unique id internally.
///
/// Unique actor paths become invalid when a component is
/// replaced with a new instance of the same type.
///
/// A unique path may look something like `"tcp://127.0.0.1:8080#1e555f40-de1d-4aee-8202-64fdc27edfa8"`, for example.
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct UniquePath {
    system: SystemPath,
    id: Uuid,
}

impl UniquePath {
    /// Construct a new unique path from parts
    pub fn new(protocol: Transport, address: IpAddr, port: u16, id: Uuid) -> UniquePath {
        UniquePath {
            system: SystemPath::new(protocol, address, port),
            id,
        }
    }

    /// Construct a new unique path from a system path and an id
    pub fn with_system(system: SystemPath, id: Uuid) -> UniquePath {
        UniquePath { system, id }
    }

    /// Construct a new unique path with a [socket](std::net::SocketAddr) and an id
    pub fn with_socket(protocol: Transport, socket: SocketAddr, id: Uuid) -> UniquePath {
        UniquePath {
            system: SystemPath::with_socket(protocol, socket),
            id,
        }
    }

    /// Returns a reference to this path's unique id
    pub fn id(&self) -> Uuid {
        self.id
    }

    /// Returns a mutable reference to this path's system path part
    pub fn system_mut(&mut self) -> &mut SystemPath {
        &mut self.system
    }
}

impl TryFrom<String> for UniquePath {
    type Error = PathParseError;

    fn try_from(s: String) -> Result<Self, Self::Error> {
        let p = UniquePath::from_str(&s)?;
        Ok(p)
    }
}

impl FromStr for UniquePath {
    type Err = PathParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split("://").collect();
        // parts: [tcp]://[IP:port#id]
        if parts.len() != 2 {
            return Err(PathParseError::Form(s.to_string()));
        }
        let proto: Transport = parts[0].parse()?;
        let parts: Vec<&str> = parts[1].split(UNIQUE_PATH_SEP).collect();
        // parts: [IP:port]#[UUID]
        if parts.len() != 2 {
            return Err(PathParseError::Form(s.to_string()));
        }
        let socket = SocketAddr::from_str(parts[0])?;
        let uuid =
            Uuid::from_str(parts[1]).map_err(|_parse_err| PathParseError::Form(s.to_string()))?;

        Ok(UniquePath::with_socket(proto, socket, uuid))
    }
}

impl SystemField for UniquePath {
    fn system(&self) -> &SystemPath {
        &self.system
    }
}

/// A named actor path identifies a service, rather than a concrete actor instance
///
/// Named paths must be [registered](KompactSystem::register_by_alias) to a particular actor,
/// and their registration can be changed over time,
/// as actors fail and are replaced, for example.
///
/// Named paths may be described hierarchically, similar to URLs.
///
/// A named path may look something like `"tcp://127.0.0.1:8080/my-actor-group/my-actor"`, for example.
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NamedPath {
    system: SystemPath,
    path: Vec<String>,
}

impl NamedPath {
    /// Construct a new named path from parts
    ///
    /// Make sure that none of the elements of the `path` vector contain slashes (i.e., `/`),
    /// as those will be the path separators for the individual elements later.
    pub fn new(protocol: Transport, address: IpAddr, port: u16, path: Vec<String>) -> NamedPath {
        debug_assert!(
            crate::actors::validate_lookup_path(&path).is_ok(),
            "Path contains illegal characters: {:?}",
            path
        );
        NamedPath {
            system: SystemPath::new(protocol, address, port),
            path,
        }
    }

    /// Construct a new named path from a [socket](std::net::SocketAddr) and vector of path elements
    ///
    /// Make sure that none of the elements of the `path` vector contain slashes (i.e., `/`),
    /// as those will be the path separators for the individual elements later.
    pub fn with_socket(protocol: Transport, socket: SocketAddr, path: Vec<String>) -> NamedPath {
        debug_assert!(
            crate::actors::validate_lookup_path(&path).is_ok(),
            "Path contains illegal characters: {:?}",
            path
        );
        NamedPath {
            system: SystemPath::with_socket(protocol, socket),
            path,
        }
    }

    /// Construct a new named path from a system path and vector of path elements
    ///
    /// Make sure that none of the elements of the `path` vector contain slashes (i.e., `/`),
    /// as those will be the path separators for the individual elements later.
    pub fn with_system(system: SystemPath, path: Vec<String>) -> NamedPath {
        debug_assert!(
            crate::actors::validate_lookup_path(&path).is_ok(),
            "Path contains illegal characters: {:?}",
            path
        );
        NamedPath { system, path }
    }

    /// Returns a reference to the path vector
    pub fn path_ref(&self) -> &[String] {
        &self.path
    }

    /// Returns a copy to the path vector
    pub fn clone_path(&self) -> Vec<String> {
        self.path.clone()
    }

    /// Return just the path vector, discarding the SystemPath
    pub fn into_path(self) -> Vec<String> {
        self.path
    }

    /// Returns a mutable reference to this path's system path part
    pub fn system_mut(&mut self) -> &mut SystemPath {
        &mut self.system
    }

    /// Add the given `segment` to the path, if it is valid to do so
    pub fn push(&mut self, segment: String) -> Result<(), PathParseError> {
        if let Some(last_segment) = self.path.last() {
            validate_lookup_path_segment(last_segment, false)?;
        }
        validate_lookup_path_segment(&segment, true)?;
        self.path.push(segment);
        Ok(())
    }

    /// Add the given relative `path` to this path, if it is valid to do so
    pub fn append(mut self, path: &str) -> Result<Self, PathParseError> {
        if let Some(last_segment) = self.path.last() {
            validate_lookup_path_segment(last_segment, false)?;
        }
        let mut segments = parse_path(path);
        self.path.append(&mut segments);
        validate_lookup_path(&self.path)?;
        Ok(self)
    }
}

impl SystemField for NamedPath {
    fn system(&self) -> &SystemPath {
        &self.system
    }
}

impl TryFrom<String> for NamedPath {
    type Error = PathParseError;

    fn try_from(s: String) -> Result<Self, Self::Error> {
        NamedPath::from_str(&s)
    }
}

impl FromStr for NamedPath {
    type Err = PathParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let s1: Vec<&str> = s.split("://").collect();
        if s1.len() != 2 {
            return Err(PathParseError::Form(s.to_string()));
        }
        let proto: Transport = s1[0].parse()?;
        let mut s2: Vec<&str> = s1[1].split(PATH_SEP).collect();
        if s2.is_empty() {
            return Err(PathParseError::Form(s.to_string()));
        }
        let socket = SocketAddr::from_str(s2[0])?;
        let path: Vec<String> = if s2.len() > 1 {
            s2.split_off(1).into_iter().map(|v| v.to_string()).collect()
        } else {
            Vec::default()
        };
        validate_lookup_path(&path)?;
        Ok(NamedPath::with_socket(proto, socket, path))
    }
}

/// Some syntactic sugar for [append](NamedPath::append)
///
/// Allows the pretty `path / "segment" / "*"` syntax
/// to create sub-paths of `path`.
impl Div<&str> for NamedPath {
    type Output = Self;

    fn div(self, rhs: &str) -> Self::Output {
        self.append(rhs).expect("illegal path")
    }
}
/// Some syntactic sugar for [push](NamedPath::push)
///
/// Allows the pretty `path / '*'` syntax
/// to create a broadcast variant of `path`.
impl Div<char> for NamedPath {
    type Output = Self;

    fn div(mut self, rhs: char) -> Self::Output {
        self.push(rhs.to_string()).expect("illegal path");
        self
    }
}

/// Split the `&str` into segments using [PATH_SEP](crate::constants::PATH_SEP)
pub fn parse_path(s: &str) -> Vec<String> {
    s.split(PATH_SEP).map(|v| v.to_string()).collect()
}

/// Check that the given path is valid as a lookup path
///
/// Lookup paths must not contain the following characters:
/// - [PATH_SEP](crate::constants::PATH_SEP)
/// - [UNIQUE_PATH_SEP](crate::constants::UNIQUE_PATH_SEP)
/// - [BROADCAST_MARKER](crate::constants::BROADCAST_MARKER) unless as the only item in the last string
/// - [SELECT_MARKER](crate::constants::SELECT_MARKER) unless as the only item in the last string
pub fn validate_lookup_path(path: &[String]) -> Result<(), PathParseError> {
    let len = path.len();
    for (index, segment) in path.iter().enumerate() {
        validate_lookup_path_segment(segment, (index + 1) == len)?;
    }
    Ok(())
}

/// Check that the given path is valid as an insert path
///
/// Insert paths must not contain the following characters:
/// - [PATH_SEP](crate::constants::PATH_SEP)
/// - [UNIQUE_PATH_SEP](crate::constants::UNIQUE_PATH_SEP)
/// - [BROADCAST_MARKER](crate::constants::BROADCAST_MARKER)
/// - [SELECT_MARKER](crate::constants::SELECT_MARKER)
pub fn validate_insert_path(path: &[String]) -> Result<(), PathParseError> {
    for segment in path.iter() {
        validate_insert_path_segment(segment)?;
    }
    Ok(())
}

pub(crate) fn validate_lookup_path_segment(
    segment: &str,
    is_last: bool,
) -> Result<(), PathParseError> {
    if segment.is_empty() {
        return Err(PathParseError::Form(
            "Path segments may not be empty!".to_string(),
        ));
    }
    let len = segment.len();
    for c in segment.chars() {
        match c {
            PATH_SEP => return Err(PathParseError::IllegalCharacter(PATH_SEP)),
            BROADCAST_MARKER if !is_last && len == 1 => {
                return Err(PathParseError::IllegalCharacter(BROADCAST_MARKER))
            }
            SELECT_MARKER if !is_last && len == 1 => {
                return Err(PathParseError::IllegalCharacter(SELECT_MARKER))
            }
            UNIQUE_PATH_SEP => return Err(PathParseError::IllegalCharacter(UNIQUE_PATH_SEP)),
            _ => (), // ok
        }
    }
    Ok(())
}
pub(crate) fn validate_insert_path_segment(segment: &str) -> Result<(), PathParseError> {
    if segment.is_empty() {
        return Err(PathParseError::Form(
            "Path segments may not be empty!".to_string(),
        ));
    }
    for c in segment.chars() {
        match c {
            PATH_SEP => return Err(PathParseError::IllegalCharacter(PATH_SEP)),
            BROADCAST_MARKER => return Err(PathParseError::IllegalCharacter(BROADCAST_MARKER)),
            SELECT_MARKER => return Err(PathParseError::IllegalCharacter(SELECT_MARKER)),
            UNIQUE_PATH_SEP => return Err(PathParseError::IllegalCharacter(UNIQUE_PATH_SEP)),
            _ => (), // ok
        }
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    const PATH: &str = "local://127.0.0.1:0/test_actor";

    #[test]
    fn actor_path_strings() {
        let ap = ActorPath::from_str(PATH).expect("a proper path");
        println!("Got ActorPath={}", ap);

        let s = ap.to_string();
        assert_eq!(PATH, &s);
        let ap2: ActorPath = s.parse().expect("a proper path");
        assert_eq!(ap, ap2);
    }

    #[test]
    fn actor_path_unique_strings() {
        let ref1 = ActorPath::Unique(UniquePath::new(
            Transport::Local,
            "127.0.0.1".parse().expect("hardcoded IP"),
            8080,
            Uuid::new_v4(),
        ));
        let ref1_string = ref1.to_string();
        let ref1_deser = ActorPath::from_str(&ref1_string).expect("a proper path");
        let ref1_deser2: ActorPath = ref1_string.parse().expect("a proper path");
        assert_eq!(ref1, ref1_deser);
        assert_eq!(ref1, ref1_deser2);
    }

    #[test]
    fn actor_path_named_strings() {
        let ref1 = ActorPath::Named(NamedPath::new(
            Transport::Local,
            "127.0.0.1".parse().expect("hardcoded IP"),
            8080,
            vec!["test".to_string(), "path".to_string()],
        ));
        let ref1_string = ref1.to_string();
        let ref1_deser = ActorPath::from_str(&ref1_string).expect("a proper path");
        let ref1_deser2: ActorPath = ref1_string.parse().expect("a proper path");
        assert_eq!(ref1, ref1_deser);
        assert_eq!(ref1, ref1_deser2);
    }
}