1pub const FAILED_TO_SAVE: &str = "Failed to save";
11pub fn failed_to_save(e: &impl std::fmt::Display) -> String {
12 format!("{}: {}", FAILED_TO_SAVE, e)
13}
14
15pub const CONFIG_CHANGED_EXTERNALLY: &str =
16 "Config changed externally. Press Esc and re-open to pick up changes.";
17
18pub const DEMO_CONNECTION_DISABLED: &str = "Demo mode. Connection disabled.";
21pub const DEMO_SYNC_DISABLED: &str = "Demo mode. Sync disabled.";
22pub const DEMO_TUNNELS_DISABLED: &str = "Demo mode. Tunnels disabled.";
23pub const DEMO_VAULT_SIGNING_DISABLED: &str = "Demo mode. Vault SSH signing disabled.";
24pub const DEMO_FILE_BROWSER_DISABLED: &str = "Demo mode. File browser disabled.";
25pub const DEMO_CONTAINER_REFRESH_DISABLED: &str = "Demo mode. Container refresh disabled.";
26pub const DEMO_CONTAINER_ACTIONS_DISABLED: &str = "Demo mode. Container actions disabled.";
27pub const DEMO_EXECUTION_DISABLED: &str = "Demo mode. Execution disabled.";
28pub const DEMO_PROVIDER_CHANGES_DISABLED: &str = "Demo mode. Provider config changes disabled.";
29
30pub fn stale_host(hint: &str) -> String {
33 format!("Stale host.{}", hint)
34}
35
36pub fn copied_ssh_command(alias: &str) -> String {
39 format!("Copied SSH command for {}.", alias)
40}
41
42pub fn copied_config_block(alias: &str) -> String {
43 format!("Copied config block for {}.", alias)
44}
45
46pub fn showing_unreachable(count: usize) -> String {
47 format!(
48 "Showing {} unreachable host{}.",
49 count,
50 if count == 1 { "" } else { "s" }
51 )
52}
53
54pub fn sorted_by(label: &str) -> String {
55 format!("Sorted by {}.", label)
56}
57
58pub fn sorted_by_save_failed(label: &str, e: &impl std::fmt::Display) -> String {
59 format!("Sorted by {}. (save failed: {})", label, e)
60}
61
62pub fn grouped_by(label: &str) -> String {
63 format!("Grouped by {}.", label)
64}
65
66pub fn grouped_by_save_failed(label: &str, e: &impl std::fmt::Display) -> String {
67 format!("Grouped by {}. (save failed: {})", label, e)
68}
69
70pub const UNGROUPED: &str = "Ungrouped.";
71
72pub fn ungrouped_save_failed(e: &impl std::fmt::Display) -> String {
73 format!("Ungrouped. (save failed: {})", e)
74}
75
76pub const GROUPED_BY_TAG: &str = "Grouped by tag.";
77
78pub fn grouped_by_tag_save_failed(e: &impl std::fmt::Display) -> String {
79 format!("Grouped by tag. (save failed: {})", e)
80}
81
82pub fn host_restored(alias: &str) -> String {
83 format!("{} is back from the dead.", alias)
84}
85
86pub fn restored_tags(count: usize) -> String {
87 format!(
88 "Restored tags on {} host{}.",
89 count,
90 if count == 1 { "" } else { "s" }
91 )
92}
93
94pub const NOTHING_TO_UNDO: &str = "Nothing to undo.";
95pub const NO_IMPORTABLE_HOSTS: &str = "No importable hosts in known_hosts.";
96pub const NO_STALE_HOSTS: &str = "No stale hosts.";
97pub const NO_HOST_SELECTED: &str = "No host selected.";
98pub const NO_HOSTS_TO_RUN: &str = "No hosts to run on.";
99pub const NO_HOSTS_TO_TAG: &str = "No hosts to tag.";
100pub const PING_FIRST: &str = "Ping first (p/P), then filter with !.";
101pub const PINGING_ALL: &str = "Pinging all the things...";
102
103pub fn included_file_edit(name: &str) -> String {
104 format!("{} is in an included file. Edit it there.", name)
105}
106
107pub fn included_file_delete(name: &str) -> String {
108 format!("{} is in an included file. Delete it there.", name)
109}
110
111pub fn included_file_clone(name: &str) -> String {
112 format!("{} is in an included file. Clone it there.", name)
113}
114
115pub fn included_host_lives_in(alias: &str, path: &impl std::fmt::Display) -> String {
116 format!("{} lives in {}. Edit it there.", alias, path)
117}
118
119pub fn included_host_clone_there(alias: &str, path: &impl std::fmt::Display) -> String {
120 format!("{} lives in {}. Clone it there.", alias, path)
121}
122
123pub fn included_host_tag_there(alias: &str, path: &impl std::fmt::Display) -> String {
124 format!("{} is included from {}. Tag it there.", alias, path)
125}
126
127pub const HOST_NOT_FOUND_IN_CONFIG: &str = "Host not found in config.";
128
129pub const SMART_PARSED: &str = "Smart-parsed that for you. Check the fields.";
132pub const LOOKS_LIKE_ADDRESS: &str = "Looks like an address. Suggested as Host.";
133
134pub fn goodbye_host(alias: &str) -> String {
137 format!("Goodbye, {}. We barely knew ye. (u to undo)", alias)
138}
139
140pub fn host_not_found(alias: &str) -> String {
141 format!("Host '{}' not found.", alias)
142}
143
144pub fn siblings_stripped(alias: &str, sibling_count: usize) -> String {
148 if sibling_count == 1 {
149 format!(
150 "Stripped {}. 1 sibling alias kept its shared config.",
151 alias
152 )
153 } else {
154 format!(
155 "Stripped {}. {} sibling aliases kept their shared config.",
156 alias, sibling_count
157 )
158 }
159}
160
161pub fn confirm_delete_siblings_note(siblings: &[String]) -> String {
165 let shown: Vec<&str> = siblings.iter().take(3).map(String::as_str).collect();
166 let tail = if siblings.len() > shown.len() {
167 format!(" +{} more", siblings.len() - shown.len())
168 } else {
169 String::new()
170 };
171 format!("Siblings kept: {}{}", shown.join(", "), tail)
172}
173
174pub fn cert_cleanup_warning(path: &impl std::fmt::Display, e: &impl std::fmt::Display) -> String {
175 format!("Warning: failed to clean up Vault SSH cert {}: {}", path, e)
176}
177
178pub const CLONED_VAULT_CLEARED: &str = "Cloned. Vault SSH role cleared on copy.";
181
182pub const TUNNEL_REMOVED: &str = "Tunnel removed.";
185pub const TUNNEL_SAVED: &str = "Tunnel saved.";
186pub const TUNNEL_NOT_FOUND: &str = "Tunnel not found in config.";
187pub const TUNNEL_INCLUDED_READ_ONLY: &str = "Included host. Tunnels are read-only.";
188pub const TUNNEL_ORIGINAL_NOT_FOUND: &str = "Original tunnel not found in config.";
189pub const TUNNEL_LIST_CHANGED: &str = "Tunnel list changed externally. Press Esc and re-open.";
190pub const TUNNEL_DUPLICATE: &str = "Duplicate tunnel already configured.";
191
192pub fn tunnel_stopped(alias: &str) -> String {
193 format!("Tunnel for {} stopped.", alias)
194}
195
196pub fn tunnel_started(alias: &str) -> String {
197 format!("Tunnel for {} started.", alias)
198}
199
200pub fn tunnel_start_failed(e: &impl std::fmt::Display) -> String {
201 format!("Failed to start tunnel: {}", e)
202}
203
204pub fn pinging_host(alias: &str, show_hint: bool) -> String {
207 if show_hint {
208 format!("Pinging {}... (Shift+P pings all)", alias)
209 } else {
210 format!("Pinging {}...", alias)
211 }
212}
213
214pub fn bastion_not_found(alias: &str) -> String {
215 format!("Bastion {} not found in config.", alias)
216}
217
218pub fn provider_removed(display_name: &str) -> String {
221 format!(
222 "Removed {} configuration. Synced hosts remain in your SSH config.",
223 display_name
224 )
225}
226
227pub fn provider_not_configured(display_name: &str) -> String {
228 format!("{} is not configured. Nothing to remove.", display_name)
229}
230
231pub fn provider_configure_first(display_name: &str) -> String {
232 format!("Configure {} first. Press Enter to set up.", display_name)
233}
234
235pub fn provider_saved_syncing(display_name: &str) -> String {
236 format!("Saved {} configuration. Syncing...", display_name)
237}
238
239pub fn provider_saved(display_name: &str) -> String {
240 format!("Saved {} configuration.", display_name)
241}
242
243pub fn syncing_provider(display_name: &str) -> String {
244 format!("Syncing {}...", display_name)
245}
246
247pub fn no_stale_hosts_for(display_name: &str) -> String {
248 format!("No stale hosts for {}.", display_name)
249}
250
251pub fn contains_control_chars(name: &str) -> String {
252 format!("{} contains control characters.", name)
253}
254
255pub const TOKEN_FORMAT_AWS: &str = "Token format: AccessKeyId:SecretAccessKey";
256pub const URL_REQUIRED_PROXMOX: &str = "URL is required for Proxmox VE.";
257pub const PROJECT_REQUIRED_GCP: &str = "Project ID can't be empty. Set your GCP project ID.";
258pub const COMPARTMENT_REQUIRED_OCI: &str =
259 "Compartment can't be empty. Set your OCI compartment OCID.";
260pub const REGIONS_REQUIRED_AWS: &str = "Select at least one AWS region.";
261pub const ZONES_REQUIRED_SCALEWAY: &str = "Select at least one Scaleway zone.";
262pub const SUBSCRIPTIONS_REQUIRED_AZURE: &str = "Enter at least one Azure subscription ID.";
263pub const ALIAS_PREFIX_INVALID: &str =
264 "Alias prefix can't contain spaces or pattern characters (*, ?, [, !).";
265pub const USER_NO_WHITESPACE: &str = "User can't contain whitespace.";
266pub const VAULT_ROLE_FORMAT: &str = "Vault SSH role must be in the form <mount>/sign/<role>.";
267
268pub const VAULT_SIGNING_CANCELLED: &str = "Vault SSH signing cancelled.";
271pub const VAULT_NO_ROLE_CONFIGURED: &str = "No Vault SSH role configured. Set one in the host form \
272 (Vault SSH role field) or on a provider for shared defaults.";
273pub const VAULT_NO_HOSTS_WITH_ROLE: &str = "No hosts with a Vault SSH role configured.";
274pub const VAULT_ALL_CERTS_VALID: &str = "All Vault SSH certificates are still valid.";
275pub const VAULT_NO_ADDRESS: &str = "No Vault address set. Edit the host (e) or provider \
276 and fill in the Vault SSH Address field.";
277
278pub fn vault_error(msg: &str) -> String {
279 format!("Vault SSH: {}", msg)
280}
281
282pub fn vault_signed(alias: &str) -> String {
283 format!("Signed Vault SSH cert for {}", alias)
284}
285
286pub fn vault_sign_failed(alias: &str, message: &str) -> String {
287 format!("Vault SSH: failed to sign {}: {}", alias, message)
288}
289
290pub fn vault_signing_progress(spinner: &str, done: usize, total: usize, alias: &str) -> String {
291 format!(
292 "{} Signing {}/{}: {} (V to cancel)",
293 spinner, done, total, alias
294 )
295}
296
297pub fn vault_cert_saved_host_gone(alias: &str) -> String {
298 format!(
299 "Vault SSH cert saved for {} but host no longer in config \
300 (renamed or deleted). CertificateFile NOT written.",
301 alias
302 )
303}
304
305pub fn vault_spawn_failed(e: &impl std::fmt::Display) -> String {
306 format!("Vault SSH: failed to spawn signing thread: {}", e)
307}
308
309pub fn vault_cert_check_failed(alias: &str, message: &str) -> String {
310 format!("Cert check failed for {}: {}", alias, message)
311}
312
313pub fn vault_role_set(role: &str) -> String {
314 format!("Vault SSH role set to {}.", role)
315}
316
317pub fn snippet_removed(name: &str) -> String {
320 format!("Removed snippet '{}'.", name)
321}
322
323pub fn snippet_added(name: &str) -> String {
324 format!("Added snippet '{}'.", name)
325}
326
327pub fn snippet_updated(name: &str) -> String {
328 format!("Updated snippet '{}'.", name)
329}
330
331pub fn snippet_exists(name: &str) -> String {
332 format!("'{}' already exists.", name)
333}
334
335pub const OUTPUT_COPIED: &str = "Output copied.";
336
337pub fn copy_failed(e: &impl std::fmt::Display) -> String {
338 format!("Copy failed: {}", e)
339}
340
341pub const GLOBAL_DEFAULT_CLEARED: &str = "Global default cleared.";
344pub const PASSWORD_SOURCE_CLEARED: &str = "Password source cleared.";
345
346pub fn global_default_set(label: &str) -> String {
347 format!("Global default set to {}.", label)
348}
349
350pub fn password_source_set(label: &str) -> String {
351 format!("Password source set to {}.", label)
352}
353
354pub fn complete_path(label: &str) -> String {
355 format!("Complete the {} path.", label)
356}
357
358pub fn key_selected(name: &str) -> String {
359 format!("Locked and loaded with {}.", name)
360}
361
362pub fn proxy_jump_set(alias: &str) -> String {
363 format!("Jumping through {}.", alias)
364}
365
366pub fn save_default_failed(e: &impl std::fmt::Display) -> String {
367 format!("Failed to save default: {}", e)
368}
369
370pub fn container_action_complete(action: &str) -> String {
373 format!("Container {} complete.", action)
374}
375
376pub const HOST_KEY_UNKNOWN: &str = "Host key unknown. Connect first (Enter) to trust the host.";
377pub const HOST_KEY_CHANGED: &str =
378 "Host key changed. Possible tampering or server re-install. Clear with ssh-keygen -R.";
379
380pub fn imported_hosts(imported: usize, skipped: usize) -> String {
383 format!(
384 "Imported {} host{}, skipped {} duplicate{}.",
385 imported,
386 if imported == 1 { "" } else { "s" },
387 skipped,
388 if skipped == 1 { "" } else { "s" }
389 )
390}
391
392pub fn all_hosts_exist(skipped: usize) -> String {
393 if skipped == 1 {
394 "Host already exists.".to_string()
395 } else {
396 format!("All {} hosts already exist.", skipped)
397 }
398}
399
400pub fn config_repaired(groups: usize, orphaned: usize) -> String {
403 format!(
404 "Repaired SSH config ({} absorbed, {} orphaned group headers).",
405 groups, orphaned
406 )
407}
408
409pub fn no_exact_match(alias: &str) -> String {
410 format!("No exact match for '{}'. Here's what we found.", alias)
411}
412
413pub fn group_pref_reset_failed(e: &impl std::fmt::Display) -> String {
414 format!("Group preference reset. (save failed: {})", e)
415}
416
417pub fn opened_in_tmux(alias: &str) -> String {
420 format!("Opened {} in new tmux window.", alias)
421}
422
423pub fn tmux_error(e: &impl std::fmt::Display) -> String {
424 format!("tmux: {}", e)
425}
426
427pub fn connection_failed(alias: &str) -> String {
428 format!("Connection to {} failed.", alias)
429}
430
431pub fn host_key_remove_failed(stderr: &str) -> String {
434 format!("Failed to remove host key: {}", stderr)
435}
436
437pub fn ssh_keygen_failed(e: &impl std::fmt::Display) -> String {
438 format!("Failed to run ssh-keygen: {}", e)
439}
440
441pub const TRANSFER_COMPLETE: &str = "Transfer complete.";
444
445pub const PING_EXPIRED: &str = "Ping expired. Press P to refresh.";
448
449pub fn provider_event(name: &str, message: &str) -> String {
450 format!("{}: {}", name, message)
451}
452
453pub fn vault_config_reapply_failed(signed: usize, e: &impl std::fmt::Display) -> String {
456 format!(
457 "External edits detected; signed {} certs but failed to re-apply CertificateFile: {}",
458 signed, e
459 )
460}
461
462pub fn vault_external_edits_merged(summary: &str, reapplied: usize) -> String {
463 format!(
464 "{} External ssh config edits detected, merged {} CertificateFile directives.",
465 summary, reapplied
466 )
467}
468
469pub fn vault_external_edits_no_write(summary: &str) -> String {
470 format!(
471 "{} External ssh config edits detected; certs on disk, no CertificateFile written.",
472 summary
473 )
474}
475
476pub fn vault_reparse_failed(signed: usize, e: &impl std::fmt::Display) -> String {
477 format!(
478 "Signed {} certs but cannot re-parse ssh config after external edit: {}. \
479 Certs are on disk under ~/.purple/certs/.",
480 signed, e
481 )
482}
483
484pub fn vault_config_update_failed(signed: usize, e: &impl std::fmt::Display) -> String {
485 format!(
486 "Signed {} certs but failed to update SSH config: {}",
487 signed, e
488 )
489}
490
491pub fn vault_config_write_after_sign(e: &impl std::fmt::Display) -> String {
492 format!("Failed to update config after vault signing: {}", e)
493}
494
495pub fn removed_host_key(hostname: &str) -> String {
500 format!("Removed host key for {}. Reconnecting...", hostname)
501}
502
503pub fn tagged_host(alias: &str, count: usize) -> String {
506 format!(
507 "Tagged {} with {} label{}.",
508 alias,
509 count,
510 if count == 1 { "" } else { "s" }
511 )
512}
513
514pub fn config_reloaded(count: usize) -> String {
517 format!("Config reloaded. {} hosts.", count)
518}
519
520pub fn synced_progress(names: &str) -> String {
523 format!("Synced: {}...", names)
524}
525
526pub fn synced_done(names: &str) -> String {
527 format!("Synced: {}", names)
528}
529
530pub const SYNC_THREAD_SPAWN_FAILED: &str = "Failed to start sync thread.";
531
532pub const SYNC_UNKNOWN_PROVIDER: &str = "Unknown provider.";
533
534pub fn vault_signing_cancelled_summary(
537 signed: u32,
538 failed: u32,
539 first_error: Option<&str>,
540) -> String {
541 let mut msg = format!(
542 "Vault SSH signing cancelled ({} signed, {} failed)",
543 signed, failed
544 );
545 if let Some(err) = first_error {
546 msg.push_str(": ");
547 msg.push_str(err);
548 }
549 msg
550}
551
552pub fn regions_selected_count(count: usize, label: &str) -> String {
555 let s = if count == 1 { "" } else { "s" };
556 format!("{} {}{} selected.", count, label, s)
557}
558
559pub const NO_CLIPBOARD_TOOL: &str =
564 "No clipboard tool found. Install pbcopy (macOS), wl-copy (Wayland), or xclip/xsel (X11).";
565
566#[path = "messages/cli.rs"]
569pub mod cli;
570
571pub mod update {
574 pub const WHATS_NEW_HINT: &str = "Press n inside purple to see what's new.";
575 pub const DONE: &str = "done.";
576 pub const CHECKSUM_OK: &str = "ok.";
577 pub const SUDO_WARNING: &str =
578 "Running via sudo. Consider fixing directory permissions instead.";
579
580 pub fn already_on(current: &str) -> String {
581 format!("already on v{} (latest).", current)
582 }
583
584 pub fn available(latest: &str, current: &str) -> String {
585 format!("v{} available (current: v{}).", latest, current)
586 }
587
588 pub fn header(bold_name: &str) -> String {
589 format!("\n {} updater\n", bold_name)
590 }
591
592 pub fn binary_path(path: &std::path::Path) -> String {
593 format!(" Binary: {}", path.display())
594 }
595
596 pub fn installed_at(bold_version: &str, path: &std::path::Path) -> String {
597 format!("\n {} installed at {}.", bold_version, path.display())
598 }
599
600 pub fn whats_new_hint_indented() -> String {
601 format!("\n {}", WHATS_NEW_HINT)
602 }
603}
604
605pub mod askpass {
608 pub const BW_NOT_FOUND: &str = "Bitwarden CLI (bw) not found. SSH will prompt for password.";
609 pub const BW_NOT_LOGGED_IN: &str = "Bitwarden vault not logged in. Run 'bw login' first.";
610 pub const EMPTY_PASSWORD: &str = "Empty password. SSH will prompt for password.";
611 pub const PASSWORD_IN_KEYCHAIN: &str = "Password stored in keychain.";
612
613 pub fn read_failed(e: &impl std::fmt::Display) -> String {
614 format!("Failed to read password: {}", e)
615 }
616
617 pub fn unlock_failed_retry(e: &impl std::fmt::Display) -> String {
618 format!("Unlock failed: {}. Try again.", e)
619 }
620
621 pub fn unlock_failed_prompt(e: &impl std::fmt::Display) -> String {
622 format!("Unlock failed: {}. SSH will prompt for password.", e)
623 }
624}
625
626pub mod logging {
629 pub fn init_failed(e: &impl std::fmt::Display) -> String {
630 format!("[purple] Failed to initialize logger: {}", e)
631 }
632
633 pub const SSH_VERSION_FAILED: &str = "[purple] Failed to detect SSH version. Is ssh installed?";
634}
635
636pub mod hints {
642 pub const IDENTITY_FILE_PICK: &str = "Space to pick a key";
647 pub const DEFAULT_SSH_USER: &str = "root";
648
649 pub const HOST_ALIAS: &str = "e.g. prod or db-01";
651 pub const HOST_ALIAS_PATTERN: &str = "10.0.0.* or *.example.com";
652 pub const HOST_HOSTNAME: &str = "192.168.1.1 or example.com";
653 pub const HOST_PORT: &str = "22";
654 pub const HOST_PROXY_JUMP: &str = "Space to pick a host";
655 pub const HOST_VAULT_SSH: &str = "e.g. ssh-client-signer/sign/my-role (auth via vault login)";
656 pub const HOST_VAULT_SSH_PICKER: &str = "Space to pick a role or type one";
657 pub const HOST_VAULT_ADDR: &str =
658 "e.g. http://127.0.0.1:8200 (inherits from provider or env when empty)";
659 pub const HOST_TAGS: &str = "e.g. prod, staging, us-east (comma-separated)";
660 pub const HOST_ASKPASS_PICK: &str = "Space to pick a source";
661
662 pub fn askpass_default(default: &str) -> String {
663 format!("default: {}", default)
664 }
665
666 pub fn inherits_from(value: &str, provider: &str) -> String {
667 format!("inherits {} from {}", value, provider)
668 }
669
670 pub const TUNNEL_BIND_PORT: &str = "8080";
672 pub const TUNNEL_REMOTE_HOST: &str = "localhost";
673 pub const TUNNEL_REMOTE_PORT: &str = "80";
674
675 pub const SNIPPET_NAME: &str = "check-disk";
677 pub const SNIPPET_COMMAND: &str = "df -h";
678 pub const SNIPPET_OPTIONAL: &str = "(optional)";
679
680 pub const PROVIDER_URL: &str = "https://pve.example.com:8006";
682 pub const PROVIDER_TOKEN_DEFAULT: &str = "your-api-token";
683 pub const PROVIDER_TOKEN_PROXMOX: &str = "user@pam!token=secret";
684 pub const PROVIDER_TOKEN_AWS: &str = "AccessKeyId:Secret (or use Profile)";
685 pub const PROVIDER_TOKEN_GCP: &str = "/path/to/service-account.json (or access token)";
686 pub const PROVIDER_TOKEN_AZURE: &str = "/path/to/service-principal.json (or access token)";
687 pub const PROVIDER_TOKEN_TAILSCALE: &str = "API key (leave empty for local CLI)";
688 pub const PROVIDER_TOKEN_ORACLE: &str = "~/.oci/config";
689 pub const PROVIDER_TOKEN_OVH: &str = "app_key:app_secret:consumer_key";
690 pub const PROVIDER_PROFILE: &str = "Name from ~/.aws/credentials (or use Token)";
691 pub const PROVIDER_PROJECT_DEFAULT: &str = "my-gcp-project-id";
692 pub const PROVIDER_PROJECT_OVH: &str = "Public Cloud project ID";
693 pub const PROVIDER_COMPARTMENT: &str = "ocid1.compartment.oc1..aaaa...";
694 pub const PROVIDER_REGIONS_DEFAULT: &str = "Space to select regions";
695 pub const PROVIDER_REGIONS_GCP: &str = "Space to select zones (empty = all)";
696 pub const PROVIDER_REGIONS_SCALEWAY: &str = "Space to select zones";
697 pub const PROVIDER_REGIONS_AZURE: &str = "comma-separated subscription IDs";
699 pub const PROVIDER_REGIONS_OVH: &str = "Space to select endpoint (default: EU)";
700 pub const PROVIDER_USER_AWS: &str = "ec2-user";
701 pub const PROVIDER_USER_GCP: &str = "ubuntu";
702 pub const PROVIDER_USER_AZURE: &str = "azureuser";
703 pub const PROVIDER_USER_ORACLE: &str = "opc";
704 pub const PROVIDER_USER_OVH: &str = "ubuntu";
705 pub const PROVIDER_VAULT_ROLE: &str =
706 "e.g. ssh-client-signer/sign/my-role (vault login; inherited)";
707 pub const PROVIDER_VAULT_ADDR: &str = "e.g. http://127.0.0.1:8200 (inherited by all hosts)";
708 pub const PROVIDER_ALIAS_PREFIX_DEFAULT: &str = "prefix";
709}
710
711#[cfg(test)]
712mod hints_tests {
713 use super::hints;
714
715 #[test]
716 fn askpass_default_formats() {
717 assert_eq!(hints::askpass_default("keychain"), "default: keychain");
718 }
719
720 #[test]
721 fn askpass_default_formats_empty() {
722 assert_eq!(hints::askpass_default(""), "default: ");
723 }
724
725 #[test]
726 fn inherits_from_formats() {
727 assert_eq!(
728 hints::inherits_from("role/x", "aws"),
729 "inherits role/x from aws"
730 );
731 }
732
733 #[test]
734 fn picker_hints_mention_space_not_enter() {
735 for s in [
738 hints::IDENTITY_FILE_PICK,
739 hints::HOST_PROXY_JUMP,
740 hints::HOST_VAULT_SSH_PICKER,
741 hints::HOST_ASKPASS_PICK,
742 hints::PROVIDER_REGIONS_DEFAULT,
743 hints::PROVIDER_REGIONS_GCP,
744 hints::PROVIDER_REGIONS_SCALEWAY,
745 hints::PROVIDER_REGIONS_OVH,
746 ] {
747 assert!(
748 s.starts_with("Space "),
749 "picker hint must mention Space: {s}"
750 );
751 assert!(!s.contains("Enter "), "picker hint must not say Enter: {s}");
752 }
753 }
754}
755
756#[path = "messages/whats_new.rs"]
757pub mod whats_new;
758
759#[path = "messages/whats_new_toast.rs"]
760pub mod whats_new_toast;