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