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