Skip to main content

ic_query/ic/api_boundary_node/
text.rs

1//! Module: ic::api_boundary_node::text
2//!
3//! Responsibility: render certified API boundary-node reports as human-facing text.
4//! Does not own: JSON serialization, state-tree collection, or report validation.
5//! Boundary: keeps certificate provenance separate from the following node table.
6
7use super::IcApiBoundaryNodeReport;
8use crate::{
9    table::{ColumnAlign, render_table},
10    text_value::sanitize_text,
11};
12
13/// Render one complete certified API boundary-node report as human-facing text.
14#[must_use]
15pub fn ic_api_boundary_node_report_text(report: &IcApiBoundaryNodeReport) -> String {
16    let provenance = &report.provenance;
17    let mut lines = vec![
18        format!("schema_version: {}", provenance.schema_version),
19        format!("network: {}", sanitize_text(&provenance.network)),
20        format!("authority: {}", sanitize_text(&provenance.authority)),
21        format!(
22            "source_endpoint: {}",
23            sanitize_text(&provenance.source_endpoint)
24        ),
25        format!(
26            "effective_canister_id: {}",
27            sanitize_text(&provenance.effective_canister_id)
28        ),
29        format!(
30            "fetched_at_unix_seconds: {}",
31            provenance.fetched_at_unix_seconds
32        ),
33        format!("fetched_at: {}", sanitize_text(&provenance.fetched_at)),
34        format!("fetched_by: {}", sanitize_text(&provenance.fetched_by)),
35        format!(
36            "certificate_time_unix_seconds: {}",
37            provenance.certificate_time_unix_seconds
38        ),
39        format!(
40            "certificate_time: {}",
41            sanitize_text(&provenance.certificate_time)
42        ),
43        format!("certified: {}", yes_no(provenance.certified)),
44        format!(
45            "point_in_time_guaranteed: {}",
46            yes_no(provenance.point_in_time_guaranteed)
47        ),
48        format!("node_count: {}", report.node_count),
49    ];
50
51    if !report.rows.is_empty() {
52        let rows = report
53            .rows
54            .iter()
55            .map(|row| {
56                [
57                    row.node_id.clone(),
58                    row.domain.clone(),
59                    row.ipv4_address.clone().unwrap_or_else(|| "-".to_string()),
60                    row.ipv6_address.clone(),
61                ]
62            })
63            .collect::<Vec<_>>();
64        lines.push(String::new());
65        lines.push("api_boundary_nodes:".to_string());
66        lines.push(render_table(
67            &["NODE ID", "DOMAIN", "IPV4", "IPV6"],
68            &rows,
69            &[
70                ColumnAlign::Left,
71                ColumnAlign::Left,
72                ColumnAlign::Left,
73                ColumnAlign::Left,
74            ],
75        ));
76    }
77
78    lines.join("\n")
79}
80
81const fn yes_no(value: bool) -> &'static str {
82    if value { "yes" } else { "no" }
83}