purple_ssh/messages/
container.rs1pub const CONTAINER_ID_EMPTY: &str = "Container ID must not be empty.";
14pub const CONTAINER_RUNTIME_MISSING: &str = "No container runtime found. Install Docker or Podman.";
15
16pub fn container_id_invalid_char(c: char) -> String {
17 format!("Container ID contains invalid character: '{c}'")
18}
19
20pub fn container_unknown_sentinel(s: &str) -> String {
21 format!("Unknown sentinel: {s}")
22}
23
24pub fn container_invalid_id(reason: &str) -> String {
25 format!("Container exec blocked: {reason}")
26}
27
28pub fn scp_copying_one(source: &str) -> String {
31 format!("Copying {}...", source)
32}
33
34pub fn scp_copying_many(count: usize) -> String {
37 format!("Copying {} files...", count)
38}
39
40pub fn scp_failed_exit_code(code: i32) -> String {
43 format!("Copy failed (exit code {}).", code)
44}
45
46pub fn scp_spawn_failed(e: &impl std::fmt::Display) -> String {
50 format!("scp failed: {}", e)
51}
52
53pub fn container_action_complete(action: &str) -> String {
56 format!("Container {} complete.", action)
57}
58
59pub const HOST_KEY_UNKNOWN: &str = "Host key unknown. Connect first (Enter) to trust the host.";
60pub const HOST_KEY_CHANGED: &str =
61 "Host key changed. Possible tampering or server re-install. Clear with ssh-keygen -R.";
62
63pub const CONTAINER_RUNTIME_NOT_FOUND: &str = "Docker or Podman not found on remote host.";
67pub const CONTAINER_PERMISSION_DENIED: &str =
68 "Permission denied. Is your user in the docker group?";
69pub const CONTAINER_DAEMON_NOT_RUNNING: &str = "Container daemon is not running.";
70pub const CONTAINER_CONNECTION_REFUSED: &str = "Connection refused.";
71pub const CONTAINER_HOST_UNREACHABLE: &str = "Host unreachable.";
72
73pub fn container_command_failed(code: i32) -> String {
77 format!("Command failed with code {}.", code)
78}
79
80pub const CONTAINER_INSPECT_EMPTY: &str = "Inspect returned no data.";
82
83pub fn container_inspect_parse_failed(reason: &str) -> String {
85 format!("Inspect parse failed: {}", reason)
86}
87
88pub fn container_not_running(name: &str) -> String {
92 format!("{} is not running. Cannot exec.", name)
93}
94
95pub const DEMO_CONTAINER_EXEC_DISABLED: &str = "Demo mode: container exec disabled.";
97
98pub fn container_exec_opened_in_tmux(name: &str, alias: &str) -> String {
100 format!("Opened {} on {} in tmux window.", name, alias)
101}
102
103pub fn container_exec_ended(name: &str) -> String {
105 format!("Container shell ended: {}.", name)
106}
107
108pub fn container_exec_failed_with_reason(name: &str, reason: &str) -> String {
110 format!("Container exec failed for {}: {}", name, reason)
111}
112
113pub fn container_exec_exited_with_code(name: &str, code: i32) -> String {
115 format!("Container exec for {} exited with code {}.", name, code)
116}
117
118pub fn container_exec_spawn_failed(name: &str) -> String {
120 format!("Failed to launch ssh for container {}.", name)
121}
122
123pub const CONTAINER_EXEC_INVALID_COMMAND: &str =
125 "Command rejected: control characters not allowed.";
126
127pub const CONTAINER_LOGS_LOADING: &str = "fetching logs…";
131
132pub fn container_logs_fetched(secs_ago: u64) -> String {
135 format!(
136 "fetched {} ago",
137 crate::containers::format_uptime_short(secs_ago)
138 )
139}
140
141pub fn container_logs_failed(reason: &str) -> String {
143 format!("logs fetch failed: {}", reason)
144}
145
146pub fn container_logs_search_position(current: usize, total: usize) -> String {
149 format!("{} of {}", current, total)
150}
151
152pub const CONTAINER_LOGS_SEARCH_NO_MATCHES: &str = "no matches";
154
155pub const CONTAINER_RESTART_BODY: &str =
159 "Sends SIGTERM, waits 10s, then SIGKILL. Live connections will drop.";
160pub const CONTAINER_STOP_BODY: &str = "Sends SIGTERM, waits 10s, then SIGKILL. Container will not restart unless its policy reschedules it.";
161
162pub fn container_stack_unknown(name: &str) -> String {
165 format!("Stack unknown for {}: open the detail panel first.", name)
166}
167
168pub fn container_stack_no_running(project: &str) -> String {
169 format!("Stack {} has no running members to restart.", project)
170}
171
172pub const CONTAINER_STACK_RESTART_BODY: &str = "Restart cycles every running member one by one. Exited members are not touched. Live connections will drop.";
173
174pub const CONTAINER_HOST_RESTART_ALL_BODY: &str = "Restart cycles every running container on the host one by one. Exited containers are not touched. Live connections will drop.";
180
181pub const CONTAINER_HOST_STOP_ALL_BODY: &str = "Stops every running container on the host one by one. Exited containers are not touched. Restart policies may reschedule them.";
183
184pub fn container_action_needs_single(action: &str) -> String {
190 format!(
191 "{} need a single container. Place the cursor on a container row.",
192 action.to_lowercase()
193 )
194}
195
196pub fn container_host_no_running(alias: &str) -> String {
198 format!("No running containers on {}.", alias)
199}
200
201pub fn container_refreshing(alias: &str) -> String {
205 format!("Refreshing {}…", alias)
206}
207
208pub const REFRESH_BATCH_ALREADY_RUNNING: &str = "Refresh already in progress.";
210
211pub const REFRESH_NOTHING_TO_REFRESH: &str = "No cached hosts to refresh. Press 'a' to add a host.";
213
214pub fn container_refresh_progress(done: usize, total: usize) -> String {
216 format!("Refreshing {}/{} hosts…", done, total)
217}
218
219pub fn container_refresh_complete(total: usize) -> String {
221 format!(
222 "Refreshed {} host{}.",
223 total,
224 if total == 1 { "" } else { "s" }
225 )
226}
227
228pub const CONTAINER_HOST_PICKER_NO_MATCH: &str = "No hosts match.";
230
231pub const CONTAINER_HOST_PICKER_NOTHING_TO_ADD: &str =
233 "All hosts already cached. Use 'r' or 'R' to refresh.";