Skip to main content

purple_ssh/messages/
cli.rs

1// ── Add host validation ─────────────────────────────────────────
2
3pub const ALIAS_EMPTY: &str = "Alias can't be empty. Use --alias to specify one.";
4pub const ALIAS_WHITESPACE: &str =
5    "Alias can't contain whitespace. Use --alias to pick a simpler name.";
6pub const ALIAS_PATTERN_CHARS: &str =
7    "Alias can't contain pattern characters. Use --alias to pick a different name.";
8pub const HOSTNAME_WHITESPACE: &str = "Hostname can't contain whitespace.";
9pub const USER_WHITESPACE: &str = "User can't contain whitespace.";
10pub const PASSWORD_EMPTY: &str = "Password can't be empty.";
11pub const CANCELLED: &str = "Cancelled.";
12pub const DESCRIPTION_CONTROL_CHARS: &str = "Description contains control characters.";
13
14pub use super::contains_control_chars as control_chars;
15
16pub fn welcome(alias: &str) -> String {
17    format!("Welcome aboard, {}!", alias)
18}
19
20// ── Import ──────────────────────────────────────────────────────
21
22pub const IMPORT_NO_FILE: &str =
23    "Provide a file or use --known-hosts. Run 'purple import --help' for details.";
24
25// ── Provider CLI ────────────────────────────────────────────────
26
27pub const NO_PROVIDERS: &str = "No providers configured. Run 'purple provider add' to set one up.";
28
29pub fn no_config_for(provider: &str) -> String {
30    format!(
31        "No configuration for {}. Run 'purple provider add {}' first.",
32        provider, provider
33    )
34}
35
36pub fn saved_config(provider: &str) -> String {
37    format!("Saved {} configuration.", provider)
38}
39
40pub fn no_config_to_remove(provider: &str) -> String {
41    format!("No configuration for '{}'. Nothing to remove.", provider)
42}
43
44pub fn removed_config(provider: &str) -> String {
45    format!("Removed {} configuration.", provider)
46}
47
48// ── Tunnel CLI ──────────────────────────────────────────────────
49
50pub fn no_tunnels_for(alias: &str) -> String {
51    format!("No tunnels configured for {}.", alias)
52}
53
54pub fn tunnels_for(alias: &str) -> String {
55    format!("Tunnels for {}:", alias)
56}
57
58pub const NO_TUNNELS: &str = "No tunnels configured.";
59
60pub fn starting_tunnel(alias: &str) -> String {
61    format!("Starting tunnel for {}... (Ctrl+C to stop)", alias)
62}
63
64pub fn host_not_found(alias: &str) -> String {
65    format!("No host '{}' found.", alias)
66}
67
68pub fn added_forward(forward: &str, alias: &str) -> String {
69    format!("Added {} to {}.", forward, alias)
70}
71
72pub fn forward_exists(forward: &str, alias: &str) -> String {
73    format!("Forward {} already exists on {}.", forward, alias)
74}
75
76pub fn forward_not_found(forward: &str, alias: &str) -> String {
77    format!("No matching forward {} found on {}.", forward, alias)
78}
79
80pub fn removed_forward(forward: &str, alias: &str) -> String {
81    format!("Removed {} from {}.", forward, alias)
82}
83
84pub fn no_forwards(alias: &str) -> String {
85    format!("No forwarding directives configured for '{}'.", alias)
86}
87
88pub fn save_config_failed(e: &impl std::fmt::Display) -> String {
89    format!("Failed to save config: {}", e)
90}
91
92pub fn included_host_read_only(alias: &str) -> String {
93    format!(
94        "Host '{}' is from an included file and cannot be modified.",
95        alias
96    )
97}
98
99pub fn operation_failed(e: &impl std::fmt::Display) -> String {
100    format!("Failed: {}", e)
101}
102
103// ── Snippet CLI ─────────────────────────────────────────────────
104
105pub const NO_SNIPPETS: &str = "No snippets configured. Use 'purple snippet add' to create one.";
106
107pub use super::snippet_added;
108pub use super::snippet_removed;
109pub use super::snippet_updated;
110
111pub fn snippet_not_found(name: &str) -> String {
112    format!("No snippet '{}' found.", name)
113}
114
115pub fn no_hosts_with_tag(tag: &str) -> String {
116    format!("No hosts found with tag '{}'.", tag)
117}
118
119pub const SPECIFY_TARGET: &str = "Specify a host alias, --tag or --all.";
120
121// ── Run/exec output ─────────────────────────────────────────────
122
123pub fn beaming_up(alias: &str) -> String {
124    format!("Beaming you up to {}...\n", alias)
125}
126
127pub fn running_snippet_on(name: &str, alias: &str) -> String {
128    format!("Running '{}' on {}...\n", name, alias)
129}
130
131pub fn host_separator(alias: &str) -> String {
132    format!("── {} ──", alias)
133}
134
135pub fn exited_with_code(code: i32) -> String {
136    format!("Exited with code {}.", code)
137}
138
139pub const DONE: &str = "Done.";
140
141pub fn done_multi(name: &str, count: usize) -> String {
142    format!("Done. Ran '{}' on {} hosts.", name, count)
143}
144
145pub const PRESS_ENTER: &str = "Press Enter to continue...";
146
147pub fn host_failed(alias: &str, e: &impl std::fmt::Display) -> String {
148    format!("[{}] Failed: {}", alias, e)
149}
150
151pub fn skipping_host(alias: &str, e: &impl std::fmt::Display) -> String {
152    format!("Skipping {}: {}", alias, e)
153}
154
155// ── Password CLI ────────────────────────────────────────────────
156
157pub fn password_removed(alias: &str) -> String {
158    format!("Password removed for {}.", alias)
159}
160
161// ── Log CLI ─────────────────────────────────────────────────────
162
163pub fn log_deleted(path: &impl std::fmt::Display) -> String {
164    format!("Log file deleted: {}", path)
165}
166
167pub fn no_log_file(path: &impl std::fmt::Display) -> String {
168    format!("No log file found at {}", path)
169}
170
171// ── Theme CLI ───────────────────────────────────────────────────
172
173pub const BUILTIN_THEMES: &str = "Built-in themes:";
174pub const CUSTOM_THEMES: &str = "\nCustom themes:";
175
176pub fn theme_set(name: &str) -> String {
177    format!("Theme set to: {}", name)
178}
179
180// ── Sync output ─────────────────────────────────────────────────
181
182pub fn syncing(name: &str, summary: &str) -> String {
183    format!("\x1b[2K\rSyncing {}... {}", name, summary)
184}
185
186pub fn servers_found_with_failures(count: usize, failures: usize, total: usize) -> String {
187    format!(
188        "{} servers found ({} of {} failed to fetch).",
189        count, failures, total
190    )
191}
192
193pub fn servers_found(count: usize) -> String {
194    format!("{} servers found.", count)
195}
196
197pub fn sync_result(prefix: &str, added: usize, updated: usize, unchanged: usize) -> String {
198    format!(
199        "{}Added {}, updated {}, unchanged {}.",
200        prefix, added, updated, unchanged
201    )
202}
203
204pub fn sync_removed(count: usize) -> String {
205    format!("  Removed {}.", count)
206}
207
208pub fn sync_stale(count: usize) -> String {
209    format!("  Marked {} stale.", count)
210}
211
212pub fn sync_skip_remove(display_name: &str) -> String {
213    format!(
214        "! {}: skipping --remove due to partial failures.",
215        display_name
216    )
217}
218
219pub fn sync_error(display_name: &str, e: &impl std::fmt::Display) -> String {
220    format!("! {}: {}", display_name, e)
221}
222
223pub const SYNC_SKIP_WRITE: &str =
224    "! Skipping config write due to sync failures. Fix the errors and re-run.";
225
226// ── Provider validation (CLI) ───────────────────────────────────
227
228pub const PROXMOX_URL_REQUIRED: &str =
229    "Proxmox requires --url (e.g. --url https://pve.example.com:8006).";
230pub const AWS_REGIONS_REQUIRED: &str =
231    "AWS requires --regions (e.g. --regions us-east-1,eu-west-1).";
232pub const AZURE_REGIONS_REQUIRED: &str =
233    "Azure requires --regions with one or more subscription IDs.";
234pub const GCP_PROJECT_REQUIRED: &str = "GCP requires --project (e.g. --project my-gcp-project-id).";
235pub use super::ALIAS_PREFIX_INVALID;
236
237pub const WARN_URL_NOT_USED: &str =
238    "Warning: --url is only used by the Proxmox provider. Ignoring.";
239pub const WARN_PROFILE_NOT_USED: &str =
240    "Warning: --profile is only used by the AWS provider. Ignoring.";
241pub const WARN_PROJECT_NOT_USED: &str =
242    "Warning: --project is only used by the GCP provider. Ignoring.";
243pub const WARN_COMPARTMENT_NOT_USED: &str =
244    "Warning: --compartment is only used by the Oracle provider. Ignoring.";
245
246// ── Vault CLI ───────────────────────────────────────────────────
247
248pub fn vault_no_role(alias: &str) -> String {
249    format!(
250        "No Vault SSH role configured for '{}'. Set it in the host form \
251         (Vault SSH Role field) or in the provider config (vault_role).",
252        alias
253    )
254}
255
256pub fn vault_cert_signed(path: &impl std::fmt::Display) -> String {
257    format!("Certificate signed: {}", path)
258}
259
260pub fn vault_sign_failed(e: &impl std::fmt::Display) -> String {
261    format!("failed: {}", e)
262}
263
264pub fn vault_config_update_warning(e: &impl std::fmt::Display) -> String {
265    format!("Warning: Failed to update SSH config: {}", e)
266}
267
268// ── List hosts ──────────────────────────────────────────────────
269
270pub const NO_HOSTS: &str = "No hosts configured. Run 'purple' to add some!";
271
272// ── Token ───────────────────────────────────────────────────────
273
274pub const NO_TOKEN: &str =
275    "No token provided. Use --token, --token-stdin, or set PURPLE_TOKEN env var.";
276
277// ── What's new (CLI) ────────────────────────────────────────────
278
279pub mod whats_new {
280    pub const HEADER: &str = "purple release notes";
281}