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