Skip to main content

horto_os_ui_shared/remote/
host.rs

1//! OpenSSH host target: config Host alias or `user@host`.
2
3use crate::error::{HortoError, Result};
4
5/// Destination passed to `ssh` / `scp` as the remote operand.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct HostSpec {
8    /// Raw value suitable for `ssh <spec>` (Host alias or `user@host`).
9    pub raw: String,
10}
11
12/// Parse a non-empty Host alias or `user@host` string.
13///
14/// # Errors
15///
16/// Returns [`crate::HortoError::Message`] when the string is empty or only whitespace.
17pub fn parse_host_spec(input: &str) -> Result<HostSpec> {
18    let raw = input.trim().to_owned();
19    if raw.is_empty() {
20        return Err(HortoError::msg(
21            "remote host is empty; pass an OpenSSH Host alias or user@host",
22        ));
23    }
24    if raw.contains(char::is_whitespace) {
25        return Err(HortoError::msg(format!(
26            "remote host must not contain whitespace: {raw:?}"
27        )));
28    }
29    Ok(HostSpec { raw })
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn accepts_alias_and_user_host() {
38        assert_eq!(parse_host_spec(" horto-box ").unwrap().raw, "horto-box");
39        assert_eq!(
40            parse_host_spec("greg@192.168.1.10").unwrap().raw,
41            "greg@192.168.1.10"
42        );
43    }
44
45    #[test]
46    fn rejects_empty_or_spaces() {
47        assert!(parse_host_spec("").is_err());
48        assert!(parse_host_spec("  ").is_err());
49        assert!(parse_host_spec("bad host").is_err());
50    }
51}