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
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! # OpenThread API Module #
//!
//! This module contains (mostly) type-safe versions of the OpenThread API,
//! excluding the platform API.
//!
//! ## Type Safety ##
//!
//! Full type safety for an API which wasn't created for type safety is hard. There are some
//! operations which feel like they should be fully safe but are ultimately marked as `unsafe`
//! because it might be possible to abuse in some way that causes undefined behavior.
//!
//! ## Types ##
//!
//! Each enum, struct, or object in the OpenThread C API is associated with a safe Rust
//! equivalent. For example:
//!
//! * [`otInstance`](crate::otsys::otInstance): [`ot::Instance`](Instance).
//! * [`otMessage`](crate::otsys::otMessage): [`ot::Message<'_>`](Message).
//! * [`otError`](crate::otsys::otError): [`ot::Error`](Error) or [`Result`].
//!
//! ## Ownership ##
//!
//! Some OpenThread API objects, like [`otsys::otInstance`](crate::otsys::otInstance) and
//! [`otsys::otMessage`](crate::otsys::otMessage) have hidden implementations and explicit ownership
//! transfer. That means we must have a notation which is capable of both "owned" instances
//! and "borrowed" references.
//!
//! The rust equivalent of passing around a pointer to one of thse
//! objects would be to pass around a reference: `otInstance*` becomes a `&ot::Instance`.
//! Owned instances are "boxed" into a [`ot::Box`](Box), so an owned `otInstance*` would become
//! a `ot::Box<ot::Instance>`, or a [`OtInstanceBox`](crate::OtInstanceBox) for short. When the box
//! goes out of scope, the appropriate OpenThread C finalization API is called.
//!
//! ## Singleton/Multiple OpenThread Instances ##
//!
//! OpenThread can be compiled to either only support a singleton instance or to support
//! multiple independent OpenThread instances.
//!
//! Currently, this crate only supports a singleton OpenThread instance. Attempting to create
//! more than one OpenThread instance at a time will result in a runtime panic.
//!
//! ## Traits ##
//!
//! The OpenThread API is broken down into "modules" of functionality containing types and
//! related methods. Similarly, this Rust interface breaks down functionality into traits.
//! This allows parts of the OpenThread API to be substituted with mocked versions for unit
//! testing.
//!
//! ## Callbacks ##
//!
//! In most cases, you register a callback by passing a closure to the appropriate callback
//! registration API.
//!
//! ## Platform Implementations ##
//!
//! This crate doesn't directly provide the platform implementations for OpenThread—that
//! needs to come from either a separate library or the program which is using this crate.
//!

use openthread_sys::*;
use std::ffi::CStr;

mod cli;
pub use cli::*;

mod error;
pub use error::*;

mod srp;
pub use srp::*;

mod singleton;
pub use singleton::*;

pub(crate) mod tasklets;
pub use tasklets::*;

mod reset;
pub use reset::*;

mod link;
pub use link::*;

mod link_metrics;
pub use link_metrics::*;

pub(crate) mod types;
pub use types::*;

mod dnssd;
pub use dnssd::*;

mod thread;
pub use thread::*;

mod ip6;
pub use ip6::*;

mod state;
pub use state::*;

mod radio;
pub use radio::*;

mod uptime;
pub use uptime::*;

mod udp;
pub use udp::*;

mod border_agent;
pub use border_agent::*;

mod infra_if;
pub use infra_if::*;

pub mod message;
pub use message::{Message, MessageBuffer};

mod otbox;
pub use otbox::*;

mod dataset;
pub use dataset::*;

mod backbone_router;
pub use backbone_router::*;

mod border_router;
pub use border_router::*;

mod platform;
pub use platform::*;

mod joiner;
pub use joiner::*;

mod trel;
pub use trel::*;

mod net_data;
pub use net_data::*;

mod nat64;
pub use nat64::*;

mod dns_upstream;
pub use dns_upstream::*;

/// Trait implemented by all OpenThread instances.
pub trait InstanceInterface:
    Ip6
    + Cli
    + Reset
    + Dataset
    + Link
    + Dnssd
    + State
    + Tasklets
    + Thread
    + BackboneRouter
    + BorderRouter
    + SrpServer
    + MessageBuffer
    + Radio
    + Joiner
    + Uptime
    + Udp
    + Trel
    + BorderAgent
    + NetData
    + Nat64
    + DnsUpstream
    + LinkMetrics
{
}

/// Returns the OpenThread version string. This is the safe equivalent of
/// [`otsys::otGetVersionString()`](crate::otsys::otGetVersionString()).
pub fn get_version_string() -> &'static str {
    unsafe {
        // SAFETY: `otGetVersionString` guarantees to return a C-String that will not change.
        CStr::from_ptr(otGetVersionString())
            .to_str()
            .expect("OpenThread version string was bad UTF8")
    }
}

/// Changes the logging level.
pub fn set_logging_level(level: LogLevel) {
    unsafe {
        otLoggingSetLevel(level.into());
    }
}

/// Get the logging level.
pub fn get_logging_level() -> LogLevel {
    unsafe { otLoggingGetLevel().into() }
}

/// Represents the thread version
#[derive(
    Debug,
    Copy,
    Clone,
    Eq,
    Ord,
    PartialOrd,
    PartialEq,
    num_derive::FromPrimitive,
    num_derive::ToPrimitive,
)]
#[repr(u16)]
pub enum ThreadVersion {
    /// Thread specification version 1.1.0.
    ///
    /// This is the functional equivalent of `OT_THREAD_VERSION_1_1`, which is not currently
    /// exported as a part of the OpenThread C API.
    V1_1 = 2,

    /// Thread specification version 1.2.0.
    ///
    /// This is the functional equivalent of `OT_THREAD_VERSION_1_2`, which is not currently
    /// exported as a part of the OpenThread C API.
    V1_2 = 3,

    /// Thread specification version 1.3.0.
    ///
    /// This is the functional equivalent of `OT_THREAD_VERSION_1_3`, which is not currently
    /// exported as a part of the OpenThread C API.
    V1_3 = 4,
}

/// Returns the version of the Thread specification that OpenThread
/// is configured to use. This is the safe equivalent of
/// [`otsys::otThreadGetVersion()`](crate::otsys::otThreadGetVersion()).
pub fn get_thread_version() -> ThreadVersion {
    use num::FromPrimitive;

    // SAFETY: otThreadGetVersion() is guaranteed to be safe to call in any context.
    let ver = unsafe { otThreadGetVersion() };

    ThreadVersion::from_u16(ver).unwrap_or_else(|| panic!("Unknown Thread specification: {ver}"))
}

/// Returns a `'static`-scoped string slice describing the version of the
/// Thread specification that is currently in use.
///
/// Format is suitable for use with MeshCop.
pub fn get_thread_version_str() -> &'static str {
    match get_thread_version() {
        ThreadVersion::V1_1 => "1.1.0",
        ThreadVersion::V1_2 => "1.2.0",
        ThreadVersion::V1_3 => "1.3.0",
    }
}

/// Converts a byte string into an ASCII [`String`], properly escaped.
pub(crate) fn ascii_dump(data: &[u8]) -> String {
    let vec = data.iter().copied().flat_map(std::ascii::escape_default).collect::<Vec<_>>();
    std::str::from_utf8(&vec).unwrap().to_string()
}

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

    #[test]
    fn test_get_version() {
        let vstr = get_version_string();
        println!("OpenThread Version: {vstr:?}");
        assert!(!vstr.is_empty());
    }
}