websocat/
specifier.rs

1use super::{L2rUser, Options, Result};
2use super::{PeerConstructor, ProgramState};
3use std;
4use std::cell::RefCell;
5use std::rc::Rc;
6
7pub enum ClassMessageBoundaryStatus {
8    StreamOriented,
9    MessageOriented,
10    MessageBoundaryStatusDependsOnInnerType,
11}
12
13pub enum ClassMulticonnectStatus {
14    MultiConnect,
15    SingleConnect,
16    MulticonnectnessDependsOnInnerType,
17}
18
19/// A trait for a each specified type's accompanying object
20///
21/// Don't forget to register each instance at the `list_of_all_specifier_classes` macro.
22pub trait SpecifierClass: std::fmt::Debug {
23    /// The primary name of the class
24    fn get_name(&self) -> &'static str;
25    /// Names to match command line parameters against, with a `:` colon if needed
26    fn get_prefixes(&self) -> Vec<&'static str>;
27    /// --long-help snippet about this specifier
28    fn help(&self) -> &'static str;
29    /// Given the command line text, construct the specifier
30    /// arg is what comes after the colon (e.g. `//echo.websocket.org` in `ws://echo.websocket.org`)
31    fn construct(&self, arg: &str) -> Result<Rc<dyn Specifier>>;
32    /// Given the inner specifier, construct this specifier.
33    fn construct_overlay(&self, inner: Rc<dyn Specifier>) -> Result<Rc<dyn Specifier>>;
34    /// Returns if this specifier is an overlay
35    fn is_overlay(&self) -> bool;
36    /// True if it is not expected to preserve message boundaries on reads
37    fn message_boundary_status(&self) -> ClassMessageBoundaryStatus;
38
39    fn multiconnect_status(&self) -> ClassMulticonnectStatus;
40    /// If it is Some then is_overlay, construct and most other things are ignored and prefix get replaced...
41    fn alias_info(&self) -> Option<&'static str>;
42}
43
44macro_rules! specifier_alias {
45    (name=$n:ident,
46            prefixes=[$($p:expr),*],
47            alias=$x:expr,
48            help=$h:expr) => {
49        #[derive(Debug,Default)]
50        pub struct $n;
51        impl $crate::SpecifierClass for $n {
52            fn get_name(&self) -> &'static str { stringify!($n) }
53            fn get_prefixes(&self) -> Vec<&'static str> { vec![$($p),*] }
54            fn help(&self) -> &'static str { $h }
55            fn message_boundary_status(&self) -> $crate::ClassMessageBoundaryStatus {
56                panic!("Error: message_boundary_status called on alias class")
57            }
58            fn multiconnect_status(&self) -> $crate::ClassMulticonnectStatus {
59                panic!("Error: multiconnect_status called on alias class")
60            }
61            fn is_overlay(&self) -> bool {
62                false
63            }
64            fn construct(&self, _arg:&str) -> $crate::Result<Rc<dyn Specifier>> {
65                panic!("Error: construct called on alias class")
66            }
67            fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {
68                panic!("Error: construct_overlay called on alias class")
69            }
70            fn alias_info(&self) -> Option<&'static str> { Some($x) }
71        }
72    };
73}
74
75macro_rules! specifier_class {
76    (name=$n:ident,
77            target=$t:ident,
78            prefixes=[$($p:expr),*],
79            arg_handling=$c:tt,
80            overlay=$o:expr,
81            $so:expr,
82            $ms:expr,
83            help=$h:expr) => {
84        #[derive(Debug,Default)]
85        pub struct $n;
86        impl $crate::SpecifierClass for $n {
87            fn get_name(&self) -> &'static str { stringify!($n) }
88            fn get_prefixes(&self) -> Vec<&'static str> { vec![$($p),*] }
89            fn help(&self) -> &'static str { $h }
90            fn message_boundary_status(&self) -> $crate::ClassMessageBoundaryStatus {
91                use $crate::ClassMessageBoundaryStatus::*;
92                $so
93            }
94            fn multiconnect_status(&self) -> $crate::ClassMulticonnectStatus {
95                use $crate::ClassMulticonnectStatus::*;
96                $ms
97            }
98            fn is_overlay(&self) -> bool {
99                $o
100            }
101            specifier_class!(construct target=$t $c);
102        }
103    };
104    (construct target=$t:ident noarg) => {
105        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {
106            if just_arg != "" {
107                Err(format!("{}-specifer requires no parameters. `{}` is not needed",
108                    self.get_name(), just_arg))?;
109            }
110            Ok(Rc::new($t))
111        }
112        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {
113            panic!("Error: construct_overlay called on non-overlay specifier class")
114        }
115        fn alias_info(&self) -> Option<&'static str> { None }
116    };
117    (construct target=$t:ident into) => {
118        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {
119            Ok(Rc::new($t(just_arg.into())))
120        }
121        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {
122            panic!("Error: construct_overlay called on non-overlay specifier class")
123        }
124        fn alias_info(&self) -> Option<&'static str> { None }
125    };
126    (construct target=$t:ident parse) => {
127        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {
128            Ok(Rc::new($t(just_arg.parse()?)))
129        }
130        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {
131            panic!("Error: construct_overlay called on non-overlay specifier class")
132        }
133        fn alias_info(&self) -> Option<&'static str> { None }
134    };
135    (construct target=$t:ident parseresolve) => {
136        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {
137            use std::net::ToSocketAddrs;
138            info!("Resolving hostname to IP addresses");
139            let addrs : Vec<std::net::SocketAddr> = just_arg.to_socket_addrs()?.collect();
140            if addrs.is_empty() {
141                Err("Failed to resolve this hostname to IP")?;
142            }
143            for addr in &addrs {
144                info!("Got IP: {}", addr);
145            }
146            Ok(Rc::new($t(addrs)))
147        }
148        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {
149            panic!("Error: construct_overlay called on non-overlay specifier class")
150        }
151        fn alias_info(&self) -> Option<&'static str> { None }
152    };
153    (construct target=$t:ident subspec) => {
154        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {
155            Ok(Rc::new($t($crate::spec(just_arg)?)))
156        }
157        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {
158            Ok(Rc::new($t(_inner)))
159        }
160        fn alias_info(&self) -> Option<&'static str> { None }
161    };
162    (construct target=$t:ident {$($x:tt)*}) => {
163        $($x)*
164        fn alias_info(&self) -> Option<&'static str> { None }
165    };
166}
167
168
169#[derive(Debug)]
170pub struct SpecifierNode {
171    pub cls: Rc<dyn SpecifierClass>,
172    //pub opt: Rc<std::any::Any>,
173}
174
175#[derive(Debug)]
176pub struct SpecifierStack {
177    pub addr: String,
178    pub addrtype: SpecifierNode,
179    pub overlays: Vec<SpecifierNode>,
180}
181
182#[derive(Clone)]
183pub struct ConstructParams {
184    pub global_state: Rc<RefCell<ProgramState>>,
185    pub program_options: Rc<Options>,
186    pub left_to_right: L2rUser,
187}
188
189/// All of those methods are about left_to_right mechanism
190impl ConstructParams {
191    /// Reset left_to_right to default value.
192    pub fn reset_l2r(&mut self) {
193        match self.left_to_right {
194            L2rUser::FillIn(ref mut x) => {
195                *x.borrow_mut() = Default::default();
196                //*x = Rc::new(RefCell::new(Default::default()));
197            }
198            L2rUser::ReadFrom(_) => panic!("ConstructParams::reset_l2r called wrong"),
199        }
200    }
201    /// Clones ConstructParams, changing FillIn to ReadFrom in left_to_right field
202    /// and also disassociating it from the original RefCell.
203    ///
204    /// Panics when called on object with left_to_right set to ReadFrom.
205    pub fn reply(&self) -> Self {
206        let l2r = match self.left_to_right {
207            L2rUser::FillIn(ref x) => Rc::new(x.borrow().clone()),
208            L2rUser::ReadFrom(_) => panic!("ConstructParams::reply called wrong"),
209        };
210        ConstructParams {
211            global_state: self.global_state.clone(),
212            program_options: self.program_options.clone(),
213            left_to_right: L2rUser::ReadFrom(l2r),
214        }
215    }
216
217    pub fn deep_clone(&self) -> Self {
218        let l2r = match self.left_to_right {
219            L2rUser::FillIn(ref x) => L2rUser::FillIn(Rc::new(RefCell::new(x.borrow().clone()))),
220            L2rUser::ReadFrom(_) => {
221                panic!(
222                    "You are not supposed to use ConstructParams::deep_clone on ReadFrom things"
223                );
224            }
225        };
226        ConstructParams {
227            global_state: self.global_state.clone(),
228            program_options: self.program_options.clone(),
229            left_to_right: l2r,
230        }
231    }
232
233    /// Access specified-specific global (singleton) data
234    pub fn global<T:std::any::Any, F>(&self, def:F) -> std::cell::RefMut<T> 
235        where F : FnOnce()->T
236    {
237        std::cell::RefMut::map(
238            self.global_state.borrow_mut(),
239            |x|{
240                x.0.entry::<T>().or_insert_with(def)
241            }
242        )
243    }
244}
245
246/// A parsed command line argument.
247/// For example, `ws-listen:tcp-l:127.0.0.1:8080` gets parsed into
248/// a `WsUpgrade(TcpListen(SocketAddr))`.
249pub trait Specifier: std::fmt::Debug {
250    /// Apply the specifier for constructing a "socket" or other connecting device.
251    fn construct(&self, p: ConstructParams) -> PeerConstructor;
252
253    // Specified by `specifier_boilerplate!`:
254    fn is_multiconnect(&self) -> bool;
255    fn uses_global_state(&self) -> bool;
256}
257
258impl Specifier for Rc<dyn Specifier> {
259    fn construct(&self, p: ConstructParams) -> PeerConstructor {
260        (**self).construct(p)
261    }
262
263    fn is_multiconnect(&self) -> bool {
264        (**self).is_multiconnect()
265    }
266    fn uses_global_state(&self) -> bool {
267        (**self).uses_global_state()
268    }
269}
270
271macro_rules! specifier_boilerplate {
272    (singleconnect $($e:tt)*) => {
273        fn is_multiconnect(&self) -> bool { false }
274        specifier_boilerplate!($($e)*);
275    };
276    (multiconnect $($e:tt)*) => {
277        fn is_multiconnect(&self) -> bool { true }
278        specifier_boilerplate!($($e)*);
279    };
280    (no_subspec $($e:tt)*) => {
281        specifier_boilerplate!($($e)*);
282    };
283    (has_subspec $($e:tt)*) => {
284        specifier_boilerplate!($($e)*);
285    };
286    () => {
287    };
288    (globalstate $($e:tt)*) => {
289        fn uses_global_state(&self) -> bool { true }
290        specifier_boilerplate!($($e)*);
291    };
292    (noglobalstate $($e:tt)*) => {
293        fn uses_global_state(&self) -> bool { false }
294        specifier_boilerplate!($($e)*);
295    };
296}
297
298macro_rules! self_0_is_subspecifier {
299    (...) => {
300       // removed with old linter
301    };
302    (proxy_is_multiconnect) => {
303        self_0_is_subspecifier!(...);
304        fn is_multiconnect(&self) -> bool { self.0.is_multiconnect() }
305    };
306}