1#![forbid(unsafe_code)]
4use super::emit::{is_quiet, write_line_human};
7use crate::masking::mask;
8use crate::ssh::ExecutionOutput;
9use crate::vps::model::VpsRecord;
10use secrecy::ExposeSecret;
11use std::io::{self, Write};
12
13#[allow(clippy::too_many_arguments)]
19pub fn print_doctor_text(
20 layer: &str,
21 config_path: &str,
22 exists: bool,
23 perms: &str,
24 schema_version: u32,
25 hosts: usize,
26 known_hosts: &str,
27 active_file: &str,
28 secrets_at_rest: &str,
29 secrets_key_source: &str,
30 secrets_key_file: &str,
31 plaintext_opt_out: bool,
32) {
33 if is_quiet() {
34 return;
35 }
36 let stdout = io::stdout();
37 let mut out = io::BufWriter::new(stdout.lock());
38 let opt_out = if plaintext_opt_out { "yes" } else { "no" };
39 let _ = (|| -> io::Result<()> {
40 writeln!(out, "Winning layer: {layer}")?;
41 writeln!(out, "Config path: {config_path}")?;
42 writeln!(out, "Exists: {exists}")?;
43 writeln!(out, "Permissions: {perms}")?;
44 writeln!(out, "Schema: {schema_version}")?;
45 writeln!(out, "Hosts: {hosts}")?;
46 writeln!(out, "known_hosts: {known_hosts}")?;
47 writeln!(out, "active file: {active_file}")?;
48 writeln!(
49 out,
50 "Secrets at-rest: {secrets_at_rest} (key source: {secrets_key_source})"
51 )?;
52 writeln!(out, "Secrets key file: {secrets_key_file}")?;
53 writeln!(out, "Plaintext opt-out: {opt_out}")?;
54 writeln!(out, "Telemetry: disabled")?;
55 out.flush()
56 })();
57}
58
59pub fn print_list_text(records: &[VpsRecord]) {
63 if is_quiet() {
64 return;
65 }
66 if records.is_empty() {
67 write_line_human(&crate::i18n::t(crate::i18n::Message::VpsRegistryEmpty));
68 return;
69 }
70
71 let stdout = io::stdout();
72 let mut out = io::BufWriter::new(stdout.lock());
73 let _ = (|| -> io::Result<()> {
74 writeln!(
75 out,
76 "{:<20} {:<30} {:<6} {:<15} {:<20}",
77 "NAME", "HOST", "PORT", "USER", "PASSWORD"
78 )?;
79 for r in records {
80 writeln!(
81 out,
82 "{:<20} {:<30} {:<6} {:<15} {:<20}",
83 r.name,
84 r.host,
85 r.port,
86 r.username,
87 mask(r.password.expose_secret())
88 )?;
89 }
90 out.flush()
91 })();
92}
93
94pub fn print_details_text(r: &VpsRecord) {
98 if is_quiet() {
99 return;
100 }
101 let password = if r.password.expose_secret().is_empty() {
104 "(not set)"
105 } else {
106 mask(r.password.expose_secret())
107 };
108 let key_path_owned = r.key_path.as_ref().map(|k| k.to_string_lossy_owned());
109 let key_path = key_path_owned.as_deref().unwrap_or("(not set)");
110 let sudo = r
111 .sudo_password
112 .as_ref()
113 .map_or("(not set)", |s| mask(s.expose_secret()));
114 let su = r
115 .su_password
116 .as_ref()
117 .map_or("(not set)", |s| mask(s.expose_secret()));
118
119 let stdout = io::stdout();
120 let mut out = io::BufWriter::new(stdout.lock());
121 let _ = (|| -> io::Result<()> {
122 writeln!(out, "Name: {}", r.name)?;
123 writeln!(out, "Host: {}", r.host)?;
124 writeln!(out, "Port: {}", r.port)?;
125 writeln!(out, "User: {}", r.username)?;
126 writeln!(out, "Password: {password}")?;
127 writeln!(out, "Key path: {key_path}")?;
128 writeln!(out, "Sudo password: {sudo}")?;
129 writeln!(out, "Su password: {su}")?;
130 writeln!(out, "Timeout (ms): {}", r.timeout_ms)?;
131 writeln!(out, "Max cmd chars: {}", r.max_command_chars.wire())?;
132 writeln!(out, "Max out chars: {}", r.max_output_chars.wire())?;
133 writeln!(out, "Disable sudo: {}", r.disable_sudo)?;
134 writeln!(out, "Schema version: {}", r.schema_version)?;
135 writeln!(out, "Added at: {}", r.added_at)?;
136 out.flush()
137 })();
138}
139
140pub fn print_execution_output(output: &ExecutionOutput) {
144 let stdout = io::stdout();
145 let mut out = io::BufWriter::new(stdout.lock());
146 let _ = (|| -> io::Result<()> {
147 writeln!(out, "--- stdout ---")?;
148 if output.stdout.is_empty() {
149 writeln!(out, "(empty)")?;
150 } else {
151 writeln!(out, "{}", output.stdout)?;
152 }
153 writeln!(out, "--- stderr ---")?;
154 if output.stderr.is_empty() {
155 writeln!(out, "(empty)")?;
156 } else {
157 writeln!(out, "{}", output.stderr)?;
158 }
159 match output.exit_code {
160 Some(code) => writeln!(
161 out,
162 "--- exit code: {} ({}ms) ---",
163 code, output.duration_ms
164 )?,
165 None => writeln!(out, "--- exit code: N/A ({}ms) ---", output.duration_ms)?,
166 }
167 if output.truncated_stdout {
169 writeln!(out, "(stdout was truncated)")?;
170 }
171 if output.truncated_stderr {
172 writeln!(out, "(stderr was truncated)")?;
173 }
174 out.flush()
175 })();
176}
177
178pub fn print_health_check(name: &str, latency_ms: u64) {
182 if is_quiet() {
183 return;
184 }
185 let msg = crate::i18n::t(crate::i18n::Message::HealthCheckOk {
186 name: name.to_string(),
187 });
188 let stdout = io::stdout();
189 let mut out = io::BufWriter::new(stdout.lock());
190 let _ = (|| -> io::Result<()> {
191 writeln!(out, "{msg}")?;
192 writeln!(out, " latency: {latency_ms}ms")?;
193 out.flush()
194 })();
195}