console_subscriber/
attribute.rs

1use console_api as proto;
2use std::collections::HashMap;
3use tracing::span::Id;
4
5#[derive(Debug, Default)]
6pub(crate) struct Attributes {
7    attributes: HashMap<FieldKey, proto::Attribute>,
8}
9
10#[derive(Debug, Clone)]
11pub(crate) struct Update {
12    pub(crate) field: proto::Field,
13    pub(crate) op: Option<UpdateOp>,
14    pub(crate) unit: Option<String>,
15}
16
17#[derive(Debug, Clone)]
18pub(crate) enum UpdateOp {
19    Add,
20    Override,
21    Sub,
22}
23
24/// Represents a key for a `proto::field::Name`. Because the
25/// proto::field::Name might not be unique we also include the
26/// resource id in this key
27#[derive(Debug, Hash, PartialEq, Eq)]
28struct FieldKey {
29    update_id: Id,
30    field_name: proto::field::Name,
31}
32
33// === impl Attributes ===
34
35impl Attributes {
36    pub(crate) fn values(&self) -> impl Iterator<Item = &proto::Attribute> {
37        self.attributes.values()
38    }
39
40    pub(crate) fn update(&mut self, id: &Id, update: &Update) {
41        let field_name = match update.field.name.as_ref() {
42            Some(name) => name.clone(),
43            None => {
44                tracing::warn!(?update.field, "field missing name, skipping...");
45                return;
46            }
47        };
48        let update_id = id.clone();
49        let key = FieldKey {
50            update_id,
51            field_name,
52        };
53
54        self.attributes
55            .entry(key)
56            .and_modify(|attr| update_attribute(attr, update))
57            .or_insert_with(|| update.clone().into());
58    }
59}
60
61fn update_attribute(attribute: &mut proto::Attribute, update: &Update) {
62    use proto::field::Value::*;
63    let attribute_val = attribute.field.as_mut().and_then(|a| a.value.as_mut());
64    let update_val = update.field.value.clone();
65    let update_name = update.field.name.clone();
66    match (attribute_val, update_val) {
67        (Some(BoolVal(v)), Some(BoolVal(upd))) => *v = upd,
68
69        (Some(StrVal(v)), Some(StrVal(upd))) => *v = upd,
70
71        (Some(DebugVal(v)), Some(DebugVal(upd))) => *v = upd,
72
73        (Some(U64Val(v)), Some(U64Val(upd))) => match update.op {
74            Some(UpdateOp::Add) => *v = v.saturating_add(upd),
75
76            Some(UpdateOp::Sub) => *v = v.saturating_sub(upd),
77
78            Some(UpdateOp::Override) => *v = upd,
79
80            None => tracing::warn!(
81                "numeric attribute update {:?} needs to have an op field",
82                update_name
83            ),
84        },
85
86        (Some(I64Val(v)), Some(I64Val(upd))) => match update.op {
87            Some(UpdateOp::Add) => *v = v.saturating_add(upd),
88
89            Some(UpdateOp::Sub) => *v = v.saturating_sub(upd),
90
91            Some(UpdateOp::Override) => *v = upd,
92
93            None => tracing::warn!(
94                "numeric attribute update {:?} needs to have an op field",
95                update_name
96            ),
97        },
98
99        (val, update) => {
100            tracing::warn!(
101                "attribute {:?} cannot be updated by update {:?}",
102                val,
103                update
104            );
105        }
106    }
107}
108
109impl From<Update> for proto::Attribute {
110    fn from(upd: Update) -> Self {
111        proto::Attribute {
112            field: Some(upd.field),
113            unit: upd.unit,
114        }
115    }
116}