use serde::Serialize;
use super::admin::{CoverageRow, EntityKind};
#[derive(Debug, Clone, Serialize)]
pub struct ConsumersReport {
pub target: String,
pub asked: Vec<String>,
pub self_zid: String,
#[serde(flatten)]
pub admin: AdminAnswer,
pub rows: Vec<ConsumerRow>,
pub reply_elided: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "admin", rename_all = "snake_case")]
pub enum AdminAnswer {
Answered { answered: usize, nodes: usize },
NotAvailable,
}
#[derive(Debug, Clone, Serialize)]
pub struct ConsumerRow {
pub zid: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub whatami: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub origins: Vec<String>,
pub attribution: Attribution,
pub kind: EntityKind,
pub keyexpr: String,
pub relation: Relation,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub is_self: bool,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub total_wildcard: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Attribution {
Session,
ReportedOnly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Relation {
Exact,
Narrower,
Wider,
Intersects,
Total,
}
impl Relation {
pub fn as_str(self) -> &'static str {
match self {
Relation::Exact => "exact",
Relation::Narrower => "narrower",
Relation::Wider => "wider",
Relation::Intersects => "intersects",
Relation::Total => "total",
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct SubjectImpact {
pub producer: String,
pub path: String,
pub class: String,
pub selector: String,
pub consumers: ConsumersReport,
#[serde(skip_serializing_if = "Option::is_none")]
pub coverage: Option<Vec<CoverageRow>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub declared_publishers: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub declared_queryables: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deprecated: Option<DeprecationFact>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct DeprecationFact {
#[serde(skip_serializing_if = "Option::is_none")]
pub since: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub replaced_by: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn row() -> ConsumerRow {
ConsumerRow {
zid: "eeff0011".into(),
whatami: Some("peer".into()),
origins: vec!["h-3fa9c2d41b7e".into()],
attribution: Attribution::Session,
kind: EntityKind::Subscriber,
keyexpr: "v1/h-3fa9c2d41b7e/state/sysinfo/health".into(),
relation: Relation::Narrower,
is_self: false,
total_wildcard: false,
}
}
#[test]
fn the_admin_answer_is_a_discriminator_not_a_zero() {
let mut r = ConsumersReport {
target: "v1/*/state/sysinfo/health".into(),
asked: vec!["@/*/*".into()],
self_zid: "ffffffff".into(),
admin: AdminAnswer::Answered {
answered: 1,
nodes: 2,
},
rows: vec![],
reply_elided: 0,
};
let v = serde_json::to_value(&r).unwrap();
assert_eq!(v["admin"], "answered");
assert_eq!(v["answered"], 1);
assert_eq!(v["nodes"], 2);
r.admin = AdminAnswer::NotAvailable;
let v = serde_json::to_value(&r).unwrap();
assert_eq!(v["admin"], "not_available");
assert!(v.get("answered").is_none(), "{v}");
assert!(v.get("nodes").is_none(), "{v}");
}
#[test]
fn a_row_omits_what_is_not_news() {
let v = serde_json::to_value(row()).unwrap();
assert_eq!(
v,
json!({
"zid": "eeff0011",
"whatami": "peer",
"origins": ["h-3fa9c2d41b7e"],
"attribution": "session",
"kind": "subscriber",
"keyexpr": "v1/h-3fa9c2d41b7e/state/sysinfo/health",
"relation": "narrower",
})
);
let bare = ConsumerRow {
whatami: None,
origins: vec![],
attribution: Attribution::ReportedOnly,
is_self: true,
total_wildcard: true,
relation: Relation::Total,
keyexpr: "**".into(),
..row()
};
let v = serde_json::to_value(bare).unwrap();
assert!(v.get("origins").is_none(), "{v}");
assert!(v.get("whatami").is_none(), "{v}");
assert_eq!(v["is_self"], true);
assert_eq!(v["total_wildcard"], true);
assert_eq!(v["attribution"], "reported_only");
assert_eq!(v["relation"], "total");
}
#[test]
fn relations_rank_specific_first() {
assert!(Relation::Exact < Relation::Narrower);
assert!(Relation::Narrower < Relation::Wider);
assert!(Relation::Wider < Relation::Intersects);
assert!(Relation::Intersects < Relation::Total);
}
}