1use 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 = "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 #[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 #[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 #[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 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#[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}