/__w/smoldot/smoldot/repo/light-base/src/platform.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | use alloc::borrow::Cow; |
19 | | use core::{ |
20 | | fmt, |
21 | | future::Future, |
22 | | net::{IpAddr, Ipv4Addr, Ipv6Addr}, |
23 | | ops, |
24 | | panic::UnwindSafe, |
25 | | pin::Pin, |
26 | | str, |
27 | | time::Duration, |
28 | | }; |
29 | | use futures_util::future; |
30 | | |
31 | | pub use smoldot::libp2p::read_write; |
32 | | |
33 | | #[cfg(feature = "std")] |
34 | | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
35 | | pub use smoldot::libp2p::with_buffers; |
36 | | |
37 | | // TODO: this module should probably not be public? |
38 | | pub mod address_parse; |
39 | | pub mod default; |
40 | | |
41 | | mod with_prefix; |
42 | | |
43 | | #[cfg(feature = "std")] |
44 | | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
45 | | pub use default::DefaultPlatform; |
46 | | |
47 | | pub use with_prefix::WithPrefix; |
48 | | |
49 | | /// Access to a platform's capabilities. |
50 | | /// |
51 | | /// Implementations of this trait are expected to be cheaply-clonable "handles". All clones of the |
52 | | /// same platform share the same objects. For instance, it is legal to create clone a platform, |
53 | | /// then create a connection on one clone, then access this connection on the other clone. |
54 | | pub trait PlatformRef: UnwindSafe + Clone + Send + Sync + 'static { |
55 | | /// `Future` that resolves once a certain amount of time has passed or a certain point in time |
56 | | /// is reached. See [`PlatformRef::sleep`] and [`PlatformRef::sleep_until`]. |
57 | | type Delay: Future<Output = ()> + Send + 'static; |
58 | | |
59 | | /// A certain point in time. Typically `std::time::Instant`, but one can also |
60 | | /// use `core::time::Duration`. |
61 | | /// |
62 | | /// The implementations of the `Add` and `Sub` traits must panic in case of overflow or |
63 | | /// underflow, similar to the ones of `std::time::Instant` and `core::time::Duration`. |
64 | | type Instant: Clone |
65 | | + ops::Add<Duration, Output = Self::Instant> |
66 | | + ops::Sub<Self::Instant, Output = Duration> |
67 | | + PartialOrd |
68 | | + Ord |
69 | | + PartialEq |
70 | | + Eq |
71 | | + Send |
72 | | + Sync |
73 | | + 'static; |
74 | | |
75 | | /// A multi-stream connection. |
76 | | /// |
77 | | /// This object is merely a handle. The underlying connection should be dropped only after |
78 | | /// the `MultiStream` and all its associated substream objects ([`PlatformRef::Stream`]) have |
79 | | /// been dropped. |
80 | | type MultiStream: Send + Sync + 'static; |
81 | | |
82 | | /// Opaque object representing either a single-stream connection or a substream in a |
83 | | /// multi-stream connection. |
84 | | /// |
85 | | /// If this object is abruptly dropped, the underlying single stream connection or substream |
86 | | /// should be abruptly dropped (i.e. RST) as well, unless its reading and writing sides |
87 | | /// have been gracefully closed in the past. |
88 | | type Stream: Send + 'static; |
89 | | |
90 | | /// Object that dereferences to [`read_write::ReadWrite`] and gives access to the stream's |
91 | | /// buffers. See the [`read_write`] module for more information. |
92 | | /// See also [`PlatformRef::read_write_access`]. |
93 | | type ReadWriteAccess<'a>: ops::DerefMut<Target = read_write::ReadWrite<Self::Instant>> + 'a; |
94 | | |
95 | | /// Reference to an error that happened on a stream. |
96 | | /// |
97 | | /// Potentially returned by [`PlatformRef::read_write_access`]. |
98 | | /// |
99 | | /// Typically `&'a std::io::Error`. |
100 | | type StreamErrorRef<'a>: fmt::Display + fmt::Debug; |
101 | | |
102 | | /// `Future` returned by [`PlatformRef::connect_stream`]. |
103 | | type StreamConnectFuture: Future<Output = Self::Stream> + Send + 'static; |
104 | | /// `Future` returned by [`PlatformRef::connect_multistream`]. |
105 | | type MultiStreamConnectFuture: Future<Output = MultiStreamWebRtcConnection<Self::MultiStream>> |
106 | | + Send |
107 | | + 'static; |
108 | | /// `Future` returned by [`PlatformRef::wait_read_write_again`]. |
109 | | type StreamUpdateFuture<'a>: Future<Output = ()> + Send + 'a; |
110 | | /// `Future` returned by [`PlatformRef::next_substream`]. |
111 | | type NextSubstreamFuture<'a>: Future<Output = Option<(Self::Stream, SubstreamDirection)>> |
112 | | + Send |
113 | | + 'a; |
114 | | |
115 | | /// Returns the time elapsed since [the Unix Epoch](https://en.wikipedia.org/wiki/Unix_time) |
116 | | /// (i.e. 00:00:00 UTC on 1 January 1970), ignoring leap seconds. |
117 | | /// |
118 | | /// # Panic |
119 | | /// |
120 | | /// Panics if the system time is configured to be below the UNIX epoch. This situation is a |
121 | | /// very very niche edge case that isn't worth handling. |
122 | | /// |
123 | | fn now_from_unix_epoch(&self) -> Duration; |
124 | | |
125 | | /// Returns an object that represents "now". |
126 | | fn now(&self) -> Self::Instant; |
127 | | |
128 | | /// The given buffer must be completely filled with pseudo-random bytes. |
129 | | /// |
130 | | /// # Panic |
131 | | /// |
132 | | /// Must panic if for some reason it is not possible to fill the buffer. |
133 | | /// |
134 | | fn fill_random_bytes(&self, buffer: &mut [u8]); |
135 | | |
136 | | /// Creates a future that becomes ready after at least the given duration has elapsed. |
137 | | fn sleep(&self, duration: Duration) -> Self::Delay; |
138 | | |
139 | | /// Creates a future that becomes ready after the given instant has been reached. |
140 | | fn sleep_until(&self, when: Self::Instant) -> Self::Delay; |
141 | | |
142 | | /// Spawns a task that runs indefinitely in the background. |
143 | | /// |
144 | | /// The first parameter is the name of the task, which can be useful for debugging purposes. |
145 | | /// |
146 | | /// The `Future` must be run until it yields a value. |
147 | | /// |
148 | | /// Implementers should be aware of the fact that polling the `Future` might panic (never |
149 | | /// intentionally, but in case of a bug). Many tasks can be restarted if they panic, and |
150 | | /// implementers are encouraged to absorb the panics that happen when polling. If a panic |
151 | | /// happens, the `Future` that has panicked must never be polled again. |
152 | | /// |
153 | | /// > **Note**: Ideally, the `task` parameter would require the `UnwindSafe` trait. |
154 | | /// > Unfortunately, at the time of writing of this comment, it is extremely |
155 | | /// > difficult if not impossible to implement this trait on `Future`s. It is for |
156 | | /// > the same reason that the `std::thread::spawn` function of the standard library |
157 | | /// > doesn't require its parameter to implement `UnwindSafe`. |
158 | | fn spawn_task( |
159 | | &self, |
160 | | task_name: Cow<str>, |
161 | | task: impl future::Future<Output = ()> + Send + 'static, |
162 | | ); |
163 | | |
164 | | /// Emit a log line. |
165 | | /// |
166 | | /// The `log_level` and `log_target` can be used in order to filter desired log lines. |
167 | | /// |
168 | | /// The `message` is typically a short constant message indicating the nature of the log line. |
169 | | /// The `key_values` are a list of keys and values that are the parameters of the log message. |
170 | | /// |
171 | | /// For example, `message` can be `"block-announce-received"` and `key_values` can contain |
172 | | /// the entries `("block_hash", ...)` and `("sender", ...)`. |
173 | | /// |
174 | | /// At the time of writing of this comment, `message` can also be a message written in plain |
175 | | /// english and destined to the user of the library. This might disappear in the future. |
176 | | // TODO: act on this last sentence ^ |
177 | | fn log<'a>( |
178 | | &self, |
179 | | log_level: LogLevel, |
180 | | log_target: &'a str, |
181 | | message: &'a str, |
182 | | key_values: impl Iterator<Item = (&'a str, &'a dyn fmt::Display)>, |
183 | | ); |
184 | | |
185 | | /// Value returned when a JSON-RPC client requests the name of the client, or when a peer |
186 | | /// performs an identification request. Reasonable value is `env!("CARGO_PKG_NAME")`. |
187 | | fn client_name(&self) -> Cow<str>; |
188 | | |
189 | | /// Value returned when a JSON-RPC client requests the version of the client, or when a peer |
190 | | /// performs an identification request. Reasonable value is `env!("CARGO_PKG_VERSION")`. |
191 | | fn client_version(&self) -> Cow<str>; |
192 | | |
193 | | /// Returns `true` if [`PlatformRef::connect_stream`] or [`PlatformRef::connect_multistream`] |
194 | | /// accepts a connection of the corresponding type. |
195 | | /// |
196 | | /// > **Note**: This function is meant to be pure. Implementations are expected to always |
197 | | /// > return the same value for the same [`ConnectionType`] input. Enabling or |
198 | | /// > disabling certain connection types after start-up is not supported. |
199 | | fn supports_connection_type(&self, connection_type: ConnectionType) -> bool; |
200 | | |
201 | | /// Starts a connection attempt to the given address. |
202 | | /// |
203 | | /// This function is only ever called with an `address` of a type for which |
204 | | /// [`PlatformRef::supports_connection_type`] returned `true` in the past. |
205 | | /// |
206 | | /// This function returns a `Future`. This `Future` **must** return as soon as possible, and |
207 | | /// must **not** wait for the connection to be established. |
208 | | /// In most scenarios, the `Future` returned by this function should immediately produce |
209 | | /// an output. |
210 | | /// |
211 | | /// # Panic |
212 | | /// |
213 | | /// The function implementation panics if [`Address`] is of a type for which |
214 | | /// [`PlatformRef::supports_connection_type`] returns `false`. |
215 | | /// |
216 | | fn connect_stream(&self, address: Address) -> Self::StreamConnectFuture; |
217 | | |
218 | | /// Starts a connection attempt to the given address. |
219 | | /// |
220 | | /// This function is only ever called with an `address` of a type for which |
221 | | /// [`PlatformRef::supports_connection_type`] returned `true` in the past. |
222 | | /// |
223 | | /// > **Note**: A so-called "multistream connection" is a connection made of multiple |
224 | | /// > substreams, and for which the opening and closing or substreams is handled by |
225 | | /// > the environment rather than by smoldot itself. Most platforms do not need to |
226 | | /// > support multistream connections. This function is in practice used in order |
227 | | /// > to support WebRTC connections when embedding smoldot-light within a web |
228 | | /// > browser. |
229 | | /// |
230 | | /// This function returns a `Future`. This `Future` **must** return as soon as possible, and |
231 | | /// must **not** wait for the connection to be established. |
232 | | /// In most scenarios, the `Future` returned by this function should immediately produce |
233 | | /// an output. |
234 | | /// |
235 | | /// # Panic |
236 | | /// |
237 | | /// The function implementation panics if [`MultiStreamAddress`] is of a type for which |
238 | | /// [`PlatformRef::supports_connection_type`] returns `false`. |
239 | | /// |
240 | | fn connect_multistream(&self, address: MultiStreamAddress) -> Self::MultiStreamConnectFuture; |
241 | | |
242 | | /// Queues the opening of an additional outbound substream. |
243 | | /// |
244 | | /// The substream, once opened, must be yielded by [`PlatformRef::next_substream`]. |
245 | | /// |
246 | | /// Calls to this function should be ignored if the connection has already been killed by the |
247 | | /// remote. |
248 | | /// |
249 | | /// > **Note**: No mechanism exists in this API to handle the situation where a substream fails |
250 | | /// > to open, as this is not supposed to happen. If you need to handle such a |
251 | | /// > situation, either try again opening a substream again or reset the entire |
252 | | /// > connection. |
253 | | fn open_out_substream(&self, connection: &mut Self::MultiStream); |
254 | | |
255 | | /// Waits until a new incoming substream arrives on the connection. |
256 | | /// |
257 | | /// This returns both inbound and outbound substreams. Outbound substreams should only be |
258 | | /// yielded once for every call to [`PlatformRef::open_out_substream`]. |
259 | | /// |
260 | | /// The future can also return `None` if the connection has been killed by the remote. If |
261 | | /// the future returns `None`, the user of the `PlatformRef` should drop the `MultiStream` and |
262 | | /// all its associated `Stream`s as soon as possible. |
263 | | fn next_substream<'a>( |
264 | | &self, |
265 | | connection: &'a mut Self::MultiStream, |
266 | | ) -> Self::NextSubstreamFuture<'a>; |
267 | | |
268 | | /// Returns an object that implements `DerefMut<Target = ReadWrite>`. The |
269 | | /// [`read_write::ReadWrite`] object allows the API user to read data from the stream and write |
270 | | /// data to the stream. |
271 | | /// |
272 | | /// If the stream has been reset in the past, this function should return a reference to |
273 | | /// the error that happened. |
274 | | /// |
275 | | /// See the documentation of [`read_write`] for more information |
276 | | /// |
277 | | /// > **Note**: The `with_buffers` module provides a helper to more easily implement this |
278 | | /// > function. |
279 | | fn read_write_access<'a>( |
280 | | &self, |
281 | | stream: Pin<&'a mut Self::Stream>, |
282 | | ) -> Result<Self::ReadWriteAccess<'a>, Self::StreamErrorRef<'a>>; |
283 | | |
284 | | /// Returns a future that becomes ready when [`PlatformRef::read_write_access`] should be |
285 | | /// called again on this stream. |
286 | | /// |
287 | | /// Must always returned immediately if [`PlatformRef::read_write_access`] has never been |
288 | | /// called on this stream. |
289 | | /// |
290 | | /// See the documentation of [`read_write`] for more information. |
291 | | /// |
292 | | /// > **Note**: The `with_buffers` module provides a helper to more easily implement this |
293 | | /// > function. |
294 | | fn wait_read_write_again<'a>( |
295 | | &self, |
296 | | stream: Pin<&'a mut Self::Stream>, |
297 | | ) -> Self::StreamUpdateFuture<'a>; |
298 | | } |
299 | | |
300 | | /// Log level of a log entry. |
301 | | /// |
302 | | /// See [`PlatformRef::log`]. |
303 | | #[derive(Debug)] |
304 | | pub enum LogLevel { |
305 | | Error = 1, |
306 | | Warn = 2, |
307 | | Info = 3, |
308 | | Debug = 4, |
309 | | Trace = 5, |
310 | | } |
311 | | |
312 | | /// Established multistream connection information. See [`PlatformRef::connect_multistream`]. |
313 | | #[derive(Debug)] |
314 | | pub struct MultiStreamWebRtcConnection<TConnection> { |
315 | | /// Object representing the WebRTC connection. |
316 | | pub connection: TConnection, |
317 | | /// SHA256 hash of the TLS certificate used by the local node at the DTLS layer. |
318 | | pub local_tls_certificate_sha256: [u8; 32], |
319 | | } |
320 | | |
321 | | /// Direction in which a substream has been opened. See [`PlatformRef::next_substream`]. |
322 | | #[derive(Debug)] |
323 | | pub enum SubstreamDirection { |
324 | | /// Substream has been opened by the remote. |
325 | | Inbound, |
326 | | /// Substream has been opened locally in response to [`PlatformRef::open_out_substream`]. |
327 | | Outbound, |
328 | | } |
329 | | |
330 | | /// Connection type passed to [`PlatformRef::supports_connection_type`]. |
331 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
332 | | pub enum ConnectionType { |
333 | | /// TCP/IP connection. |
334 | | TcpIpv4, |
335 | | /// TCP/IP connection. |
336 | | TcpIpv6, |
337 | | /// TCP/IP connection. |
338 | | TcpDns, |
339 | | /// Non-secure WebSocket connection. |
340 | | WebSocketIpv4 { |
341 | | /// `true` if the target of the connection is `localhost`. |
342 | | /// |
343 | | /// > **Note**: Some platforms (namely browsers) sometimes only accept non-secure WebSocket |
344 | | /// > connections only towards `localhost`. |
345 | | remote_is_localhost: bool, |
346 | | }, |
347 | | /// Non-secure WebSocket connection. |
348 | | WebSocketIpv6 { |
349 | | /// `true` if the target of the connection is `localhost`. |
350 | | /// |
351 | | /// > **Note**: Some platforms (namely browsers) sometimes only accept non-secure WebSocket |
352 | | /// > connections only towards `localhost`. |
353 | | remote_is_localhost: bool, |
354 | | }, |
355 | | /// WebSocket connection. |
356 | | WebSocketDns { |
357 | | /// `true` for WebSocket secure connections. |
358 | | secure: bool, |
359 | | /// `true` if the target of the connection is `localhost`. |
360 | | /// |
361 | | /// > **Note**: Some platforms (namely browsers) sometimes only accept non-secure WebSocket |
362 | | /// > connections only towards `localhost`. |
363 | | remote_is_localhost: bool, |
364 | | }, |
365 | | /// Libp2p-specific WebRTC flavour. |
366 | | WebRtcIpv4, |
367 | | /// Libp2p-specific WebRTC flavour. |
368 | | WebRtcIpv6, |
369 | | } |
370 | | |
371 | | impl<'a> From<&'a Address<'a>> for ConnectionType { |
372 | 0 | fn from(address: &'a Address<'a>) -> ConnectionType { |
373 | 0 | match address { |
374 | | Address::TcpIp { |
375 | | ip: IpAddr::V4(_), .. |
376 | 0 | } => ConnectionType::TcpIpv4, |
377 | | Address::TcpIp { |
378 | | ip: IpAddr::V6(_), .. |
379 | 0 | } => ConnectionType::TcpIpv6, |
380 | 0 | Address::TcpDns { .. } => ConnectionType::TcpDns, |
381 | | Address::WebSocketIp { |
382 | 0 | ip: IpAddr::V4(ip), .. |
383 | 0 | } => ConnectionType::WebSocketIpv4 { |
384 | 0 | remote_is_localhost: Ipv4Addr::from(*ip).is_loopback(), |
385 | 0 | }, |
386 | | Address::WebSocketIp { |
387 | 0 | ip: IpAddr::V6(ip), .. |
388 | 0 | } => ConnectionType::WebSocketIpv6 { |
389 | 0 | remote_is_localhost: Ipv6Addr::from(*ip).is_loopback(), |
390 | 0 | }, |
391 | | Address::WebSocketDns { |
392 | 0 | hostname, secure, .. |
393 | 0 | } => ConnectionType::WebSocketDns { |
394 | 0 | secure: *secure, |
395 | 0 | remote_is_localhost: hostname.eq_ignore_ascii_case("localhost"), |
396 | 0 | }, |
397 | | } |
398 | 0 | } Unexecuted instantiation: _RNvXNtCsiGub1lfKphe_13smoldot_light8platformNtB2_14ConnectionTypeINtNtCsaYZPK01V26L_4core7convert4FromRNtB2_7AddressE4from Unexecuted instantiation: _RNvXNtCsih6EgvAwZF2_13smoldot_light8platformNtB2_14ConnectionTypeINtNtCsaYZPK01V26L_4core7convert4FromRNtB2_7AddressE4from |
399 | | } |
400 | | |
401 | | impl<'a> From<&'a MultiStreamAddress<'a>> for ConnectionType { |
402 | 0 | fn from(address: &'a MultiStreamAddress) -> ConnectionType { |
403 | 0 | match address { |
404 | | MultiStreamAddress::WebRtc { |
405 | | ip: IpAddr::V4(_), .. |
406 | 0 | } => ConnectionType::WebRtcIpv4, |
407 | | MultiStreamAddress::WebRtc { |
408 | | ip: IpAddr::V6(_), .. |
409 | 0 | } => ConnectionType::WebRtcIpv6, |
410 | | } |
411 | 0 | } Unexecuted instantiation: _RNvXs_NtCsiGub1lfKphe_13smoldot_light8platformNtB4_14ConnectionTypeINtNtCsaYZPK01V26L_4core7convert4FromRNtB4_18MultiStreamAddressE4from Unexecuted instantiation: _RNvXs_NtCsih6EgvAwZF2_13smoldot_light8platformNtB4_14ConnectionTypeINtNtCsaYZPK01V26L_4core7convert4FromRNtB4_18MultiStreamAddressE4from |
412 | | } |
413 | | |
414 | | /// Address passed to [`PlatformRef::connect_stream`]. |
415 | | // TODO: we don't differentiate between Dns4 and Dns6 |
416 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
417 | | pub enum Address<'a> { |
418 | | /// TCP/IP connection with a domain name. |
419 | | TcpDns { |
420 | | /// DNS hostname to connect to. |
421 | | /// |
422 | | /// > **Note**: According to RFC2181 section 11, a domain name is not necessarily an UTF-8 |
423 | | /// > string. Any binary data can be used as a domain name, provided it follows |
424 | | /// > a few restrictions (notably its length). However, in the context of the |
425 | | /// > [`PlatformRef`] trait, we automatically consider as non-supported a |
426 | | /// > multiaddress that contains a non-UTF-8 domain name, for the sake of |
427 | | /// > simplicity. |
428 | | hostname: &'a str, |
429 | | /// TCP port to connect to. |
430 | | port: u16, |
431 | | }, |
432 | | |
433 | | /// TCP/IP connection with an IP address. |
434 | | TcpIp { |
435 | | /// IP address to connect to. |
436 | | ip: IpAddr, |
437 | | /// TCP port to connect to. |
438 | | port: u16, |
439 | | }, |
440 | | |
441 | | /// WebSocket connection with an IP address. |
442 | | WebSocketIp { |
443 | | /// IP address to connect to. |
444 | | ip: IpAddr, |
445 | | /// TCP port to connect to. |
446 | | port: u16, |
447 | | }, |
448 | | |
449 | | /// WebSocket connection with a domain name. |
450 | | WebSocketDns { |
451 | | /// DNS hostname to connect to. |
452 | | /// |
453 | | /// > **Note**: According to RFC2181 section 11, a domain name is not necessarily an UTF-8 |
454 | | /// > string. Any binary data can be used as a domain name, provided it follows |
455 | | /// > a few restrictions (notably its length). However, in the context of the |
456 | | /// > [`PlatformRef`] trait, we automatically consider as non-supported a |
457 | | /// > multiaddress that contains a non-UTF-8 domain name, for the sake of |
458 | | /// > simplicity. |
459 | | hostname: &'a str, |
460 | | /// TCP port to connect to. |
461 | | port: u16, |
462 | | /// `true` for WebSocket secure connections. |
463 | | secure: bool, |
464 | | }, |
465 | | } |
466 | | |
467 | | /// Address passed to [`PlatformRef::connect_multistream`]. |
468 | | // TODO: we don't differentiate between Dns4 and Dns6 |
469 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
470 | | pub enum MultiStreamAddress<'a> { |
471 | | /// Libp2p-specific WebRTC flavour. |
472 | | /// |
473 | | /// The implementation the [`PlatformRef`] trait is responsible for opening the SCTP |
474 | | /// connection. The API user of the [`PlatformRef`] trait is responsible for opening the first |
475 | | /// data channel and performing the Noise handshake. |
476 | | // TODO: maybe explain more what the implementation is supposed to do? |
477 | | WebRtc { |
478 | | /// IP address to connect to. |
479 | | ip: IpAddr, |
480 | | /// UDP port to connect to. |
481 | | port: u16, |
482 | | /// SHA-256 hash of the target's WebRTC certificate. |
483 | | remote_certificate_sha256: &'a [u8; 32], |
484 | | }, |
485 | | } |
486 | | |
487 | | // TODO: find a way to keep this private somehow? |
488 | | #[macro_export] |
489 | | macro_rules! log_inner { |
490 | | () => { |
491 | | core::iter::empty() |
492 | | }; |
493 | | (,) => { |
494 | | core::iter::empty() |
495 | | }; |
496 | | ($key:ident = $value:expr) => { |
497 | | $crate::log_inner!($key = $value,) |
498 | | }; |
499 | | ($key:ident = ?$value:expr) => { |
500 | | $crate::log_inner!($key = ?$value,) |
501 | | }; |
502 | | ($key:ident) => { |
503 | | $crate::log_inner!($key = $key,) |
504 | | }; |
505 | | (?$key:ident) => { |
506 | | $crate::log_inner!($key = ?$key,) |
507 | | }; |
508 | | ($key:ident = $value:expr, $($rest:tt)*) => { |
509 | | core::iter::once((stringify!($key), &$value as &dyn core::fmt::Display)).chain($crate::log_inner!($($rest)*)) |
510 | | }; |
511 | | ($key:ident = ?$value:expr, $($rest:tt)*) => { |
512 | | core::iter::once((stringify!($key), &format_args!("{:?}", $value) as &dyn core::fmt::Display)).chain($crate::log_inner!($($rest)*)) |
513 | | }; |
514 | | ($key:ident, $($rest:tt)*) => { |
515 | | $crate::log_inner!($key = $key, $($rest)*) |
516 | | }; |
517 | | (?$key:ident, $($rest:tt)*) => { |
518 | | $crate::log_inner!($key = ?$key, $($rest)*) |
519 | | }; |
520 | | } |
521 | | |
522 | | /// Helper macro for using the [`crate::platform::PlatformRef::log`] function. |
523 | | /// |
524 | | /// This macro takes at least 4 parameters: |
525 | | /// |
526 | | /// - A reference to an implementation of the [`crate::platform::PlatformRef`] trait. |
527 | | /// - A log level: `Error`, `Warn`, `Info`, `Debug`, or `Trace`. See [`crate::platform::LogLevel`]. |
528 | | /// - A `&str` representing the target of the logging. This can be used in order to filter log |
529 | | /// entries belonging to a specific target. |
530 | | /// - An object that implements of `AsRef<str>` and that contains the log message itself. |
531 | | /// |
532 | | /// In addition to these parameters, the macro accepts an unlimited number of extra |
533 | | /// (comma-separated) parameters. |
534 | | /// |
535 | | /// Each parameter has one of these four syntaxes: |
536 | | /// |
537 | | /// - `key = value`, where `key` is an identifier and `value` an expression that implements the |
538 | | /// `Display` trait. |
539 | | /// - `key = ?value`, where `key` is an identifier and `value` an expression that implements |
540 | | /// the `Debug` trait. |
541 | | /// - `key`, which is syntax sugar for `key = key`. |
542 | | /// - `?key`, which is syntax sugar for `key = ?key`. |
543 | | /// |
544 | | #[macro_export] |
545 | | macro_rules! log { |
546 | | ($plat:expr, $level:ident, $target:expr, $message:expr) => { |
547 | | log!($plat, $level, $target, $message,) |
548 | | }; |
549 | | ($plat:expr, $level:ident, $target:expr, $message:expr, $($params:tt)*) => { |
550 | | $crate::platform::PlatformRef::log( |
551 | | $plat, |
552 | | $crate::platform::LogLevel::$level, |
553 | | $target, |
554 | | AsRef::<str>::as_ref(&$message), |
555 | | $crate::log_inner!($($params)*) |
556 | | ) |
557 | | }; |
558 | | } |