Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
129 lines
3.8 KiB
Rust
129 lines
3.8 KiB
Rust
//! Remote-location value objects: the strategy that locates where a project's
|
|
//! filesystem and processes actually live (local, SSH, or WSL).
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::error::DomainError;
|
|
|
|
/// SSH authentication strategy.
|
|
///
|
|
/// The domain only models *which* strategy is selected; resolving keys,
|
|
/// agents or known-hosts is an infrastructure concern.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase", tag = "type")]
|
|
pub enum SshAuth {
|
|
/// Use the running SSH agent.
|
|
Agent,
|
|
/// Use a private-key file at the given (remote-machine-agnostic) path.
|
|
Key {
|
|
/// Path to the private key on the local machine.
|
|
path: String,
|
|
},
|
|
/// Interactive / stored password authentication.
|
|
Password,
|
|
}
|
|
|
|
/// Strategy describing where a project is physically located and executed.
|
|
///
|
|
/// Invariants:
|
|
/// - `Ssh.port` ∈ `1..=65535`,
|
|
/// - `Ssh.host` / `Ssh.user` / `Ssh.remote_root` non-empty,
|
|
/// - `Wsl.distro` non-empty.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase", tag = "kind")]
|
|
pub enum RemoteRef {
|
|
/// The local machine.
|
|
Local,
|
|
/// A remote host reached over SSH.
|
|
#[serde(rename_all = "camelCase")]
|
|
Ssh {
|
|
/// Hostname or IP.
|
|
host: String,
|
|
/// TCP port (`1..=65535`).
|
|
port: u16,
|
|
/// Remote user.
|
|
user: String,
|
|
/// Authentication strategy.
|
|
auth: SshAuth,
|
|
/// Absolute root path on the remote machine.
|
|
remote_root: String,
|
|
},
|
|
/// A Windows Subsystem for Linux distribution.
|
|
Wsl {
|
|
/// Distribution name (e.g. `Ubuntu-22.04`).
|
|
distro: String,
|
|
},
|
|
}
|
|
|
|
impl RemoteRef {
|
|
/// Convenience constructor for the local machine.
|
|
#[must_use]
|
|
pub const fn local() -> Self {
|
|
Self::Local
|
|
}
|
|
|
|
/// Validated constructor for an SSH remote.
|
|
///
|
|
/// # Errors
|
|
/// Returns [`DomainError`] if the port is `0`, or if `host`, `user` or
|
|
/// `remote_root` is empty.
|
|
pub fn ssh(
|
|
host: impl Into<String>,
|
|
port: u16,
|
|
user: impl Into<String>,
|
|
auth: SshAuth,
|
|
remote_root: impl Into<String>,
|
|
) -> Result<Self, DomainError> {
|
|
let host = host.into();
|
|
let user = user.into();
|
|
let remote_root = remote_root.into();
|
|
crate::validation::non_empty(&host, "ssh.host")?;
|
|
crate::validation::non_empty(&user, "ssh.user")?;
|
|
crate::validation::non_empty(&remote_root, "ssh.remote_root")?;
|
|
// u16 already bounds the upper end; only port 0 is invalid.
|
|
if port == 0 {
|
|
return Err(DomainError::InvalidPort { port: 0 });
|
|
}
|
|
Ok(Self::Ssh {
|
|
host,
|
|
port,
|
|
user,
|
|
auth,
|
|
remote_root,
|
|
})
|
|
}
|
|
|
|
/// Validated constructor for a WSL remote.
|
|
///
|
|
/// # Errors
|
|
/// Returns [`DomainError`] if `distro` is empty.
|
|
pub fn wsl(distro: impl Into<String>) -> Result<Self, DomainError> {
|
|
let distro = distro.into();
|
|
crate::validation::non_empty(&distro, "wsl.distro")?;
|
|
Ok(Self::Wsl { distro })
|
|
}
|
|
|
|
/// Returns the coarse kind of this remote, for adapter selection.
|
|
#[must_use]
|
|
pub fn kind(&self) -> RemoteKind {
|
|
match self {
|
|
Self::Local => RemoteKind::Local,
|
|
Self::Ssh { .. } => RemoteKind::Ssh,
|
|
Self::Wsl { .. } => RemoteKind::Wsl,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Coarse discriminant used by the [`crate::ports::RemoteHost`] port to advertise
|
|
/// its kind without exposing connection details.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum RemoteKind {
|
|
/// Local execution.
|
|
Local,
|
|
/// SSH remote.
|
|
Ssh,
|
|
/// WSL distribution.
|
|
Wsl,
|
|
}
|