Skip to main content

ic_query/cloud_engine/provider/
text.rs

1//! Module: cloud_engine::provider::text
2//!
3//! Responsibility: render CloudEngine provider reports as compact human-facing text.
4//! Does not own: JSON serialization, source validation, live calls, or process output.
5//! Boundary: separates Dashboard provenance from provider and location tables.
6
7use super::{
8    CloudEngineProviderInfoReport, CloudEngineProviderListReport, CloudEngineProviderLocation,
9};
10use crate::{
11    ic::dashboard_provenance_lines,
12    table::{ColumnAlign, render_table},
13    text_value::{optional_text, sanitize_text, yes_no},
14};
15
16/// Render the complete CloudEngine-bearing provider list.
17#[must_use]
18pub fn cloud_engine_provider_list_report_text(report: &CloudEngineProviderListReport) -> String {
19    let mut lines = dashboard_provenance_lines(&report.provenance);
20    lines.extend([
21        format!(
22            "source_node_provider_count: {}",
23            report.source_node_provider_count
24        ),
25        format!(
26            "cloud_engine_provider_count: {}",
27            report.cloud_engine_provider_count
28        ),
29    ]);
30    if !report.providers.is_empty() {
31        let rows = report
32            .providers
33            .iter()
34            .map(|provider| {
35                [
36                    provider.principal_id.clone(),
37                    sanitize_text(&provider.display_name),
38                    provider.total_cloud_engines.to_string(),
39                    provider.total_cloud_engine_nodes.to_string(),
40                    provider.total_cloud_engine_unassigned_nodes.to_string(),
41                    provider.cloud_engine_location_count.to_string(),
42                ]
43            })
44            .collect::<Vec<_>>();
45        lines.push(String::new());
46        lines.push("CloudEngine providers".to_string());
47        lines.push(render_table(
48            &[
49                "Provider",
50                "Name",
51                "Engines",
52                "CE nodes",
53                "CE unassigned",
54                "CE locations",
55            ],
56            &rows,
57            &[
58                ColumnAlign::Left,
59                ColumnAlign::Left,
60                ColumnAlign::Right,
61                ColumnAlign::Right,
62                ColumnAlign::Right,
63                ColumnAlign::Right,
64            ],
65        ));
66    }
67    lines.join("\n")
68}
69
70/// Render one exact Dashboard provider record and its CloudEngine location evidence.
71#[must_use]
72pub fn cloud_engine_provider_info_report_text(report: &CloudEngineProviderInfoReport) -> String {
73    let provider = &report.provider;
74    let mut lines = dashboard_provenance_lines(&report.provenance);
75    lines.extend([
76        format!(
77            "cloud_engine_evidence_present: {}",
78            yes_no(report.cloud_engine_evidence_present)
79        ),
80        format!("node_provider_id: {}", provider.principal_id),
81        format!("display_name: {}", sanitize_text(&provider.display_name)),
82        format!("website: {}", optional_text(provider.website.as_ref())),
83        format!("logo_url: {}", optional_text(provider.logo_url.as_ref())),
84        format!("total_cloud_engines: {}", provider.total_cloud_engines),
85        format!(
86            "total_cloud_engine_nodes: {}",
87            provider.total_cloud_engine_nodes
88        ),
89        format!(
90            "total_cloud_engine_unassigned_nodes: {}",
91            provider.total_cloud_engine_unassigned_nodes
92        ),
93        format!(
94            "cloud_engine_location_count: {}",
95            provider.cloud_engine_location_count
96        ),
97        format!("total_nodes: {}", provider.total_nodes),
98        format!(
99            "total_unassigned_nodes: {}",
100            provider.total_unassigned_nodes
101        ),
102        format!(
103            "total_rewardable_nodes: {}",
104            provider.total_rewardable_nodes
105        ),
106        format!("total_node_allowance: {}", provider.total_node_allowance),
107        format!("total_subnets: {}", provider.total_subnets),
108        format!("location_count: {}", provider.location_count),
109    ]);
110    if !provider.cloud_engine_locations.is_empty() {
111        lines.push(String::new());
112        lines.push("CloudEngine locations".to_string());
113        lines.push(location_table(&provider.cloud_engine_locations));
114    }
115    lines.join("\n")
116}
117
118fn location_table(locations: &[CloudEngineProviderLocation]) -> String {
119    let rows = locations
120        .iter()
121        .map(|location| {
122            [
123                location.dc_key.clone(),
124                sanitize_text(&location.display_name),
125                sanitize_text(location.owner.trim()),
126                sanitize_text(&location.region),
127                location.latitude.to_string(),
128                location.longitude.to_string(),
129            ]
130        })
131        .collect::<Vec<_>>();
132    render_table(
133        &["DC", "Location", "Owner", "Region", "Latitude", "Longitude"],
134        &rows,
135        &[
136            ColumnAlign::Left,
137            ColumnAlign::Left,
138            ColumnAlign::Left,
139            ColumnAlign::Left,
140            ColumnAlign::Right,
141            ColumnAlign::Right,
142        ],
143    )
144}