eva_common/
lib.rs

1//#![cfg_attr(feature = "nostd", no_std)]
2
3//#[cfg(feature = "ext")]
4//#[macro_use]
5//extern crate lazy_static;
6
7use crate::value::{to_value, Value};
8#[cfg(feature = "axum")]
9use axum::http::StatusCode;
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11use serde_repr::{Deserialize_repr, Serialize_repr};
12use std::borrow::Cow;
13use std::cmp::Ordering;
14use std::collections::{BTreeMap, HashSet};
15use std::convert::{TryFrom, TryInto};
16use std::fmt;
17use std::hash::{BuildHasher, Hash, Hasher};
18use std::str::FromStr;
19use std::time::Duration;
20
21pub const LOG_LEVEL_TRACE: u8 = 0;
22pub const LOG_LEVEL_DEBUG: u8 = 10;
23pub const LOG_LEVEL_INFO: u8 = 20;
24pub const LOG_LEVEL_WARN: u8 = 30;
25pub const LOG_LEVEL_ERROR: u8 = 40;
26pub const LOG_LEVEL_OFF: u8 = 100;
27
28#[inline]
29pub fn log_level_code(level: log::Level) -> u8 {
30    match level {
31        log::Level::Trace => LOG_LEVEL_TRACE,
32        log::Level::Debug => LOG_LEVEL_DEBUG,
33        log::Level::Info => LOG_LEVEL_INFO,
34        log::Level::Warn => LOG_LEVEL_WARN,
35        log::Level::Error => LOG_LEVEL_ERROR,
36    }
37}
38
39pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
40
41pub mod op;
42mod runtime_tests;
43pub mod tools;
44
45#[allow(unused_imports)]
46pub use runtime_tests::self_test;
47
48#[cfg(feature = "acl")]
49pub mod acl;
50#[cfg(feature = "actions")]
51pub mod actions;
52#[cfg(feature = "cache")]
53pub mod cache;
54#[cfg(feature = "common-payloads")]
55pub mod common_payloads;
56#[cfg(feature = "console-logger")]
57pub mod console_logger;
58#[cfg(feature = "db")]
59pub mod db;
60#[cfg(feature = "data-objects")]
61pub mod dobj;
62#[cfg(any(feature = "events", feature = "common-payloads", feature = "logger"))]
63pub mod events;
64//#[cfg(feature = "ext")]
65//pub mod ext;
66#[cfg(feature = "hyper-tools")]
67pub mod hyper_tools;
68#[cfg(feature = "logger")]
69pub mod logger;
70#[cfg(feature = "logic")]
71pub mod logic;
72#[cfg(feature = "multimedia")]
73pub mod multimedia;
74#[cfg(feature = "payload")]
75pub mod payload;
76#[cfg(feature = "registry")]
77pub mod registry;
78#[cfg(feature = "serde-keyvalue")]
79pub mod serde_keyvalue;
80#[cfg(feature = "services")]
81pub mod services;
82#[cfg(feature = "time")]
83pub mod time;
84pub mod transform;
85#[cfg(feature = "workers")]
86pub mod workers;
87
88pub mod value;
89
90pub mod prelude {
91    pub use crate::value::to_value;
92    pub use crate::value::Value;
93    pub use crate::value::ValueOption;
94    pub use crate::value::ValueOptionOwned;
95    pub use crate::EResult;
96    pub use crate::Error;
97    pub use crate::ErrorKind;
98    pub use crate::ItemKind;
99    pub use crate::ItemStatus;
100    pub use crate::IEID;
101    pub use crate::OID;
102}
103
104static ERR_INVALID_OID: &str = "Invalid OID format";
105static ERR_OID_TOO_LONG: &str = "OID too long";
106
107pub const SLEEP_STEP: Duration = Duration::from_millis(100);
108
109#[inline]
110pub fn get_default_sleep_step() -> Duration {
111    SLEEP_STEP
112}
113
114pub type EResult<T> = std::result::Result<T, Error>;
115
116pub type ItemStatus = i16;
117
118pub const ITEM_STATUS_ERROR: i16 = -1;
119
120pub const ERR_CODE_NOT_FOUND: i16 = -32001;
121pub const ERR_CODE_ACCESS_DENIED: i16 = -32002;
122pub const ERR_CODE_SYSTEM_ERROR: i16 = -32003;
123pub const ERR_CODE_OTHER: i16 = -32004;
124pub const ERR_CODE_NOT_READY: i16 = -32005;
125pub const ERR_CODE_UNSUPPORTED: i16 = -32006;
126pub const ERR_CODE_CORE_ERROR: i16 = -32007;
127pub const ERR_CODE_TIMEOUT: i16 = -32008;
128pub const ERR_CODE_INVALID_DATA: i16 = -32009;
129pub const ERR_CODE_FUNC_FAILED: i16 = -32010;
130pub const ERR_CODE_ABORTED: i16 = -32011;
131pub const ERR_CODE_ALREADY_EXISTS: i16 = -32012;
132pub const ERR_CODE_BUSY: i16 = -32013;
133pub const ERR_CODE_METHOD_NOT_IMPLEMENTED: i16 = -32014;
134pub const ERR_CODE_TOKEN_RESTRICTED: i16 = -32015;
135pub const ERR_CODE_IO: i16 = -32016;
136pub const ERR_CODE_REGISTRY: i16 = -32017;
137pub const ERR_CODE_EVAHI_AUTH_REQUIRED: i16 = -32018;
138
139pub const ERR_CODE_ACCESS_DENIED_MORE_DATA_REQUIRED: i16 = -32022;
140
141pub const ERR_CODE_PARSE: i16 = -32700;
142pub const ERR_CODE_INVALID_REQUEST: i16 = -32600;
143pub const ERR_CODE_METHOD_NOT_FOUND: i16 = -32601;
144pub const ERR_CODE_INVALID_PARAMS: i16 = -32602;
145pub const ERR_CODE_INTERNAL_RPC: i16 = -32603;
146
147pub const ERR_CODE_BUS_CLIENT_NOT_REGISTERED: i16 = -32113;
148pub const ERR_CODE_BUS_DATA: i16 = -32114;
149pub const ERR_CODE_BUS_IO: i16 = -32115;
150pub const ERR_CODE_BUS_OTHER: i16 = -32116;
151pub const ERR_CODE_BUS_NOT_SUPPORTED: i16 = -32117;
152pub const ERR_CODE_BUS_BUSY: i16 = -32118;
153pub const ERR_CODE_BUS_NOT_DELIVERED: i16 = -32119;
154pub const ERR_CODE_BUS_TIMEOUT: i16 = -32120;
155pub const ERR_CODE_BUS_ACCESS: i16 = -32121;
156
157pub const WILDCARD: &[&str] = &["#", "*"];
158pub const MATCH_ANY: &[&str] = &["+", "?"];
159
160#[inline]
161pub fn is_str_wildcard(s: &str) -> bool {
162    WILDCARD.contains(&s)
163}
164#[inline]
165pub fn is_str_any(s: &str) -> bool {
166    MATCH_ANY.contains(&s)
167}
168
169#[derive(Serialize_repr, Deserialize_repr, Eq, PartialEq, Debug, Copy, Clone)]
170#[repr(i16)]
171pub enum ErrorKind {
172    CoreError = ERR_CODE_CORE_ERROR,
173    Unsupported = ERR_CODE_UNSUPPORTED,
174    NotReady = ERR_CODE_NOT_READY,
175    IOError = ERR_CODE_IO,
176    RegistryError = ERR_CODE_REGISTRY,
177    InvalidData = ERR_CODE_INVALID_DATA,
178    FunctionFailed = ERR_CODE_FUNC_FAILED,
179    ResourceNotFound = ERR_CODE_NOT_FOUND,
180    ResourceBusy = ERR_CODE_BUSY,
181    ResourceAlreadyExists = ERR_CODE_ALREADY_EXISTS,
182    AccessDenied = ERR_CODE_ACCESS_DENIED,
183    AccessDeniedMoreDataRequired = ERR_CODE_ACCESS_DENIED_MORE_DATA_REQUIRED,
184    MethodNotImplemented = ERR_CODE_METHOD_NOT_IMPLEMENTED,
185    MethodNotFound = ERR_CODE_METHOD_NOT_FOUND,
186    InvalidParameter = ERR_CODE_INVALID_PARAMS,
187    Timeout = ERR_CODE_TIMEOUT,
188    Aborted = ERR_CODE_ABORTED,
189    EvaHIAuthenticationRequired = ERR_CODE_EVAHI_AUTH_REQUIRED,
190    TokenRestricted = ERR_CODE_TOKEN_RESTRICTED,
191    BusClientNotRegistered = ERR_CODE_BUS_CLIENT_NOT_REGISTERED,
192    BusData = ERR_CODE_BUS_DATA,
193    BusIo = ERR_CODE_BUS_IO,
194    BusOther = ERR_CODE_BUS_OTHER,
195    BusNotSupported = ERR_CODE_BUS_NOT_SUPPORTED,
196    BusBusy = ERR_CODE_BUS_BUSY,
197    BusNotDelivered = ERR_CODE_BUS_NOT_DELIVERED,
198    BusTimeout = ERR_CODE_BUS_TIMEOUT,
199    BusAccess = ERR_CODE_BUS_ACCESS,
200    Other = ERR_CODE_OTHER,
201}
202
203impl From<i16> for ErrorKind {
204    fn from(code: i16) -> ErrorKind {
205        match code {
206            x if x == ErrorKind::CoreError as i16 => ErrorKind::CoreError,
207            x if x == ErrorKind::Unsupported as i16 => ErrorKind::Unsupported,
208            x if x == ErrorKind::IOError as i16 => ErrorKind::IOError,
209            x if x == ErrorKind::RegistryError as i16 => ErrorKind::RegistryError,
210            x if x == ErrorKind::InvalidData as i16 => ErrorKind::InvalidData,
211            x if x == ErrorKind::FunctionFailed as i16 => ErrorKind::FunctionFailed,
212            x if x == ErrorKind::ResourceNotFound as i16 => ErrorKind::ResourceNotFound,
213            x if x == ErrorKind::ResourceBusy as i16 => ErrorKind::ResourceBusy,
214            x if x == ErrorKind::ResourceAlreadyExists as i16 => ErrorKind::ResourceAlreadyExists,
215            x if x == ErrorKind::AccessDenied as i16 => ErrorKind::AccessDenied,
216            x if x == ErrorKind::AccessDeniedMoreDataRequired as i16 => {
217                ErrorKind::AccessDeniedMoreDataRequired
218            }
219            x if x == ErrorKind::MethodNotImplemented as i16 => ErrorKind::MethodNotImplemented,
220            x if x == ErrorKind::MethodNotFound as i16 => ErrorKind::MethodNotFound,
221            x if x == ErrorKind::InvalidParameter as i16 => ErrorKind::InvalidParameter,
222            x if x == ErrorKind::Timeout as i16 => ErrorKind::Timeout,
223            x if x == ErrorKind::Aborted as i16 => ErrorKind::Aborted,
224            x if x == ErrorKind::EvaHIAuthenticationRequired as i16 => {
225                ErrorKind::EvaHIAuthenticationRequired
226            }
227            x if x == ErrorKind::TokenRestricted as i16 => ErrorKind::TokenRestricted,
228            x if x == ErrorKind::Other as i16 => ErrorKind::Other,
229            x if x == ErrorKind::NotReady as i16 => ErrorKind::NotReady,
230            x if x == ErrorKind::BusClientNotRegistered as i16 => ErrorKind::BusClientNotRegistered,
231            x if x == ErrorKind::BusData as i16 => ErrorKind::BusData,
232            x if x == ErrorKind::BusIo as i16 => ErrorKind::BusIo,
233            x if x == ErrorKind::BusOther as i16 => ErrorKind::BusOther,
234            x if x == ErrorKind::BusNotSupported as i16 => ErrorKind::BusNotSupported,
235            x if x == ErrorKind::BusBusy as i16 => ErrorKind::BusBusy,
236            x if x == ErrorKind::BusNotDelivered as i16 => ErrorKind::BusNotDelivered,
237            x if x == ErrorKind::BusTimeout as i16 => ErrorKind::BusTimeout,
238            x if x == ErrorKind::BusAccess as i16 => ErrorKind::BusAccess,
239            _ => ErrorKind::Other,
240        }
241    }
242}
243
244impl std::fmt::Display for ErrorKind {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        write!(
247            f,
248            "{}",
249            match self {
250                ErrorKind::CoreError => "Core error",
251                ErrorKind::Unsupported => "Unsupported",
252                ErrorKind::IOError => "IO error",
253                ErrorKind::RegistryError => "Registry error",
254                ErrorKind::InvalidData => "Invalid data",
255                ErrorKind::FunctionFailed => "Function failed",
256                ErrorKind::ResourceNotFound => "Resource not found",
257                ErrorKind::ResourceBusy => "Resource busy",
258                ErrorKind::ResourceAlreadyExists => "Resource already exists",
259                ErrorKind::AccessDenied => "Access denied",
260                ErrorKind::AccessDeniedMoreDataRequired => "Access denied, more data required",
261                ErrorKind::MethodNotImplemented => "Method not implemented",
262                ErrorKind::MethodNotFound => "Method not found",
263                ErrorKind::InvalidParameter => "Invalid parameter",
264                ErrorKind::Timeout => "Timed out",
265                ErrorKind::Aborted => "Aborted",
266                ErrorKind::EvaHIAuthenticationRequired => "EvaHI authentication required",
267                ErrorKind::TokenRestricted => "Token restricted",
268                ErrorKind::Other => "Other",
269                ErrorKind::NotReady => "Not ready",
270                ErrorKind::BusClientNotRegistered => "Bus client not registered",
271                ErrorKind::BusData => "Bus data error",
272                ErrorKind::BusIo => "Bus IO error",
273                ErrorKind::BusOther => "Bus error",
274                ErrorKind::BusNotSupported => "Bus feature not supported",
275                ErrorKind::BusBusy => "Bus busy",
276                ErrorKind::BusNotDelivered => "Bus not delivered",
277                ErrorKind::BusTimeout => "Bus timed out",
278                ErrorKind::BusAccess => "Bus op access denied",
279            }
280        )
281    }
282}
283
284#[derive(Debug, Eq, PartialEq, Clone)]
285pub struct Error {
286    kind: ErrorKind,
287    message: Option<Cow<'static, str>>,
288}
289
290impl std::error::Error for Error {}
291
292macro_rules! impl_err_error {
293    ($src: ty, $f: path) => {
294        impl From<$src> for Error {
295            fn from(err: $src) -> Error {
296                $f(err)
297            }
298        }
299    };
300}
301
302impl_err_error!(std::string::FromUtf8Error, Error::invalid_data);
303impl_err_error!(std::fmt::Error, Error::failed);
304impl_err_error!(std::str::Utf8Error, Error::invalid_data);
305impl_err_error!(std::num::ParseIntError, Error::invalid_data);
306impl_err_error!(std::num::ParseFloatError, Error::invalid_data);
307impl_err_error!(std::num::TryFromIntError, Error::invalid_data);
308impl_err_error!(ipnetwork::IpNetworkError, Error::invalid_data);
309impl_err_error!(serde_json::Error, Error::invalid_data);
310impl_err_error!(std::io::Error, Error::io);
311#[cfg(feature = "bus-rpc")]
312impl_err_error!(busrt::Error, Error::io);
313#[cfg(any(feature = "services", feature = "workers"))]
314impl_err_error!(tokio::sync::oneshot::error::RecvError, Error::io);
315#[cfg(any(feature = "services", feature = "workers"))]
316impl_err_error!(tokio::sync::TryLockError, Error::core);
317#[cfg(feature = "payload")]
318impl_err_error!(rmp_serde::encode::Error, Error::invalid_data);
319#[cfg(feature = "payload")]
320impl_err_error!(rmp_serde::decode::Error, Error::invalid_data);
321impl_err_error!(std::array::TryFromSliceError, Error::invalid_data);
322#[cfg(feature = "db")]
323impl_err_error!(yedb::Error, Error::registry);
324#[cfg(any(feature = "db", feature = "cache"))]
325impl_err_error!(sqlx::Error, Error::io);
326#[cfg(feature = "dataconv")]
327impl_err_error!(hex::FromHexError, Error::invalid_data);
328#[cfg(feature = "dataconv")]
329impl_err_error!(regex::Error, Error::invalid_data);
330#[cfg(any(feature = "actions", feature = "dataconv"))]
331impl_err_error!(uuid::Error, Error::invalid_data);
332#[cfg(feature = "openssl")]
333impl_err_error!(openssl::error::ErrorStack, Error::core);
334
335#[cfg(feature = "bus-rpc")]
336impl From<busrt::rpc::RpcError> for Error {
337    fn from(err: busrt::rpc::RpcError) -> Self {
338        Error {
339            kind: err.code().into(),
340            message: err
341                .data()
342                .map(|v| Cow::Owned(std::str::from_utf8(v).unwrap_or_default().to_owned())),
343        }
344    }
345}
346
347#[cfg(feature = "bus-rpc")]
348impl From<Error> for busrt::rpc::RpcError {
349    fn from(err: Error) -> Self {
350        busrt::rpc::RpcError::new(
351            err.kind() as i16,
352            busrt::rpc::rpc_err_str(err.message().unwrap_or_default()),
353        )
354    }
355}
356
357#[cfg(feature = "bus-rpc")]
358impl From<crate::value::SerializerError> for busrt::rpc::RpcError {
359    fn from(err: crate::value::SerializerError) -> Self {
360        busrt::rpc::RpcError::new(
361            ErrorKind::InvalidData as i16,
362            busrt::rpc::rpc_err_str(err.to_string()),
363        )
364    }
365}
366
367#[cfg(feature = "bus-rpc")]
368impl From<crate::value::DeserializerError> for busrt::rpc::RpcError {
369    fn from(err: crate::value::DeserializerError) -> Self {
370        busrt::rpc::RpcError::new(
371            ErrorKind::InvalidData as i16,
372            busrt::rpc::rpc_err_str(err.to_string()),
373        )
374    }
375}
376
377#[cfg(any(feature = "services", feature = "workers", feature = "extended-value"))]
378impl From<tokio::time::error::Elapsed> for Error {
379    fn from(_e: tokio::time::error::Elapsed) -> Error {
380        Error::timeout()
381    }
382}
383
384#[cfg(any(feature = "services", feature = "workers"))]
385impl From<tokio::task::JoinError> for Error {
386    fn from(e: tokio::task::JoinError) -> Error {
387        Error::failed(e)
388    }
389}
390
391impl From<std::convert::Infallible> for Error {
392    fn from(_err: std::convert::Infallible) -> Error {
393        panic!();
394    }
395}
396
397impl Error {
398    #[allow(clippy::must_use_candidate)]
399    pub fn new<T: fmt::Display>(kind: ErrorKind, message: T) -> Self {
400        Self {
401            kind,
402            message: Some(Cow::Owned(message.to_string())),
403        }
404    }
405
406    #[allow(clippy::must_use_candidate)]
407    pub fn new0(kind: ErrorKind) -> Self {
408        Self {
409            kind,
410            message: None,
411        }
412    }
413
414    #[allow(clippy::must_use_candidate)]
415    pub fn newc(kind: ErrorKind, message: Option<impl fmt::Display>) -> Self {
416        Self {
417            kind,
418            message: message.map(|v| Cow::Owned(v.to_string())),
419        }
420    }
421
422    pub fn code(&self) -> i16 {
423        self.kind as i16
424    }
425
426    #[allow(clippy::must_use_candidate)]
427    pub fn e<T: fmt::Display>(kind: ErrorKind, message: T) -> Self {
428        Self {
429            kind,
430            message: Some(Cow::Owned(message.to_string())),
431        }
432    }
433
434    #[allow(clippy::must_use_candidate)]
435    pub fn not_found<T: fmt::Display>(message: T) -> Self {
436        Self {
437            kind: ErrorKind::ResourceNotFound,
438            message: Some(Cow::Owned(message.to_string())),
439        }
440    }
441
442    #[allow(clippy::must_use_candidate)]
443    pub fn not_ready<T: fmt::Display>(message: T) -> Self {
444        Self {
445            kind: ErrorKind::NotReady,
446            message: Some(Cow::Owned(message.to_string())),
447        }
448    }
449
450    #[allow(clippy::must_use_candidate)]
451    pub fn unsupported<T: fmt::Display>(message: T) -> Self {
452        Self {
453            kind: ErrorKind::Unsupported,
454            message: Some(Cow::Owned(message.to_string())),
455        }
456    }
457
458    #[allow(clippy::must_use_candidate)]
459    pub fn registry<T: fmt::Display>(message: T) -> Self {
460        Self {
461            kind: ErrorKind::RegistryError,
462            message: Some(Cow::Owned(message.to_string())),
463        }
464    }
465
466    #[allow(clippy::must_use_candidate)]
467    pub fn busy<T: fmt::Display>(message: T) -> Self {
468        Self {
469            kind: ErrorKind::ResourceBusy,
470            message: Some(Cow::Owned(message.to_string())),
471        }
472    }
473
474    #[allow(clippy::must_use_candidate)]
475    pub fn core<T: fmt::Display>(message: T) -> Self {
476        Self {
477            kind: ErrorKind::CoreError,
478            message: Some(Cow::Owned(message.to_string())),
479        }
480    }
481
482    #[allow(clippy::must_use_candidate)]
483    pub fn io<T: fmt::Display>(message: T) -> Self {
484        Self {
485            kind: ErrorKind::IOError,
486            message: Some(Cow::Owned(message.to_string())),
487        }
488    }
489
490    #[allow(clippy::must_use_candidate)]
491    pub fn duplicate<T: fmt::Display>(message: T) -> Self {
492        Self {
493            kind: ErrorKind::ResourceAlreadyExists,
494            message: Some(Cow::Owned(message.to_string())),
495        }
496    }
497
498    #[allow(clippy::must_use_candidate)]
499    pub fn failed<T: fmt::Display>(message: T) -> Self {
500        Self {
501            kind: ErrorKind::FunctionFailed,
502            message: Some(Cow::Owned(message.to_string())),
503        }
504    }
505
506    #[allow(clippy::must_use_candidate)]
507    pub fn access<T: fmt::Display>(message: T) -> Self {
508        Self {
509            kind: ErrorKind::AccessDenied,
510            message: Some(Cow::Owned(message.to_string())),
511        }
512    }
513
514    #[allow(clippy::must_use_candidate)]
515    pub fn access_more_data_required<T: fmt::Display>(message: T) -> Self {
516        Self {
517            kind: ErrorKind::AccessDeniedMoreDataRequired,
518            message: Some(Cow::Owned(message.to_string())),
519        }
520    }
521
522    #[allow(clippy::must_use_candidate)]
523    pub fn timeout() -> Self {
524        Self {
525            kind: ErrorKind::Timeout,
526            message: None,
527        }
528    }
529
530    #[allow(clippy::must_use_candidate)]
531    pub fn aborted() -> Self {
532        Self {
533            kind: ErrorKind::Aborted,
534            message: None,
535        }
536    }
537
538    #[allow(clippy::must_use_candidate)]
539    pub fn invalid_data<T: fmt::Display>(message: T) -> Self {
540        Self {
541            kind: ErrorKind::InvalidData,
542            message: Some(Cow::Owned(message.to_string())),
543        }
544    }
545    fn invalid_data_static(message: &'static str) -> Self {
546        Self {
547            kind: ErrorKind::InvalidData,
548            message: Some(Cow::Borrowed(message)),
549        }
550    }
551    pub fn invalid_params<T: fmt::Display>(message: T) -> Self {
552        Self {
553            kind: ErrorKind::InvalidParameter,
554            message: Some(Cow::Owned(message.to_string())),
555        }
556    }
557    pub fn not_implemented<T: fmt::Display>(message: T) -> Self {
558        Self {
559            kind: ErrorKind::MethodNotImplemented,
560            message: Some(Cow::Owned(message.to_string())),
561        }
562    }
563    pub fn kind(&self) -> ErrorKind {
564        self.kind
565    }
566    pub fn message(&self) -> Option<&str> {
567        self.message.as_deref().map(AsRef::as_ref)
568    }
569}
570
571impl std::fmt::Display for Error {
572    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
573        if let Some(msg) = self.message.as_ref() {
574            write!(f, "{}: {}", self.kind, msg)
575        } else {
576            write!(f, "{}", self.kind)
577        }
578    }
579}
580
581#[cfg(feature = "axum")]
582impl From<Error> for (StatusCode, String) {
583    fn from(e: Error) -> Self {
584        let code = match e.kind() {
585            ErrorKind::NotReady => StatusCode::SERVICE_UNAVAILABLE,
586            ErrorKind::ResourceNotFound => StatusCode::NOT_FOUND,
587            ErrorKind::ResourceBusy => StatusCode::LOCKED,
588            ErrorKind::ResourceAlreadyExists => StatusCode::CONFLICT,
589            ErrorKind::AccessDenied
590            | ErrorKind::AccessDeniedMoreDataRequired
591            | ErrorKind::EvaHIAuthenticationRequired
592            | ErrorKind::TokenRestricted => StatusCode::FORBIDDEN,
593            ErrorKind::MethodNotFound
594            | ErrorKind::MethodNotImplemented
595            | ErrorKind::InvalidParameter => StatusCode::BAD_REQUEST,
596            ErrorKind::Timeout => StatusCode::REQUEST_TIMEOUT,
597            _ => StatusCode::INTERNAL_SERVER_ERROR,
598        };
599        (code, e.message.map(|v| v.to_string()).unwrap_or_default())
600    }
601}
602
603#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
604pub struct IEID(u64, u64);
605
606impl IEID {
607    #[allow(clippy::must_use_candidate)]
608    #[inline]
609    pub fn new(b: u64, i: u64) -> Self {
610        Self(b, i)
611    }
612    #[inline]
613    pub fn boot_id(&self) -> u64 {
614        self.0
615    }
616    #[inline]
617    pub fn is_phantom(&self) -> bool {
618        self.0 == 0
619    }
620    #[inline]
621    pub fn mark_phantom(&mut self) {
622        self.0 = 0;
623        self.1 = 0;
624    }
625    /// # Panics
626    ///
627    /// Will panic if the serializer has gone mad
628    #[allow(clippy::must_use_candidate)]
629    #[inline]
630    pub fn to_value(&self) -> Value {
631        let value_b: Value = self.0.into();
632        let value_i: Value = self.1.into();
633        to_value(vec![value_b, value_i]).unwrap()
634    }
635
636    /// Other IEID is newer than current
637    #[inline]
638    pub fn other_is_newer(&self, other: &IEID) -> bool {
639        other.0 > self.0 || (other.0 == self.0 && other.1 > self.1)
640    }
641
642    /// Other IEID is less or equal to the current
643    #[inline]
644    pub fn other_is_less_or_equal(&self, other: &IEID) -> bool {
645        other.0 < self.0 || (other.0 == self.0 && other.1 <= self.1)
646    }
647}
648
649impl TryFrom<&Value> for IEID {
650    type Error = Error;
651    fn try_from(v: &Value) -> EResult<Self> {
652        if let Value::Seq(s) = v {
653            let mut ix = s.iter();
654            let ieid_b = if let Some(b) = ix.next() {
655                b.try_into()?
656            } else {
657                return Err(Error::invalid_data("First IEID element mismatch"));
658            };
659            let ieid_i = if let Some(i) = ix.next() {
660                i.try_into()?
661            } else {
662                return Err(Error::invalid_data("Second IEID element mismatch"));
663            };
664            if ix.next().is_some() {
665                return Err(Error::invalid_data(
666                    "Incompatible IEID (more than 2 elements)",
667                ));
668            }
669            Ok(Self(ieid_b, ieid_i))
670        } else {
671            Err(Error::invalid_data("invalid value for IEID"))
672        }
673    }
674}
675
676impl PartialOrd for IEID {
677    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
678        match self.0.cmp(&other.0) {
679            Ordering::Less => Some(Ordering::Less),
680            Ordering::Greater => Some(Ordering::Greater),
681            Ordering::Equal => self.1.partial_cmp(&other.1),
682        }
683    }
684}
685
686#[derive(Clone, Eq)]
687pub struct OID {
688    kind: ItemKind,
689    oid_str: String,
690    path_str: String,
691    tpos: u16,
692    grp_pos: Option<u16>,
693}
694
695impl PartialEq for OID {
696    fn eq(&self, other: &Self) -> bool {
697        self.oid_str == other.oid_str
698    }
699}
700
701impl Ord for OID {
702    fn cmp(&self, other: &Self) -> Ordering {
703        if self.kind == other.kind {
704            self.full_id().cmp(other.full_id())
705        } else {
706            self.kind.cmp(&other.kind)
707        }
708    }
709}
710
711impl PartialOrd for OID {
712    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
713        Some(self.cmp(other))
714    }
715}
716
717pub const OID_ALLOWED_SYMBOLS: &str = "_.()[]-\\";
718pub const OID_MASK_ALLOWED_SYMBOLS: &str = "^$~_.(){}|[]-+?#*\\";
719
720pub const OID_MASK_PREFIX_FORMULA: &str = "f~";
721pub const OID_MASK_PREFIX_REGEX: &str = "r~";
722
723impl OID {
724    #[inline]
725    fn check(s: &str, is_path: bool) -> EResult<()> {
726        if s.len() > 65000 {
727            return Err(Error::invalid_data("OID too long"));
728        }
729        for c in s.chars() {
730            if !(c.is_alphanumeric() || OID_ALLOWED_SYMBOLS.contains(c) || (is_path && c == '/')) {
731                return Err(Error::invalid_data(format!("Invalid symbol in OID: {}", c)));
732            }
733        }
734        Ok(())
735    }
736    #[allow(clippy::cast_possible_truncation)]
737    pub fn new(kind: ItemKind, group: &str, id: &str) -> EResult<Self> {
738        OID::check(group, true)?;
739        OID::check(id, false)?;
740        if group == "+" || id == "+" {
741            return Err(Error::invalid_data("OID group or id can not be equal to +"));
742        }
743        let tp_str = kind.to_string();
744        if id.is_empty() || group.is_empty() {
745            Err(Error::invalid_data(ERR_INVALID_OID))
746        } else if tp_str.len() + id.len() + group.len() + 2 > u16::MAX as usize {
747            Err(Error::invalid_data(ERR_OID_TOO_LONG))
748        } else {
749            let oid_str = format!("{}:{}/{}", kind, group, id);
750            let path_str = format!("{}/{}/{}", kind, group, id);
751            let grp_pos = Some((group.len() as u16) + (tp_str.len() as u16) + 1);
752            Ok(Self {
753                kind,
754                oid_str,
755                path_str,
756                grp_pos,
757                tpos: tp_str.len() as u16 + 1,
758            })
759        }
760    }
761    #[inline]
762    pub fn new0(kind: ItemKind, id: &str) -> EResult<Self> {
763        Self::_new0(kind, id, true)
764    }
765    #[inline]
766    pub fn new0_unchecked(kind: ItemKind, id: &str) -> EResult<Self> {
767        Self::_new0(kind, id, false)
768    }
769    #[allow(clippy::cast_possible_truncation)]
770    fn _new0(kind: ItemKind, id: &str, need_check: bool) -> EResult<Self> {
771        if need_check {
772            OID::check(id, true)?;
773        }
774        let tp_str = kind.to_string();
775        if id.is_empty() {
776            Err(Error::invalid_data(ERR_INVALID_OID))
777        } else if id.len() + tp_str.len() >= u16::MAX as usize {
778            Err(Error::invalid_data(ERR_OID_TOO_LONG))
779        } else {
780            let grp_pos = id.rfind('/').map(|p| p as u16 + tp_str.len() as u16 + 1);
781            let oid_str = format!("{}:{}", kind, id);
782            let path_str = format!("{}/{}", kind, id);
783            Ok(Self {
784                kind,
785                oid_str,
786                path_str,
787                grp_pos,
788                tpos: tp_str.len() as u16 + 1,
789            })
790        }
791    }
792    #[inline]
793    pub fn id(&self) -> &str {
794        self.grp_pos.map_or_else(
795            || &self.oid_str[self.tpos as usize..],
796            |g| &self.oid_str[(g + 1) as usize..],
797        )
798    }
799    #[inline]
800    pub fn full_id(&self) -> &str {
801        &self.oid_str[self.tpos as usize..]
802    }
803    #[inline]
804    pub fn group(&self) -> Option<&str> {
805        self.grp_pos
806            .map(|g| &self.oid_str[self.tpos as usize..g as usize])
807    }
808    #[inline]
809    pub fn kind(&self) -> ItemKind {
810        self.kind
811    }
812    #[inline]
813    pub fn as_str(&self) -> &str {
814        &self.oid_str
815    }
816    #[inline]
817    pub fn as_path(&self) -> &str {
818        &self.path_str
819    }
820    #[inline]
821    pub fn is_wildcard(&self) -> bool {
822        is_str_wildcard(self.id())
823    }
824    #[inline]
825    pub fn to_wildcard_str(&self, wildcard_suffix: &str) -> String {
826        let mut s = format!("{}:", self.kind);
827        if let Some(group) = self.group() {
828            s = s + group + "/";
829        }
830        s + wildcard_suffix
831    }
832    pub fn serialize_into(&self, target: &mut BTreeMap<Value, Value>) {
833        target.insert("oid".into(), self.as_str().into());
834        //COMPAT, deprecated, remove in 4.2
835        target.insert("full_id".into(), self.full_id().into());
836        target.insert("id".into(), self.id().into());
837        target.insert("group".into(), self.group().map_or(Value::Unit, Into::into));
838        target.insert("type".into(), self.kind.into());
839    }
840    pub fn from_str_type(tp: ItemKind, s: &str) -> EResult<Self> {
841        if let Some(tpos) = s.find(':') {
842            let otp: ItemKind = s[..tpos].parse()?;
843            if otp == tp {
844                Self::new0(tp, &s[tpos + 1..])
845            } else {
846                Err(Error::invalid_data(format!(
847                    "OID type mismatch, expected: {}, found: {}",
848                    tp, otp
849                )))
850            }
851        } else {
852            OID::new0(tp, s)
853        }
854    }
855    #[inline]
856    pub fn from_path(s: &str) -> EResult<Self> {
857        Self::parse_oid(s, '/')
858    }
859    #[inline]
860    fn parse_oid(s: &str, c: char) -> EResult<Self> {
861        s.find(c).map_or(
862            Err(Error::invalid_data(format!("{}: {}", ERR_INVALID_OID, s))),
863            |tpos| {
864                let tp: ItemKind = s[..tpos].parse()?;
865                Self::new0(tp, &s[tpos + 1..])
866            },
867        )
868    }
869}
870
871impl AsRef<str> for OID {
872    fn as_ref(&self) -> &str {
873        self.as_str()
874    }
875}
876
877impl AsRef<OID> for OID {
878    fn as_ref(&self) -> &OID {
879        self
880    }
881}
882
883impl FromStr for OID {
884    type Err = Error;
885    #[inline]
886    fn from_str(s: &str) -> Result<Self, Self::Err> {
887        Self::parse_oid(s, ':')
888    }
889}
890
891impl TryFrom<&Value> for OID {
892    type Error = Error;
893    fn try_from(value: &Value) -> Result<OID, Self::Error> {
894        let s: &str = value.try_into()?;
895        s.parse()
896    }
897}
898
899impl Serialize for OID {
900    #[inline]
901    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
902    where
903        S: Serializer,
904    {
905        serializer.serialize_str(self.as_str())
906    }
907}
908
909// in case of problems with Deserializer
910#[inline]
911pub fn deserialize_oid<'de, D>(deserializer: D) -> Result<OID, D::Error>
912where
913    D: Deserializer<'de>,
914{
915    let buf = String::deserialize(deserializer)?;
916    buf.parse().map_err(serde::de::Error::custom)
917}
918
919impl<'de> Deserialize<'de> for OID {
920    #[inline]
921    fn deserialize<D>(deserializer: D) -> Result<OID, D::Error>
922    where
923        D: Deserializer<'de>,
924    {
925        let s: String = Deserialize::deserialize(deserializer)?;
926        s.parse().map_err(serde::de::Error::custom)
927    }
928}
929
930impl fmt::Display for OID {
931    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
932        write!(f, "{}", self.oid_str)
933    }
934}
935
936impl fmt::Debug for OID {
937    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
938        write!(f, "{}", self.oid_str)
939    }
940}
941
942impl Hash for OID {
943    fn hash<H: Hasher>(&self, hasher: &mut H) {
944        (self.kind as u16).hash(hasher);
945        self.full_id().hash(hasher);
946    }
947}
948
949impl From<OID> for Value {
950    fn from(oid: OID) -> Value {
951        oid.as_str().into()
952    }
953}
954
955impl From<&OID> for Value {
956    fn from(oid: &OID) -> Value {
957        oid.as_str().into()
958    }
959}
960
961impl TryFrom<Value> for OID {
962    type Error = Error;
963    fn try_from(value: Value) -> EResult<OID> {
964        match value {
965            Value::String(s) => Ok(s.parse()?),
966            _ => Err(Error::invalid_data("Expected string")),
967        }
968    }
969}
970
971impl<S: BuildHasher + Default> TryFrom<Value> for HashSet<OID, S> {
972    type Error = Error;
973    fn try_from(value: Value) -> EResult<HashSet<OID, S>> {
974        match value {
975            Value::Seq(vec) => {
976                let mut result = HashSet::default();
977                for v in vec {
978                    result.insert(v.try_into()?);
979                }
980                Ok(result)
981            }
982            Value::String(s) => {
983                let mut result = HashSet::default();
984                for v in s.split(',') {
985                    result.insert(v.parse()?);
986                }
987                Ok(result)
988            }
989            _ => Err(Error::invalid_data("Expected vec or string")),
990        }
991    }
992}
993
994impl<S: BuildHasher> From<HashSet<OID, S>> for Value {
995    fn from(v: HashSet<OID, S>) -> Value {
996        Value::Seq(v.iter().map(|oid| to_value(oid).unwrap()).collect())
997    }
998}
999
1000#[derive(Debug, Eq, PartialEq, Copy, Clone, Ord, PartialOrd, Hash)]
1001#[repr(u16)]
1002pub enum ItemKind {
1003    Unit = 100,
1004    Sensor = 101,
1005    Lvar = 200,
1006    Lmacro = 300,
1007}
1008
1009impl ItemKind {
1010    pub fn as_str(&self) -> &str {
1011        match self {
1012            ItemKind::Unit => "unit",
1013            ItemKind::Sensor => "sensor",
1014            ItemKind::Lvar => "lvar",
1015            ItemKind::Lmacro => "lmacro",
1016        }
1017    }
1018}
1019
1020impl fmt::Display for ItemKind {
1021    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1022        write!(f, "{}", self.as_str())
1023    }
1024}
1025
1026impl From<ItemKind> for Value {
1027    fn from(src: ItemKind) -> Value {
1028        src.to_string().into()
1029    }
1030}
1031
1032impl FromStr for ItemKind {
1033    type Err = Error;
1034    fn from_str(s: &str) -> Result<Self, Self::Err> {
1035        match s {
1036            "unit" | "U" => Ok(ItemKind::Unit),
1037            "sensor" | "S" => Ok(ItemKind::Sensor),
1038            "lvar" | "LV" => Ok(ItemKind::Lvar),
1039            "lmacro" | "K" => Ok(ItemKind::Lmacro),
1040            _ => Err(Error::new(
1041                ErrorKind::InvalidData,
1042                format!("Invalid item type: {}", s),
1043            )),
1044        }
1045    }
1046}
1047
1048impl TryFrom<&Value> for ItemKind {
1049    type Error = Error;
1050    fn try_from(value: &Value) -> Result<ItemKind, Self::Error> {
1051        TryInto::<&str>::try_into(value)?.parse()
1052    }
1053}
1054
1055impl TryFrom<&Value> for Vec<ItemKind> {
1056    type Error = Error;
1057    fn try_from(value: &Value) -> Result<Vec<ItemKind>, Self::Error> {
1058        let data: Vec<&str> = value.try_into()?;
1059        let mut result = Vec::new();
1060        for d in data {
1061            result.push(TryInto::<&str>::try_into(d)?.parse()?);
1062        }
1063        Ok(result)
1064    }
1065}
1066
1067impl TryFrom<Value> for ItemKind {
1068    type Error = Error;
1069    fn try_from(value: Value) -> Result<ItemKind, Self::Error> {
1070        TryInto::<String>::try_into(value)?.parse()
1071    }
1072}
1073
1074impl TryFrom<Value> for Vec<ItemKind> {
1075    type Error = Error;
1076    fn try_from(value: Value) -> Result<Vec<ItemKind>, Self::Error> {
1077        let data: Vec<String> = value.try_into()?;
1078        let mut result = Vec::new();
1079        for d in data {
1080            result.push(TryInto::<String>::try_into(d)?.parse()?);
1081        }
1082        Ok(result)
1083    }
1084}
1085
1086#[cfg(test)]
1087mod tests {
1088    use super::to_value;
1089    use super::{Error, ItemKind, Value, IEID, OID};
1090    use std::convert::TryInto;
1091
1092    #[test]
1093    fn test_oid() {
1094        let oid: OID = "sensor:env/room1/temp1".parse().unwrap();
1095        assert_eq!(oid.id(), "temp1");
1096        assert_eq!(oid.full_id(), "env/room1/temp1");
1097        assert_eq!(oid.group().unwrap(), "env/room1");
1098        assert_eq!(oid.kind, ItemKind::Sensor);
1099        assert!("sensorx:env/temp1".parse::<OID>().is_err());
1100        assert!("sensorxenv/temp1".parse::<OID>().is_err());
1101        assert!("sensorxenv/:temp1".parse::<OID>().is_err());
1102        assert!("sensor|temp1".parse::<OID>().is_err());
1103        assert!("sensor:".parse::<OID>().is_err());
1104        let oid = OID::new0(ItemKind::Sensor, "tests/test1").unwrap();
1105        assert_eq!(oid.id(), "test1");
1106        assert_eq!(oid.group().unwrap(), "tests");
1107        assert_eq!(oid.kind(), ItemKind::Sensor);
1108        let oid = OID::new0(ItemKind::Sensor, "tests/room1/test1").unwrap();
1109        assert_eq!(oid.id(), "test1");
1110        assert_eq!(oid.group().unwrap(), "tests/room1");
1111        assert_eq!(oid.kind(), ItemKind::Sensor);
1112    }
1113
1114    #[test]
1115    fn test_ieid() {
1116        assert!(IEID::new(1, 1) == IEID::new(1, 1));
1117        assert!(IEID::new(2, 1) > IEID::new(1, 9));
1118        assert!(IEID::new(2, 2) < IEID::new(3, 1));
1119        assert!(IEID::new(2, 4) > IEID::new(2, 2));
1120        assert!(IEID::new(2, 4) < IEID::new(2, 5));
1121    }
1122
1123    #[test]
1124    fn test_try_into_vec() {
1125        let v = vec!["1", "2", "3"];
1126        let value = to_value(v.clone()).unwrap();
1127        let result: Vec<&str> = (&value).try_into().unwrap();
1128        let value2: Value = "1,2,3".into();
1129        assert_eq!(result, v);
1130        let result: Vec<&str> = (&value2).try_into().unwrap();
1131        assert_eq!(result, v);
1132    }
1133
1134    #[test]
1135    fn test_try_into_bool() {
1136        assert!(TryInto::<bool>::try_into(Value::String("True".to_owned())).unwrap());
1137        assert!(TryInto::<bool>::try_into(Value::String("Trux".to_owned())).is_err());
1138        assert!(!TryInto::<bool>::try_into(Value::U64(0)).unwrap());
1139        assert!(TryInto::<bool>::try_into(Value::F64(1.0)).unwrap());
1140        assert!(TryInto::<bool>::try_into(Value::F64(2.0)).is_err());
1141    }
1142
1143    #[test]
1144    fn test_err() {
1145        assert_eq!(format!("{}", Error::timeout()), "Timed out");
1146        assert_eq!(
1147            format!("{}", Error::not_found("test")),
1148            "Resource not found: test"
1149        );
1150    }
1151}