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