1
1
use crate :: value:: Value ;
2
2
use std:: rc:: Rc ;
3
3
4
- // @TODO should we just exclusively call them protocols as
5
- // we've been doing (deciding that Clojure without the host
6
- // has Protocols as its natural abstraction for interfaces)
7
- // or should interfaces, as an abstraction, exist (and perhaps
8
- // be used by Protocols even?)
9
4
/// A type that implements Protocol is one that has a pseudo downcasting of
10
5
///
11
6
/// value.as::<ISeq>()
@@ -16,26 +11,154 @@ use std::rc::Rc;
16
11
/// functions to the Value inside, and unwrap() it to get your Value back when
17
12
/// you're done
18
13
pub trait Protocol : Sized {
19
- fn try_as_protocol ( val : & Rc < Value > ) -> Option < Self > ; // where Self:Sized;
14
+ fn instanceof ( val : & Rc < Value > ) -> bool ;
15
+
16
+ // The following exist so we can build the rest of the trait functions on this generic behavior
17
+ /// Simply wraps the value in this protocol
18
+ /// whether its a valid cast or not
19
+ fn raw_wrap ( val : & Rc < Value > ) -> Self ;
20
+
21
+ /// Simply retrieves the Rc<Value> stored in the Protocol,
22
+ /// whether its valid or not
23
+ fn raw_unwrap ( & self ) -> Rc < Value > ;
24
+
25
+ // @TODO consider renaming downcast
26
+ fn try_as_protocol ( val : & Rc < Value > ) -> Option < Self > {
27
+ if Self :: instanceof ( val) {
28
+ Some ( Self :: raw_wrap ( val) )
29
+ }
30
+ else {
31
+ None
32
+ }
33
+ }
34
+
20
35
/// Panics if your Value isn't an instance of Protocol
21
36
fn as_protocol ( val : & Rc < Value > ) -> Self {
22
37
Self :: try_as_protocol ( val) . unwrap ( )
23
38
}
24
- fn try_unwrap ( & self ) -> Option < Rc < Value > > ;
25
- /// Panics if your Value isn't an instance of Protocol
39
+
40
+ fn try_unwrap ( & self ) -> Option < Rc < Value > > {
41
+ let inner_value = self . raw_unwrap ( ) ;
42
+ if Self :: instanceof ( & inner_value) {
43
+ Some ( inner_value)
44
+ }
45
+ else {
46
+ None
47
+ }
48
+ }
49
+ // Realistically, the fact that you unwrap to get an upcast is just an implementation detail, so
50
+ // @TODO change to upcast
51
+ /// Panics if Value not instance of Protocol
26
52
fn unwrap ( & self ) -> Rc < Value > {
27
53
self . try_unwrap ( ) . unwrap ( )
28
54
}
29
55
}
30
56
pub trait ProtocolCastable {
57
+ fn instanceof < T : Protocol > ( & self ) -> bool ;
31
58
fn try_as_protocol < T : Protocol > ( & self ) -> Option < T > ;
32
59
fn as_protocol < T : Protocol > ( & self ) -> T ;
33
60
}
61
+
34
62
impl ProtocolCastable for Rc < Value > {
63
+ fn instanceof < T : Protocol > ( & self ) -> bool {
64
+ T :: instanceof ( self )
65
+ }
35
66
fn try_as_protocol < T : Protocol > ( & self ) -> Option < T > {
36
67
T :: try_as_protocol ( self )
37
68
}
38
69
fn as_protocol < T : Protocol > ( & self ) -> T {
39
70
T :: as_protocol ( self )
40
71
}
41
72
}
73
+
74
+ // @TODO Consider changing syntax to differentiate protocol from variants
75
+ // @TODO Consider trade offs of having a very plain function like macro of macro!(a,b,c)
76
+ // and having a clearer one like define_protocol(Iterable = A | B | C)
77
+ #[ macro_export]
78
+ macro_rules! define_protocol {
79
+ // define_protocol!(Protocol = A | B)
80
+ ( $protocol: ident = $( $variant: ident) |* ) => {
81
+ #[ derive( Debug , Clone ) ]
82
+ pub struct $protocol {
83
+ value: Rc <Value >
84
+ }
85
+ impl crate :: protocol:: Protocol for $protocol {
86
+ fn raw_wrap( val: & Rc <Value >) -> Self {
87
+ $protocol { value: Rc :: clone( val) }
88
+ }
89
+ fn raw_unwrap( & self ) -> Rc <Value > {
90
+ Rc :: clone( & self . value)
91
+ }
92
+ fn instanceof( val: & Rc <Value >) -> bool {
93
+ match & * * val {
94
+ $(
95
+ crate :: value:: Value :: $variant( _) => true ,
96
+ ) *
97
+ _ => false
98
+ }
99
+ }
100
+ }
101
+ } ;
102
+ ( $protocol: ident, $( $variant: ident) , * ) => {
103
+ #[ derive( Debug , Clone ) ]
104
+ pub struct $protocol {
105
+ value: Rc <Value >
106
+ }
107
+ impl crate :: protocol:: Protocol for $protocol {
108
+ fn raw_wrap( val: & Rc <Value >) -> Self {
109
+ $protocol { value: Rc :: clone( val) }
110
+ }
111
+ fn raw_unwrap( & self ) -> Rc <Value > {
112
+ Rc :: clone( & self . value)
113
+ }
114
+ fn instanceof( val: & Rc <Value >) -> bool {
115
+ match & * * val {
116
+ $(
117
+ crate :: value:: Value :: $variant( _) => true ,
118
+ ) *
119
+ _ => false
120
+ }
121
+ }
122
+ }
123
+ } ;
124
+ }
125
+ // @TODO next trick; extend_protocol, so that it actually wraps trait equivalent of protocol
126
+ // Ie, make it so that protocol::IFn is itself of trait IFn, and automatically
127
+ // wraps its trait functions
128
+ // @TODO Think, however, whether its worth it to always have a trait and protocol
129
+ // I believe so -- I think basically the idea is the trait wraps true Rust values,
130
+ // like symbol::Symbol, and the protocol wraps its ClojureRS equivalent, like Value::Symbol
131
+ //
132
+ //
133
+ // traits and traits are both to give us interface behavior, traits give that to us
134
+ // for our rust primitives like symbol::Symbol, but too much of the power of both is at compile time.
135
+ // Just as we made the Value to give us a value whose rust type could be unknown at runtime,
136
+ // we created the Protocol to give us an interface we could cast Values to and from at runtime,
137
+ // as well as some other benefits we need
138
+ //
139
+ // macro_rules! extend_protocol {
140
+ // ($protocol:ident, $fn_name:ident, $fn:expr, $($variant:ident), *) => {
141
+ //
142
+ // impl $protocol {
143
+ // fn $fn_name(val: &Rc<Value>) -> Option<Self> {
144
+ // match &**val {
145
+ // $(
146
+ // crate::value::Value::$variant(_) => Some($protocol {
147
+ // value: Rc::clone(val),
148
+ // }),
149
+ // )*
150
+ // _ => None
151
+ // }
152
+ // }
153
+ // fn try_unwrap(&self) -> Option<Rc<Value>> {
154
+ // match &*self.value {
155
+ // $(
156
+ // crate::value::Value::$variant(_) => Some(Rc::clone(&self.value)),
157
+ // )*
158
+ // _ => None
159
+ // }
160
+ // }
161
+ // }
162
+ // };
163
+ // }
164
+ //
0 commit comments