summaryrefslogtreecommitdiffstats
path: root/below/model/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'below/model/src/lib.rs')
-rw-r--r--below/model/src/lib.rs236
1 files changed, 87 insertions, 149 deletions
diff --git a/below/model/src/lib.rs b/below/model/src/lib.rs
index 36174868..c69380f5 100644
--- a/below/model/src/lib.rs
+++ b/below/model/src/lib.rs
@@ -15,6 +15,7 @@
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::fmt;
+use std::marker::PhantomData;
use std::str::FromStr;
use std::time::Duration;
use std::time::Instant;
@@ -341,160 +342,96 @@ pub trait Nameable {
fn name() -> &'static str;
}
-/// Sequence that wraps a delegate sequence and owns no variants.
-trait DelegatedSequence {
- type Delegate: Sequence;
- fn get_delegate(&self) -> &Self::Delegate;
- // Not using From trait as conversion should only be used for Sequence
- fn from_delegate(delegate: Self::Delegate) -> Self;
+/// Type that contains sub-queriables of the same type, individually retrieveable
+/// by some index. It is itself a Queriable.
+pub trait QueriableContainer {
+ type Idx;
+ type SubqueryId: FieldId;
+ const IDX_PLACEHOLDER: &'static str = "<idx>.";
+ fn split(s: &str) -> Option<(&str, &str)> {
+ s.split_once('.')
+ }
+ fn get_item(&self, idx: &Self::Idx) -> Option<&<Self::SubqueryId as FieldId>::Queriable>;
}
-/// Implements Sequence for DelegatedSequence. Must be a macro due to orphan
-/// rule. See https://github.com/rust-lang/rfcs/issues/1124
-macro_rules! impl_sequence_for_delegated_sequence {
- () => {
- const CARDINALITY: usize = <Self as DelegatedSequence>::Delegate::CARDINALITY;
- fn next(&self) -> Option<Self> {
- self.get_delegate().next().map(Self::from_delegate)
- }
- fn previous(&self) -> Option<Self> {
- self.get_delegate().previous().map(Self::from_delegate)
- }
- fn first() -> Option<Self> {
- <Self as DelegatedSequence>::Delegate::first().map(Self::from_delegate)
- }
- fn last() -> Option<Self> {
- <Self as DelegatedSequence>::Delegate::last().map(Self::from_delegate)
- }
- };
+impl<C: QueriableContainer> Queriable for C {
+ type FieldId = QueriableContainerFieldId<C>;
+ fn query(&self, field_id: &<C as Queriable>::FieldId) -> Option<Field> {
+ self.get_item(field_id.idx.as_ref()?)
+ .and_then(|sub| sub.query(&field_id.subquery_id.0))
+ }
}
-pub(crate) use impl_sequence_for_delegated_sequence;
-/// Type that makes Vec Queriable if Vec's inner type is Queriable. Uses `idx`
-/// to query into a Vec. Uses `subquery_id` to query into the selected item.
#[derive(Clone, Debug, PartialEq)]
-pub struct VecFieldId<F: FieldId> {
+pub struct QueriableContainerFieldId<C: QueriableContainer> {
/// None is only for listing variants and otherwise invalid.
- pub idx: Option<usize>,
- pub subquery_id: F,
+ /// If None, shows up as C::IDX_PLACEHOLDER
+ pub idx: Option<C::Idx>,
+ // Wraps inside a tuple so we can #[derive] traits without adding type constraints
+ pub subquery_id: (C::SubqueryId,),
+ phantom: PhantomData<C>,
}
-impl<F: FieldId> FieldId for VecFieldId<F>
-where
- <F as FieldId>::Queriable: Sized,
-{
- type Queriable = Vec<F::Queriable>;
+impl<C: QueriableContainer> FieldId for QueriableContainerFieldId<C> {
+ type Queriable = C;
}
-impl<F: FieldId + Sequence> DelegatedSequence for VecFieldId<F> {
- type Delegate = F;
- fn get_delegate(&self) -> &Self::Delegate {
- &self.subquery_id
- }
- fn from_delegate(delegate: Self::Delegate) -> Self {
+impl<C: QueriableContainer> QueriableContainerFieldId<C> {
+ pub fn new(idx: Option<C::Idx>, subquery_id: C::SubqueryId) -> Self {
Self {
- idx: None,
- subquery_id: delegate,
+ idx,
+ subquery_id: (subquery_id,),
+ phantom: PhantomData,
}
}
}
-impl<F: FieldId + Sequence> Sequence for VecFieldId<F> {
- impl_sequence_for_delegated_sequence!();
-}
-
-impl<F: FieldId + ToString> ToString for VecFieldId<F> {
- fn to_string(&self) -> String {
- match self.idx {
- Some(idx) => format!("{}.{}", idx, self.subquery_id.to_string()),
- None => format!("<idx>.{}", self.subquery_id.to_string()),
- }
- }
-}
-
-impl<F: FieldId + FromStr> FromStr for VecFieldId<F>
+impl<C: QueriableContainer> Sequence for QueriableContainerFieldId<C>
where
- <F as FromStr>::Err: Into<anyhow::Error>,
+ C::SubqueryId: Sequence,
{
- type Err = anyhow::Error;
- fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
- if let Some(dot_idx) = s.find('.') {
- Ok(Self {
- idx: Some(s[..dot_idx].parse()?),
- subquery_id: F::from_str(&s[dot_idx + 1..]).map_err(Into::into)?,
- })
- } else {
- Err(anyhow!(
- "Unable to find a variant of the given enum matching string `{}`.",
- s,
- ))
- }
+ const CARDINALITY: usize = C::SubqueryId::CARDINALITY;
+ fn next(&self) -> Option<Self> {
+ self.subquery_id.0.next().map(|s| Self::new(None, s))
}
-}
-
-impl<Q: Queriable> Queriable for Vec<Q> {
- type FieldId = VecFieldId<Q::FieldId>;
- fn query(&self, field_id: &Self::FieldId) -> Option<Field> {
- self.get(field_id.idx?)
- .and_then(|f| f.query(&field_id.subquery_id))
+ fn previous(&self) -> Option<Self> {
+ self.subquery_id.0.previous().map(|s| Self::new(None, s))
}
-}
-
-/// Type that makes BTreeMap Queriable if its value is Queriable. Uses `key`
-/// to query into a map. Uses `subquery_id` to query into the selected value.
-#[derive(Clone, Debug, PartialEq)]
-pub struct BTreeMapFieldId<K, F: FieldId> {
- /// None is only for listing variants and otherwise invalid.
- pub key: Option<K>,
- pub subquery_id: F,
-}
-
-impl<K: Ord, F: FieldId> FieldId for BTreeMapFieldId<K, F>
-where
- <F as FieldId>::Queriable: Sized,
-{
- type Queriable = BTreeMap<K, F::Queriable>;
-}
-
-impl<K, F: FieldId + Sequence> DelegatedSequence for BTreeMapFieldId<K, F> {
- type Delegate = F;
- fn get_delegate(&self) -> &Self::Delegate {
- &self.subquery_id
+ fn first() -> Option<Self> {
+ C::SubqueryId::first().map(|s| Self::new(None, s))
}
- fn from_delegate(delegate: Self::Delegate) -> Self {
- Self {
- key: None,
- subquery_id: delegate,
- }
+ fn last() -> Option<Self> {
+ C::SubqueryId::last().map(|s| Self::new(None, s))
}
}
-impl<K, F: FieldId + Sequence> Sequence for BTreeMapFieldId<K, F> {
- impl_sequence_for_delegated_sequence!();
-}
-
-impl<K: ToString, F: FieldId + ToString> ToString for BTreeMapFieldId<K, F> {
+impl<C: QueriableContainer> ToString for QueriableContainerFieldId<C>
+where
+ C::Idx: ToString,
+ C::SubqueryId: ToString,
+{
fn to_string(&self) -> String {
- match &self.key {
- Some(key) => format!("{}.{}", key.to_string(), self.subquery_id.to_string()),
- None => format!("<key>.{}", self.subquery_id.to_string()),
+ match self.idx.as_ref() {
+ Some(idx) => format!("{}.{}", idx.to_string(), self.subquery_id.0.to_string()),
+ None => format!("{}{}", C::IDX_PLACEHOLDER, self.subquery_id.0.to_string()),
}
}
}
-impl<K: FromStr, F: FieldId + FromStr> FromStr for BTreeMapFieldId<K, F>
+impl<C: QueriableContainer> FromStr for QueriableContainerFieldId<C>
where
- <K as FromStr>::Err: Into<anyhow::Error>,
- <F as FromStr>::Err: Into<anyhow::Error>,
+ C::Idx: FromStr,
+ C::SubqueryId: FromStr,
+ <C::Idx as FromStr>::Err: Into<anyhow::Error>,
+ <C::SubqueryId as FromStr>::Err: Into<anyhow::Error>,
{
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
- // Only works with keys that don't contain dot
- if let Some(dot_idx) = s.find('.') {
- Ok(Self {
- key: Some(K::from_str(&s[..dot_idx]).map_err(Into::into)?),
- subquery_id: F::from_str(&s[dot_idx + 1..]).map_err(Into::into)?,
- })
+ if let Some((idx_str, subquery_id_str)) = C::split(s) {
+ Ok(Self::new(
+ Some(C::Idx::from_str(idx_str).map_err(Into::into)?),
+ C::SubqueryId::from_str(subquery_id_str).map_err(Into::into)?,
+ ))
} else {
Err(anyhow!(
"Unable to find a variant of the given enum matching string `{}`.",
@@ -504,14 +441,27 @@ where
}
}
-impl<K: Ord, Q: Queriable> Queriable for BTreeMap<K, Q> {
- type FieldId = BTreeMapFieldId<K, Q::FieldId>;
- fn query(&self, field_id: &Self::FieldId) -> Option<Field> {
- self.get(field_id.key.as_ref()?)
- .and_then(|f| f.query(&field_id.subquery_id))
+impl<Q: Queriable> QueriableContainer for Vec<Q> {
+ type Idx = usize;
+ type SubqueryId = Q::FieldId;
+ fn get_item(&self, idx: &usize) -> Option<&Q> {
+ self.get(*idx)
}
}
+pub type VecFieldId<Q> = QueriableContainerFieldId<Vec<Q>>;
+
+impl<K: Ord, Q: Queriable> QueriableContainer for BTreeMap<K, Q> {
+ type Idx = K;
+ type SubqueryId = Q::FieldId;
+ const IDX_PLACEHOLDER: &'static str = "<key>.";
+ fn get_item(&self, idx: &K) -> Option<&Q> {
+ self.get(idx)
+ }
+}
+
+pub type BTreeMapFieldId<K, Q> = QueriableContainerFieldId<BTreeMap<K, Q>>;
+
pub struct NetworkStats<'a> {
net: &'a procfs::NetStat,
ethtool: &'a Option<ethtool::EthtoolStats>,
@@ -645,14 +595,8 @@ mod tests {
#[test]
fn test_vec_field_id() {
let query_str = "1.msg";
- let query = <VecFieldId<TestModelFieldId>>::from_str(query_str).expect("bad query str");
- assert_eq!(
- query,
- VecFieldId {
- idx: Some(1),
- subquery_id: TestModelFieldId::Msg,
- }
- );
+ let query = <VecFieldId<TestModel>>::from_str(query_str).expect("bad query str");
+ assert_eq!(query, VecFieldId::new(Some(1), TestModelFieldId::Msg),);
assert_eq!(query.to_string(), query_str);
}
@@ -667,10 +611,7 @@ mod tests {
},
];
assert_eq!(
- data.query(&VecFieldId {
- idx: Some(1),
- subquery_id: TestModelFieldId::Msg,
- }),
+ data.query(&VecFieldId::new(Some(1), TestModelFieldId::Msg,)),
Some(Field::Str("world".to_owned()))
);
}
@@ -678,14 +619,11 @@ mod tests {
#[test]
fn test_btreemap_field_id() {
let query_str = "hello.msg";
- let query = <BTreeMapFieldId<String, TestModelFieldId>>::from_str(query_str)
- .expect("bad query str");
+ let query =
+ <BTreeMapFieldId<String, TestModel>>::from_str(query_str).expect("bad query str");
assert_eq!(
query,
- BTreeMapFieldId {
- key: Some("hello".to_owned()),
- subquery_id: TestModelFieldId::Msg,
- }
+ BTreeMapFieldId::new(Some("hello".to_owned()), TestModelFieldId::Msg)
);
assert_eq!(query.to_string(), query_str);
}
@@ -706,10 +644,10 @@ mod tests {
},
);
assert_eq!(
- data.query(&BTreeMapFieldId {
- key: Some("hello".to_owned()),
- subquery_id: TestModelFieldId::Msg,
- }),
+ data.query(&BTreeMapFieldId::new(
+ Some("hello".to_owned()),
+ TestModelFieldId::Msg,
+ )),
Some(Field::Str("world".to_owned()))
);
}