use crate::events::{RawStateEvent, ReplicationInventoryItem, ReplicationStateEvent};
use crate::time::monotonic_ns;
use crate::time::Time;
use crate::{EResult, Error};
use eva_common::acl::{OIDMask, OIDMaskList};
use eva_common::prelude::*;
use eva_common::ITEM_STATUS_ERROR;
use log::warn;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::hash::{Hash, Hasher};
use std::str::Split;
use std::sync::atomic;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
#[cfg(test)]
mod tests {
#[test]
fn test_ser() {
let mut logic = super::Logic::default();
let mut range = super::Range::default();
range.max = Some(33.22);
logic.range = Some(range);
let mut opts = super::ServiceOptions {
svc: "test".to_owned(),
timeout: 5.0,
config: None,
};
opts.timeout = 33.3;
let item = std::sync::Arc::new(super::ItemData {
oid: "unit:tests/t1".parse().unwrap(),
state: None,
meta: None,
source: None,
enabled: std::sync::atomic::AtomicBool::new(true),
logic: Some(logic),
action: None,
});
let payload = serde_json::to_string(&item).unwrap();
let v: serde_json::Value = serde_json::from_str(&payload).unwrap();
let _item: super::ItemData = serde_json::from_value(v).unwrap();
let payload = rmp_serde::to_vec_named(&item).unwrap();
let _item: super::ItemData = rmp_serde::from_read_ref(&payload).unwrap();
}
#[test]
fn test_logic() {
let oid: OID = "sensor:tests/s1".parse().unwrap();
use eva_common::prelude::*;
let mut state = super::ItemState::new0(IEID::new(1, 1), EvaItemKind::Sensor);
state.value = Value::from(33.99);
assert_eq!(state.apply_logic(None, &oid, 1), false);
assert_eq!(state.status, 1);
let mut logic = super::Logic { range: None };
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
assert_eq!(state.status, 1);
logic.range = Some(super::Range {
min: Some(0.0),
max: None,
min_eq: false,
max_eq: false,
});
state.value = Value::Unit;
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
assert_eq!(state.status, -1);
state.status = 1;
state.value = Value::from(1);
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
assert_eq!(state.status, 1);
state.value = Value::from(0);
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
assert_eq!(state.status, -1);
state.status = 1;
logic.range = Some(super::Range {
min: Some(0.0),
max: None,
min_eq: true,
max_eq: false,
});
state.value = Value::from(0);
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
assert_eq!(state.status, 1);
state.value = Value::from(-1);
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
assert_eq!(state.status, -1);
state.status = 1;
logic.range = Some(super::Range {
min: Some(0.0),
max: Some(10.0),
min_eq: false,
max_eq: false,
});
state.value = Value::from(1);
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
assert_eq!(state.status, 1);
state.value = Value::from(10.0);
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
assert_eq!(state.status, -1);
state.status = 1;
logic.range = Some(super::Range {
min: Some(0.0),
max: Some(10.0),
min_eq: true,
max_eq: true,
});
state.value = Value::from(10);
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
assert_eq!(state.status, 1);
state.value = Value::from(11);
assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
assert_eq!(state.status, -1);
}
}
#[derive(Serialize)]
pub struct InventoryStats {
sources: HashMap<String, usize>,
items: usize,
}
#[derive(Debug)]
pub struct ItemSource {
node: String,
data: SourceData,
}
trait IeidX {
fn generate(boot_id: u64) -> Self;
}
impl IeidX for IEID {
#[inline]
fn generate(boot_id: u64) -> Self {
Self::new(boot_id, monotonic_ns())
}
}
#[inline]
fn create_source(source_id: &str, svc: &str) -> Source {
Arc::new(ItemSource::new(source_id, svc))
}
#[inline]
fn validate_logic(value: Option<&Value>, logic: Option<&Logic>) -> bool {
if let Some(r) = logic.and_then(|l| l.range.as_ref()) {
value.map_or(false, |v| {
if let Ok(val) = TryInto::<f64>::try_into(v) {
let mut correct = true;
if let Some(min) = r.min {
if (r.min_eq && val < min) || (!r.min_eq && val <= min) {
correct = false;
}
}
if correct {
if let Some(max) = r.max {
if (r.max_eq && val > max) || (!r.max_eq && val >= max) {
correct = false;
}
}
}
correct
} else {
false
}
})
} else {
true
}
}
impl ItemSource {
pub fn new(node: &str, svc: &str) -> Self {
Self {
node: node.to_owned(),
data: SourceData::new(svc),
}
}
#[inline]
pub fn mark_online(&self, online: bool) {
self.data.online.store(online, atomic::Ordering::SeqCst);
}
#[inline]
pub fn mark_destroyed(&self) {
self.data.destroyed.store(true, atomic::Ordering::SeqCst);
}
#[inline]
pub fn node(&self) -> &str {
&self.node
}
#[inline]
pub fn svc(&self) -> &str {
&self.data.svc
}
#[inline]
pub fn is_destroyed(&self) -> bool {
self.data.destroyed.load(atomic::Ordering::SeqCst)
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct ItemState {
status: ItemStatus,
value: Value,
#[serde(skip_deserializing)]
act: Option<usize>, ieid: IEID,
t: f64,
}
macro_rules! mark_out_of_range {
($self: expr, $oid: expr, $value: expr) => {
warn!("{} value is invalid/out of range: {:?}", $oid, $value);
$self.status = ITEM_STATUS_ERROR;
};
}
impl ItemState {
#[inline]
pub fn new0(ieid: IEID, tp: EvaItemKind) -> Self {
let (status, act) = if tp == EvaItemKind::Unit {
(0, Some(0))
} else {
(1, None)
};
Self {
status,
value: Value::Unit,
act,
ieid,
t: Time::now().timestamp(),
}
}
#[inline]
pub fn new(status: ItemStatus, value: Value, act: Option<usize>, ieid: IEID, t: f64) -> Self {
Self {
status,
value,
act,
ieid,
t,
}
}
pub fn force_set_state(&mut self, status: ItemStatus, mut value: Option<Value>, ieid: IEID) {
self.status = status;
if let Some(value) = value.take() {
self.value = value;
}
self.ieid = ieid;
self.t = Time::now().timestamp();
}
pub fn act_decr(&mut self, ieid: IEID) {
let a = self.act.unwrap_or_default();
assert!(
!(a == 0),
"core fatal error, attempt to decr zero act for the unit"
);
self.act = Some(a - 1);
self.ieid = ieid;
self.t = Time::now().timestamp();
}
pub fn act_incr(&mut self, ieid: IEID) {
self.act = Some(self.act.map_or(1, |a| a + 1));
self.ieid = ieid;
self.t = Time::now().timestamp();
}
#[inline]
pub fn set_from_rse(&mut self, mut rpl: ReplicationStateEvent) {
let status = rpl.status();
let value = rpl.take_value().unwrap_or_default();
self.status = status;
if let Some(act) = rpl.act() {
self.act = Some(act);
}
self.value = value;
self.ieid = rpl.ieid().clone();
self.t = rpl.time();
}
#[inline]
pub fn set_from_raw(
&mut self,
mut raw: RawStateEvent,
logic: Option<&Logic>,
oid: &OID,
boot_id: u64,
) -> bool {
let mut modified = false;
let status = raw.status();
let mut value = raw.take_value();
if status == ITEM_STATUS_ERROR || validate_logic(value.as_ref(), logic) {
if self.status != status {
self.status = status;
modified = true;
}
if let Some(val) = value.take() {
if self.value != val {
self.value = val;
modified = true;
}
}
} else {
mark_out_of_range!(self, oid, value);
modified = true;
}
if modified {
self.ieid = IEID::generate(boot_id);
self.t = Time::now().timestamp();
true
} else {
false
}
}
pub fn serialize_db_into(&self, result: &mut BTreeMap<Value, Value>) {
result.insert("status".into(), Value::from(self.status));
result.insert("value".into(), self.value.clone());
result.insert("ieid".into(), self.ieid.to_value());
result.insert("t".into(), Value::from(self.t));
}
#[inline]
pub fn serialize_into(&self, result: &mut BTreeMap<Value, Value>) {
self.serialize_db_into(result);
self.act
.as_ref()
.map(|v| result.insert("act".into(), Value::U64(*v as u64)));
}
#[inline]
pub fn apply_logic(&mut self, logic: Option<&Logic>, oid: &OID, boot_id: u64) -> bool {
if self.status == ITEM_STATUS_ERROR || validate_logic(Some(&self.value), logic) {
false
} else {
mark_out_of_range!(self, oid, self.value);
self.ieid = IEID::generate(boot_id);
self.t = Time::now().timestamp();
true
}
}
pub fn ieid(&self) -> &IEID {
&self.ieid
}
pub fn status(&self) -> ItemStatus {
self.status
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct Range {
#[serde(default)]
min: Option<f64>,
#[serde(default)]
max: Option<f64>,
#[serde(default)]
min_eq: bool,
#[serde(default)]
max_eq: bool,
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Logic {
range: Option<Range>, }
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ServiceOptions {
svc: String,
#[serde(default)]
timeout: f64, #[serde(default, skip_serializing_if = "Option::is_none")]
config: Option<Value>, }
impl ServiceOptions {
pub fn svc(&self) -> &str {
&self.svc
}
pub fn timeout(&self) -> Option<Duration> {
if self.timeout > 0.0 {
Some(Duration::from_secs_f64(self.timeout))
} else {
None
}
}
pub fn config(&self) -> Option<&Value> {
self.config.as_ref()
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct ItemData {
oid: OID,
#[serde(skip_serializing)]
state: Option<Mutex<ItemState>>, meta: Option<Value>, #[serde(skip)]
source: Option<Source>, enabled: atomic::AtomicBool,
logic: Option<Logic>, action: Option<ServiceOptions>, }
#[inline]
pub fn serialize_db_state_from(state: &ItemState) -> BTreeMap<Value, Value> {
let mut result = BTreeMap::new();
state.serialize_db_into(&mut result);
result
}
impl ItemData {
fn from_repl_item(i: ReplicationInventoryItem, source: Source) -> Self {
Self {
oid: i.oid,
state: Some(Mutex::new(ItemState::new(
i.status.unwrap_or_default(),
i.value.unwrap_or_default(),
i.act,
i.ieid,
i.t,
))),
meta: i.meta,
source: Some(source),
enabled: atomic::AtomicBool::new(i.enabled),
logic: None,
action: None,
}
}
#[inline]
pub fn oid(&self) -> &OID {
&self.oid
}
#[inline]
pub fn state(&self) -> Option<&Mutex<ItemState>> {
self.state.as_ref()
}
#[inline]
pub fn source(&self) -> Option<&Source> {
self.source.as_ref()
}
#[inline]
pub fn meta(&self) -> Option<&Value> {
self.meta.as_ref()
}
#[inline]
pub fn is_enabled(&self) -> bool {
self.enabled.load(atomic::Ordering::SeqCst)
}
#[inline]
pub fn set_enabled(&self, val: bool) {
self.enabled.store(val, atomic::Ordering::SeqCst);
}
#[inline]
pub fn logic(&self) -> Option<&Logic> {
self.logic.as_ref()
}
#[inline]
pub fn action(&self) -> Option<&ServiceOptions> {
self.action.as_ref()
}
#[inline]
fn serialize_short_state_into(&self, result: &mut BTreeMap<Value, Value>) {
if let Some(ref st) = self.state {
let state = st.lock().unwrap();
state.serialize_into(result);
}
}
#[inline]
pub fn serialize_db_state(&self) -> Option<BTreeMap<Value, Value>> {
if let Some(ref st) = self.state {
let mut result = BTreeMap::new();
let state = st.lock().unwrap();
state.serialize_db_into(&mut result);
Some(result)
} else {
None
}
}
#[inline]
fn serialize_meta_into(&self, result: &mut BTreeMap<Value, Value>) {
if let Some(ref meta) = self.meta {
result.insert("meta".into(), meta.clone());
}
}
#[inline]
fn serialize_enabled_into(&self, result: &mut BTreeMap<Value, Value>) {
result.insert("enabled".into(), Value::from(self.is_enabled()));
}
#[inline]
pub fn prepare_serialization_payload(&self) -> BTreeMap<Value, Value> {
let mut result: BTreeMap<Value, Value> = BTreeMap::new();
result.insert("oid".into(), Value::from(&self.oid));
result
}
#[inline]
pub fn serialize_state_props_into(&self, hostname: &str, result: &mut BTreeMap<Value, Value>) {
result.insert(
"connected".into(),
Value::from(
self.source
.as_ref()
.map_or(true, |s| s.data.online.load(atomic::Ordering::SeqCst)),
),
);
result.insert(
"node".into(),
Value::from(self.source.as_ref().map_or(hostname, |s| &s.node)),
);
}
#[inline]
pub fn serialize_state(&self, hostname: &str) -> BTreeMap<Value, Value> {
let mut result = self.prepare_serialization_payload();
self.serialize_short_state_into(&mut result);
self.serialize_state_props_into(hostname, &mut result);
result
}
#[inline]
pub fn serialize_state_from(
&self,
state: &ItemState,
hostname: &str,
) -> BTreeMap<Value, Value> {
let mut result = self.prepare_serialization_payload();
state.serialize_into(&mut result);
self.serialize_state_props_into(hostname, &mut result);
result
}
#[inline]
pub fn serialize_state_basic(&self) -> Option<BTreeMap<Value, Value>> {
if self.state.is_some() {
let mut result = self.prepare_serialization_payload();
self.serialize_short_state_into(&mut result);
Some(result)
} else {
None
}
}
#[inline]
pub fn serialize_state_basic_from(&self, state: &ItemState) -> BTreeMap<Value, Value> {
let mut result = self.prepare_serialization_payload();
state.serialize_into(&mut result);
result
}
#[inline]
pub fn serialize_state_full(&self, hostname: &str) -> BTreeMap<Value, Value> {
let mut result = self.serialize_state(hostname);
self.serialize_meta_into(&mut result);
self.serialize_enabled_into(&mut result);
result
}
pub fn serialize_config(&self) -> Option<BTreeMap<Value, Value>> {
if self.source.is_some() {
return None;
}
let mut result = self.prepare_serialization_payload();
self.serialize_meta_into(&mut result);
self.serialize_enabled_into(&mut result);
self.serialize_logic_into(&mut result);
self.serialize_action_into(&mut result);
Some(result)
}
#[inline]
fn serialize_logic_into(&self, result: &mut BTreeMap<Value, Value>) {
if let Some(ref logic) = self.logic {
result.insert("logic".into(), to_value(logic).unwrap());
}
}
#[inline]
fn serialize_action_into(&self, result: &mut BTreeMap<Value, Value>) {
if let Some(ref action) = self.action {
result.insert("action".into(), to_value(action).unwrap());
}
}
}
impl Hash for ItemData {
fn hash<H: Hasher>(&self, state: &mut H) {
self.oid.hash(state);
}
}
impl Eq for ItemData {}
impl PartialEq for ItemData {
fn eq(&self, other: &Self) -> bool {
self.oid == other.oid
}
}
#[derive(Debug)]
pub struct SourceData {
online: atomic::AtomicBool,
destroyed: atomic::AtomicBool,
svc: String,
}
impl SourceData {
fn new(svc: &str) -> Self {
Self {
online: atomic::AtomicBool::new(true),
destroyed: <_>::default(),
svc: svc.to_owned(),
}
}
}
pub type Item = Arc<ItemData>;
pub type Source = Arc<ItemSource>;
#[derive(Default)]
pub struct Inventory {
items: ItemMap,
items_by_source: HashMap<Option<String>, HashMap<Arc<OID>, Item>>,
sources: HashMap<String, Source>,
}
impl Inventory {
pub fn new() -> Self {
<_>::default()
}
pub fn stats(&self) -> InventoryStats {
let mut st = HashMap::new();
let mut item_cnt = 0;
for (src, items) in &self.items_by_source {
item_cnt += items.len();
st.insert(
src.as_ref()
.map_or_else(|| crate::LOCAL_NODE_ALIAS.to_owned(), ToOwned::to_owned),
items.len(),
);
}
InventoryStats {
sources: st,
items: item_cnt,
}
}
#[inline]
pub fn get_or_create_source(&self, source_id: &str, svc: &str) -> Source {
self.sources
.get(source_id)
.map_or_else(|| create_source(source_id, svc), Clone::clone)
}
#[inline]
pub fn get_items_by_source(&self, source_id: &str) -> Option<HashMap<Arc<OID>, Item>> {
self.items_by_source
.get(&Some(source_id.to_owned()))
.map(Clone::clone)
}
#[inline]
pub fn list_local_items(&self) -> Vec<Item> {
let mut result = Vec::new();
if let Some(items) = self.items_by_source.get(&None) {
for item in items.values() {
result.push(item.clone());
}
}
result
}
#[inline]
pub fn mark_source_online(&self, source_id: &str, online: bool) {
if let Some(source) = self.sources.get(source_id) {
source.mark_online(online);
}
}
pub fn append_item_from_value(
&mut self,
oid: Option<&OID>,
config_value: serde_json::Value, state: Option<ItemState>,
boot_id: u64,
replace: bool,
allow_remote: bool,
) -> EResult<Item> {
let mut data: ItemData = serde_json::from_value(config_value)?;
if replace
&& state.is_none()
&& data.state.is_none()
&& data.oid.kind() != EvaItemKind::Lmacro
{
if let Some(item) = self.get_item(&data.oid) {
if let Some(ref old_stc) = item.state {
data.state = Some(Mutex::new(old_stc.lock().unwrap().clone()));
}
}
}
if let Some(o) = oid {
if data.oid != *o {
return Err(Error::invalid_data("oid does not match"));
}
}
if data.source.is_some() && !allow_remote {
return Err(Error::invalid_data("unable to append remote item"));
}
macro_rules! check_none {
($field: expr, $n: expr) => {
if $field.is_some() {
return Err(Error::invalid_data(format!("unsupported field: {}", $n)));
}
};
}
let tp = data.oid.kind();
match tp {
EvaItemKind::Unit => {}
EvaItemKind::Sensor | EvaItemKind::Lvar => {
check_none!(data.action, "action");
}
EvaItemKind::Lmacro => {
check_none!(data.logic, "logic");
}
}
if let Some(st) = state {
data.state = Some(Mutex::new(st));
} else if data.state.is_none() && tp != EvaItemKind::Lmacro {
data.state = Some(Mutex::new(ItemState::new0(IEID::generate(boot_id), tp)));
}
self.append_item(Arc::new(data), replace)
}
pub fn create_item(
&mut self,
oid: OID,
ieid: Option<IEID>,
from: Option<(&str, &str)>, ) -> EResult<Item> {
let tp = oid.kind();
let source = from.as_ref().map(|i| {
if let Some(src) = self.sources.get(i.0) {
src.clone()
} else {
create_source(i.0, i.1)
}
});
let item = Arc::new(ItemData {
oid,
state: if tp == EvaItemKind::Lmacro {
None
} else {
Some(Mutex::new(ItemState::new0(
ieid.ok_or_else(|| Error::invalid_data("IEID not specified"))?,
tp,
)))
},
source,
enabled: atomic::AtomicBool::new(true),
meta: None,
logic: None,
action: None,
});
self.append_item(item, false)
}
pub fn append_remote_item(
&mut self,
remote_item: ReplicationInventoryItem,
source: Source,
) -> EResult<Item> {
let item_data: ItemData = ItemData::from_repl_item(remote_item, source);
self.append_item(Arc::new(item_data), true)
}
fn append_item(&mut self, item: Item, replace: bool) -> EResult<Item> {
let source = item.source.clone();
self.items.append(item.clone(), replace)?;
let mut node = source.as_ref().map(|i| i.node.clone());
if let Some(items) = self.items_by_source.get_mut(&node) {
items.insert(Arc::new(item.oid.clone()), item.clone());
} else {
let mut items = HashMap::new();
items.insert(Arc::new(item.oid.clone()), item.clone());
if let Some(s) = source {
self.items_by_source.insert(node.clone(), items);
self.sources.insert(node.take().unwrap(), s);
} else {
self.items_by_source.insert(node, items);
}
}
Ok(item)
}
#[inline]
pub fn get_items_by_mask(&self, mask: &OIDMask, filter: &Filter) -> Vec<Item> {
self.items.get_by_mask(mask, filter)
}
#[inline]
pub fn get_item(&self, oid: &OID) -> Option<Item> {
self.items.get(oid)
}
#[inline]
pub fn remove_item(&mut self, oid: &OID) -> Option<Item> {
if let Some(item) = self.items.remove(oid) {
let node = item.source.as_ref().map(|x| x.node.clone());
if let Some(items) = self.items_by_source.get_mut(&node) {
items.remove(&item.oid);
if items.is_empty() {
self.items_by_source.remove(&node);
node.map(|ref i| self.sources.remove(i));
}
}
Some(item)
} else {
None
}
}
}
#[derive(Default, Debug)]
struct ItemTree {
childs: HashMap<String, ItemTree>,
childs_any: Option<Box<ItemTree>>,
members: HashMap<OID, Item>,
members_wildcard: HashMap<OID, Item>,
}
impl ItemTree {
fn is_empty(&self) -> bool {
self.childs.is_empty() && self.members.is_empty()
}
}
#[derive(Debug, Default)]
pub struct ItemMap {
unit: ItemTree,
sensor: ItemTree,
lvar: ItemTree,
lmacro: ItemTree,
}
pub enum NodeFilter<'a> {
Local,
Remote(&'a str),
}
#[derive(Default)]
pub struct Filter<'a> {
include: Option<&'a OIDMaskList>,
exclude: Option<&'a OIDMaskList>,
node: Option<NodeFilter<'a>>,
}
impl<'a> Filter<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn include(mut self, mask_list: &'a OIDMaskList) -> Self {
self.include = Some(mask_list);
self
}
pub fn exclude(mut self, mask_list: &'a OIDMaskList) -> Self {
self.exclude = Some(mask_list);
self
}
pub fn node(mut self, sid: NodeFilter<'a>) -> Self {
self.node = Some(sid);
self
}
#[inline]
pub fn set_include(&mut self, mask_list: &'a OIDMaskList) {
self.include = Some(mask_list);
}
#[inline]
pub fn set_exclude(&mut self, mask_list: &'a OIDMaskList) {
self.exclude = Some(mask_list);
}
#[inline]
pub fn set_node(&mut self, sid: NodeFilter<'a>) {
self.node = Some(sid);
}
#[inline]
pub fn matches(&self, item: &Item) -> bool {
if let Some(ref node) = self.node {
match node {
NodeFilter::Local => {
if item.source.is_some() {
return false;
}
}
NodeFilter::Remote(id) => {
if let Some(ref source) = item.source {
if *id != "#" && *id != "*" && source.node != *id {
return false;
}
} else {
return false;
}
}
}
}
if let Some(f) = self.include {
if !f.matches(&item.oid) {
return false;
}
}
if let Some(f) = self.exclude {
!f.matches(&item.oid)
} else {
true
}
}
}
impl ItemMap {
#[inline]
fn get_tree(&self, tp: EvaItemKind) -> &ItemTree {
match tp {
EvaItemKind::Unit => &self.unit,
EvaItemKind::Sensor => &self.sensor,
EvaItemKind::Lvar => &self.lvar,
EvaItemKind::Lmacro => &self.lmacro,
}
}
#[inline]
fn get_tree_mut(&mut self, tp: EvaItemKind) -> &mut ItemTree {
match tp {
EvaItemKind::Unit => &mut self.unit,
EvaItemKind::Sensor => &mut self.sensor,
EvaItemKind::Lvar => &mut self.lvar,
EvaItemKind::Lmacro => &mut self.lmacro,
}
}
#[inline]
pub fn append(&mut self, item: Item, replace: bool) -> EResult<()> {
let tree = self.get_tree_mut(item.oid.kind());
append_item_rec(tree, item.oid.full_id().split('/'), &item, replace)
}
#[inline]
pub fn get(&self, oid: &OID) -> Option<Item> {
let tree = self.get_tree(oid.kind());
get_item_rec(tree, oid.full_id().split('/'))
}
#[inline]
pub fn remove(&mut self, oid: &OID) -> Option<Item> {
let tree = self.get_tree_mut(oid.kind());
remove_item_rec(tree, oid.full_id().split('/'), oid)
}
pub fn get_by_mask(&self, mask: &OIDMask, filter: &Filter) -> Vec<Item> {
if let Some(tp) = mask.kind() {
let tree = self.get_tree(tp);
if let Some(chunks) = mask.chunks() {
let mut result = Vec::new();
get_item_by_mask_rec(tree, chunks.iter(), &mut result, filter);
result
} else {
tree.members_wildcard
.values()
.filter(|x| filter.matches(x))
.cloned()
.collect()
}
} else {
let mut result = Vec::new();
if let Some(chunks) = mask.chunks() {
get_item_by_mask_rec(&self.unit, chunks.iter(), &mut result, filter);
get_item_by_mask_rec(&self.sensor, chunks.iter(), &mut result, filter);
get_item_by_mask_rec(&self.lvar, chunks.iter(), &mut result, filter);
get_item_by_mask_rec(&self.lmacro, chunks.iter(), &mut result, filter);
} else {
result.extend(
self.unit
.members_wildcard
.values()
.filter(|x| filter.matches(x))
.cloned()
.collect::<Vec<Item>>(),
);
result.extend(
self.sensor
.members_wildcard
.values()
.filter(|x| filter.matches(x))
.cloned()
.collect::<Vec<Item>>(),
);
result.extend(
self.lvar
.members_wildcard
.values()
.filter(|x| filter.matches(x))
.cloned()
.collect::<Vec<Item>>(),
);
result.extend(
self.lmacro
.members_wildcard
.values()
.filter(|x| filter.matches(x))
.cloned()
.collect::<Vec<Item>>(),
);
}
result
}
}
}
fn get_item_rec(tree: &ItemTree, mut sp: Split<char>) -> Option<Item> {
if let Some(chunk) = sp.next() {
if let Some(child) = tree.childs.get(chunk) {
get_item_rec(child, sp)
} else {
None
}
} else if tree.members.is_empty() {
None
} else {
Some(tree.members.values().next().unwrap().clone())
}
}
fn remove_item_rec(tree: &mut ItemTree, mut sp: Split<char>, oid: &OID) -> Option<Item> {
if let Some(chunk) = sp.next() {
tree.members_wildcard.remove(oid)?;
let item = if let Some(c) = tree.childs.get_mut(chunk) {
let item = remove_item_rec(c, sp.clone(), oid)?;
if c.is_empty() {
tree.childs.remove(chunk);
}
item
} else {
return None;
};
if let Some(ref mut c) = tree.childs_any {
remove_item_rec(c, sp, oid)?;
if c.is_empty() {
tree.childs_any = None;
}
}
Some(item)
} else {
tree.members.remove(oid)
}
}
fn get_item_by_mask_rec(
tree: &ItemTree,
mut iter: std::slice::Iter<&str>,
result: &mut Vec<Item>,
filter: &Filter,
) {
if let Some(chunk) = iter.next() {
if *chunk == "#" {
result.extend(
tree.members_wildcard
.values()
.filter(|x| filter.matches(x))
.cloned()
.collect::<Vec<Item>>(),
);
} else if *chunk == "+" {
if let Some(ref child) = tree.childs_any {
get_item_by_mask_rec(child, iter, result, filter);
}
} else if let Some(child) = tree.childs.get(*chunk) {
get_item_by_mask_rec(child, iter, result, filter);
}
} else {
result.extend(
tree.members
.values()
.filter(|x| filter.matches(x))
.cloned()
.collect::<Vec<Item>>(),
);
}
}
fn append_item_rec(
tree: &mut ItemTree,
mut sp: Split<char>,
item: &Item,
replace: bool,
) -> EResult<()> {
if let Some(chunk) = sp.next() {
if tree.members_wildcard.contains_key(&item.oid) && !replace {
return Err(Error::busy(format!(
"item {} is already registered",
item.oid
)));
}
tree.members_wildcard.insert(item.oid.clone(), item.clone());
if let Some(c) = tree.childs.get_mut(chunk) {
append_item_rec(c, sp.clone(), item, replace)?;
} else {
let mut child = ItemTree::default();
append_item_rec(&mut child, sp.clone(), item, replace)?;
tree.childs.insert(chunk.to_owned(), child);
}
if let Some(ref mut c) = tree.childs_any {
append_item_rec(c, sp, item, replace)
} else {
let mut child = ItemTree::default();
append_item_rec(&mut child, sp, item, replace)?;
tree.childs_any.replace(Box::new(child));
Ok(())
}
} else if tree.members.contains_key(&item.oid) && !replace {
Err(Error::busy(format!(
"item {} is already registered",
item.oid
)))
} else {
tree.members.insert(item.oid.clone(), item.clone());
Ok(())
}
}