1pub 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
20pub const IMPORT_NO_FILE: &str =
23 "Provide a file or use --known-hosts. Run 'purple import --help' for details.";
24
25pub const NO_PROVIDERS: &str = "No providers configured. Run 'purple provider add' to set one up.";
28
29pub const PROVIDER_LIST: &str = "digitalocean, vultr, linode, hetzner, upcloud, proxmox, aws, \
33 scaleway, gcp, azure, tailscale, oracle, ovh, leaseweb, i3d, transip";
34
35pub fn unknown_provider(name: &str) -> String {
39 format!("Never heard of '{}'. Try: {}.", name, PROVIDER_LIST)
40}
41
42pub fn skipping_unknown_provider(name: &str) -> String {
46 format!(
47 "Skipping unknown provider '{}'. Try: {}.",
48 name, PROVIDER_LIST
49 )
50}
51
52pub fn alias_already_exists(alias: &str) -> String {
55 format!(
56 "'{}' already exists. Use --alias to pick a different name.",
57 alias
58 )
59}
60
61pub fn import_parse_failures(count: usize) -> String {
64 let s = if count == 1 { "" } else { "s" };
65 format!(
66 "! {} line{} could not be parsed (invalid format).",
67 count, s
68 )
69}
70
71pub fn import_read_errors(count: usize) -> String {
72 let s = if count == 1 { "" } else { "s" };
73 format!("! {} line{} could not be read (encoding error).", count, s)
74}
75
76pub fn no_config_for(provider: &str) -> String {
77 format!(
78 "No configuration for {}. Run 'purple provider add {}' first.",
79 provider, provider
80 )
81}
82
83pub fn saved_config(provider: &str) -> String {
84 format!("Saved {} configuration.", provider)
85}
86
87pub fn no_config_to_remove(provider: &str) -> String {
88 format!("No configuration for '{}'. Nothing to remove.", provider)
89}
90
91pub fn removed_config(provider: &str) -> String {
92 format!("Removed {} configuration.", provider)
93}
94
95pub fn no_tunnels_for(alias: &str) -> String {
98 format!("No tunnels configured for {}.", alias)
99}
100
101pub fn tunnels_for(alias: &str) -> String {
102 format!("Tunnels for {}:", alias)
103}
104
105pub const NO_TUNNELS: &str = "No tunnels configured.";
106
107pub fn starting_tunnel(alias: &str) -> String {
108 format!("Starting tunnel for {}... (Ctrl+C to stop)", alias)
109}
110
111pub fn host_not_found(alias: &str) -> String {
112 format!("No host '{}' found.", alias)
113}
114
115pub fn added_forward(forward: &str, alias: &str) -> String {
116 format!("Added {} to {}.", forward, alias)
117}
118
119pub fn forward_exists(forward: &str, alias: &str) -> String {
120 format!("Forward {} already exists on {}.", forward, alias)
121}
122
123pub fn forward_not_found(forward: &str, alias: &str) -> String {
124 format!("No matching forward {} found on {}.", forward, alias)
125}
126
127pub fn removed_forward(forward: &str, alias: &str) -> String {
128 format!("Removed {} from {}.", forward, alias)
129}
130
131pub fn no_forwards(alias: &str) -> String {
132 format!("No forwarding directives configured for '{}'.", alias)
133}
134
135pub fn save_config_failed(e: &impl std::fmt::Display) -> String {
136 format!("Failed to save config: {}", e)
137}
138
139pub fn included_host_read_only(alias: &str) -> String {
140 format!(
141 "Host '{}' is from an included file and cannot be modified.",
142 alias
143 )
144}
145
146pub fn operation_failed(e: &impl std::fmt::Display) -> String {
147 format!("Failed: {}", e)
148}
149
150pub const NO_SNIPPETS: &str = "No snippets configured. Use 'purple snippet add' to create one.";
153
154pub use super::snippet_added;
155pub use super::snippet_removed;
156pub use super::snippet_updated;
157
158pub fn snippet_not_found(name: &str) -> String {
159 format!("No snippet '{}' found.", name)
160}
161
162pub fn no_hosts_with_tag(tag: &str) -> String {
163 format!("No hosts found with tag '{}'.", tag)
164}
165
166pub const SPECIFY_TARGET: &str = "Specify a host alias, --tag or --all.";
167
168pub fn beaming_up(alias: &str) -> String {
171 format!("Beaming you up to {}...\n", alias)
172}
173
174pub fn running_snippet_on(name: &str, alias: &str) -> String {
175 format!("Running '{}' on {}...\n", name, alias)
176}
177
178pub fn host_separator(alias: &str) -> String {
179 format!("── {} ──", alias)
180}
181
182pub fn exited_with_code(code: i32) -> String {
183 format!("Exited with code {}.", code)
184}
185
186pub const DONE: &str = "Done.";
187
188pub fn done_multi(name: &str, count: usize) -> String {
189 format!("Done. Ran '{}' on {} hosts.", name, count)
190}
191
192pub const PRESS_ENTER: &str = "Press Enter to continue...";
193
194pub fn host_failed(alias: &str, e: &impl std::fmt::Display) -> String {
195 format!("[{}] Failed: {}", alias, e)
196}
197
198pub fn skipping_host(alias: &str, e: &impl std::fmt::Display) -> String {
199 format!("Skipping {}: {}", alias, e)
200}
201
202pub fn password_removed(alias: &str) -> String {
205 format!("Password removed for {}.", alias)
206}
207
208pub fn log_deleted(path: &impl std::fmt::Display) -> String {
211 format!("Log file deleted: {}", path)
212}
213
214pub fn no_log_file(path: &impl std::fmt::Display) -> String {
215 format!("No log file found at {}", path)
216}
217
218pub const BUILTIN_THEMES: &str = "Built-in themes:";
221pub const CUSTOM_THEMES: &str = "\nCustom themes:";
222
223pub fn theme_set(name: &str) -> String {
224 format!("Theme set to: {}", name)
225}
226
227pub fn syncing(name: &str, summary: &str) -> String {
230 format!("\x1b[2K\rSyncing {}... {}", name, summary)
231}
232
233pub fn syncing_start(name: &str) -> String {
238 format!("Syncing {}... ", name)
239}
240
241pub fn vault_signing_host(alias: &str) -> String {
245 format!("Signing {}... ", alias)
246}
247
248pub fn vault_sign_host_block_gone(alias: &str) -> String {
253 format!(
254 " warning: {} no longer in ssh config; CertificateFile not written (cert saved on disk)",
255 alias
256 )
257}
258
259pub const SYNC_FAILED: &str = "failed.";
263
264pub fn servers_found_with_failures(count: usize, failures: usize, total: usize) -> String {
265 format!(
266 "{} servers found ({} of {} failed to fetch).",
267 count, failures, total
268 )
269}
270
271pub fn servers_found(count: usize) -> String {
272 format!("{} servers found.", count)
273}
274
275pub fn sync_result(prefix: &str, added: usize, updated: usize, unchanged: usize) -> String {
276 format!(
277 "{}Added {}, updated {}, unchanged {}.",
278 prefix, added, updated, unchanged
279 )
280}
281
282pub fn sync_removed(count: usize) -> String {
283 format!(" Removed {}.", count)
284}
285
286pub fn sync_stale(count: usize) -> String {
287 format!(" Marked {} stale.", count)
288}
289
290pub fn sync_skip_remove(display_name: &str) -> String {
291 format!(
292 "! {}: skipping --remove due to partial failures.",
293 display_name
294 )
295}
296
297pub fn sync_error(display_name: &str, e: &impl std::fmt::Display) -> String {
298 format!("! {}: {}", display_name, e)
299}
300
301pub const SYNC_SKIP_WRITE: &str =
302 "! Skipping config write due to sync failures. Fix the errors and re-run.";
303
304pub const PROXMOX_URL_REQUIRED: &str =
307 "Proxmox requires --url (e.g. --url https://pve.example.com:8006).";
308pub const AWS_REGIONS_REQUIRED: &str =
309 "AWS requires --regions (e.g. --regions us-east-1,eu-west-1).";
310pub const AZURE_REGIONS_REQUIRED: &str =
311 "Azure requires --regions with one or more subscription IDs.";
312pub const GCP_PROJECT_REQUIRED: &str = "GCP requires --project (e.g. --project my-gcp-project-id).";
313pub use super::ALIAS_PREFIX_INVALID;
314
315pub const WARN_URL_NOT_USED: &str =
316 "Warning: --url is only used by the Proxmox provider. Ignoring.";
317pub const WARN_PROFILE_NOT_USED: &str =
318 "Warning: --profile is only used by the AWS provider. Ignoring.";
319pub const WARN_PROJECT_NOT_USED: &str =
320 "Warning: --project is only used by the GCP provider. Ignoring.";
321pub const WARN_COMPARTMENT_NOT_USED: &str =
322 "Warning: --compartment is only used by the Oracle provider. Ignoring.";
323pub const WARN_NO_VERIFY_TLS_NOT_USED: &str =
324 "Warning: --no-verify-tls is only used by the Proxmox provider. Ignoring.";
325pub const WARN_VERIFY_TLS_NOT_USED: &str =
326 "Warning: --verify-tls is only used by the Proxmox provider. Ignoring.";
327pub const WARN_REGIONS_NOT_USED: &str = "Warning: --regions is only used by the AWS, Scaleway, GCP, Azure and Oracle providers. \
328 Ignoring.";
329
330pub const SYNC_RESULT_PREFIX_LIVE: &str = " ";
335pub const SYNC_RESULT_PREFIX_DRY_RUN: &str = " Would have: ";
336
337pub const PROVIDER_URL_REQUIRES_HTTPS: &str =
342 "URL must start with https://. For self-signed certificates use --no-verify-tls.";
343
344pub use super::PROVIDER_TOKEN_REQUIRED_GCP;
348pub use super::PROVIDER_TOKEN_REQUIRED_ORACLE;
349pub use super::azure_subscription_id_invalid;
350pub use super::provider_token_required;
351
352pub const SCALEWAY_REGIONS_REQUIRED: &str = "Scaleway requires --regions with one or more zones \
356 (e.g. --regions fr-par-1,nl-ams-1).";
357
358pub const ORACLE_COMPARTMENT_REQUIRED: &str =
359 "Oracle requires --compartment (e.g. --compartment ocid1.compartment.oc1..aaa...).";
360
361pub fn vault_no_role(alias: &str) -> String {
364 format!(
365 "No Vault SSH role configured for '{}'. Set it in the host form \
366 (Vault SSH Role field) or in the provider config (vault_role).",
367 alias
368 )
369}
370
371pub fn vault_cert_signed(path: &impl std::fmt::Display) -> String {
372 format!("Certificate signed: {}", path)
373}
374
375pub fn vault_sign_failed(e: &impl std::fmt::Display) -> String {
376 format!("failed: {}", e)
377}
378
379pub fn vault_config_update_warning(e: &impl std::fmt::Display) -> String {
380 format!("Warning: Failed to update SSH config: {}", e)
381}
382
383pub const NO_HOSTS: &str = "No hosts configured. Run 'purple' to add some!";
386
387pub const NO_TOKEN: &str =
390 "No token provided. Use --token, --token-stdin, or set PURPLE_TOKEN env var.";
391
392pub mod whats_new {
395 pub const HEADER: &str = "purple release notes";
396}