343 lines
8.9 KiB
Rust
343 lines
8.9 KiB
Rust
use std::fmt;
|
|
|
|
use crate::{BackendRuntimeListTarget, BackendRuntimeTarget, WorkerRuntimeCommand};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum TargetKind {
|
|
Local,
|
|
Backend,
|
|
}
|
|
|
|
impl fmt::Display for TargetKind {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Local => f.write_str("local"),
|
|
Self::Backend => f.write_str("Backend"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct LocalTarget;
|
|
|
|
impl LocalTarget {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
|
|
fn runtime_command(&self) -> Result<WorkerRuntimeCommand, TargetError> {
|
|
WorkerRuntimeCommand::resolve().map_err(TargetError::local_runtime_command)
|
|
}
|
|
}
|
|
|
|
impl Default for LocalTarget {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct BackendTarget {
|
|
pub base_url: String,
|
|
pub workspace_id: Option<String>,
|
|
}
|
|
|
|
impl BackendTarget {
|
|
pub fn new(base_url: impl Into<String>, workspace_id: Option<impl Into<String>>) -> Self {
|
|
Self {
|
|
base_url: base_url.into(),
|
|
workspace_id: workspace_id.map(Into::into),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct WorkerListRequest {
|
|
pub runtime_id: Option<String>,
|
|
pub include_stopped: bool,
|
|
}
|
|
|
|
impl WorkerListRequest {
|
|
pub fn new(runtime_id: Option<String>) -> Self {
|
|
Self {
|
|
runtime_id,
|
|
include_stopped: false,
|
|
}
|
|
}
|
|
|
|
pub fn with_stopped(runtime_id: Option<String>) -> Self {
|
|
Self {
|
|
runtime_id,
|
|
include_stopped: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct WorkerConnectionSelector {
|
|
pub runtime_id: String,
|
|
pub worker_id: String,
|
|
}
|
|
|
|
impl WorkerConnectionSelector {
|
|
pub fn new(runtime_id: impl Into<String>, worker_id: impl Into<String>) -> Self {
|
|
Self {
|
|
runtime_id: runtime_id.into(),
|
|
worker_id: worker_id.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct WorkerSpawn {
|
|
pub runtime_command: WorkerRuntimeCommand,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct WorkerByName {
|
|
pub runtime_command: WorkerRuntimeCommand,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct WorkerResume {
|
|
pub runtime_command: WorkerRuntimeCommand,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct Dashboard {
|
|
pub runtime_command: WorkerRuntimeCommand,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct WorkerList {
|
|
pub local_runtime_command: Option<WorkerRuntimeCommand>,
|
|
pub backend_target: Option<BackendRuntimeListTarget>,
|
|
pub include_stopped: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct WorkerConnection {
|
|
pub target: BackendRuntimeTarget,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct TargetError {
|
|
message: String,
|
|
}
|
|
|
|
impl TargetError {
|
|
fn unsupported(operation: &'static str, target: TargetKind) -> Self {
|
|
Self {
|
|
message: format!("{operation} is not supported by {target} target"),
|
|
}
|
|
}
|
|
|
|
fn local_runtime_command(error: std::io::Error) -> Self {
|
|
Self {
|
|
message: format!("failed to resolve local Worker runtime command: {error}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for TargetError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.write_str(&self.message)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for TargetError {}
|
|
|
|
pub trait Target: fmt::Debug + Send + Sync {
|
|
fn kind(&self) -> TargetKind;
|
|
|
|
fn spawn_worker(&self) -> Result<WorkerSpawn, TargetError>;
|
|
|
|
fn worker_by_name(&self) -> Result<WorkerByName, TargetError>;
|
|
|
|
fn resume_worker(&self) -> Result<WorkerResume, TargetError>;
|
|
|
|
fn dashboard(&self) -> Result<Dashboard, TargetError>;
|
|
|
|
fn list_workers(&self, request: WorkerListRequest) -> Result<WorkerList, TargetError>;
|
|
|
|
fn connect_worker(
|
|
&self,
|
|
selector: WorkerConnectionSelector,
|
|
) -> Result<WorkerConnection, TargetError>;
|
|
}
|
|
|
|
impl Target for LocalTarget {
|
|
fn kind(&self) -> TargetKind {
|
|
TargetKind::Local
|
|
}
|
|
|
|
fn spawn_worker(&self) -> Result<WorkerSpawn, TargetError> {
|
|
Ok(WorkerSpawn {
|
|
runtime_command: self.runtime_command()?,
|
|
})
|
|
}
|
|
|
|
fn worker_by_name(&self) -> Result<WorkerByName, TargetError> {
|
|
Ok(WorkerByName {
|
|
runtime_command: self.runtime_command()?,
|
|
})
|
|
}
|
|
|
|
fn resume_worker(&self) -> Result<WorkerResume, TargetError> {
|
|
Ok(WorkerResume {
|
|
runtime_command: self.runtime_command()?,
|
|
})
|
|
}
|
|
|
|
fn dashboard(&self) -> Result<Dashboard, TargetError> {
|
|
Ok(Dashboard {
|
|
runtime_command: self.runtime_command()?,
|
|
})
|
|
}
|
|
|
|
fn list_workers(&self, request: WorkerListRequest) -> Result<WorkerList, TargetError> {
|
|
if request.runtime_id.is_some() {
|
|
return Err(TargetError::unsupported(
|
|
"Explicit runtime id for local worker listing",
|
|
self.kind(),
|
|
));
|
|
}
|
|
Ok(WorkerList {
|
|
local_runtime_command: Some(self.runtime_command()?),
|
|
backend_target: None,
|
|
include_stopped: request.include_stopped,
|
|
})
|
|
}
|
|
|
|
fn connect_worker(
|
|
&self,
|
|
_selector: WorkerConnectionSelector,
|
|
) -> Result<WorkerConnection, TargetError> {
|
|
Err(TargetError::unsupported(
|
|
"Backend runtime worker connection",
|
|
self.kind(),
|
|
))
|
|
}
|
|
}
|
|
|
|
impl Target for BackendTarget {
|
|
fn kind(&self) -> TargetKind {
|
|
TargetKind::Backend
|
|
}
|
|
|
|
fn spawn_worker(&self) -> Result<WorkerSpawn, TargetError> {
|
|
Err(TargetError::unsupported("Worker spawn", self.kind()))
|
|
}
|
|
|
|
fn worker_by_name(&self) -> Result<WorkerByName, TargetError> {
|
|
Err(TargetError::unsupported(
|
|
"Worker name attachment",
|
|
self.kind(),
|
|
))
|
|
}
|
|
|
|
fn resume_worker(&self) -> Result<WorkerResume, TargetError> {
|
|
Err(TargetError::unsupported("Worker resume", self.kind()))
|
|
}
|
|
|
|
fn dashboard(&self) -> Result<Dashboard, TargetError> {
|
|
Err(TargetError::unsupported("Dashboard", self.kind()))
|
|
}
|
|
|
|
fn list_workers(&self, request: WorkerListRequest) -> Result<WorkerList, TargetError> {
|
|
Ok(WorkerList {
|
|
local_runtime_command: None,
|
|
backend_target: Some(BackendRuntimeListTarget::new(
|
|
self.base_url.clone(),
|
|
self.workspace_id.clone(),
|
|
request.runtime_id,
|
|
)),
|
|
include_stopped: request.include_stopped,
|
|
})
|
|
}
|
|
|
|
fn connect_worker(
|
|
&self,
|
|
selector: WorkerConnectionSelector,
|
|
) -> Result<WorkerConnection, TargetError> {
|
|
Ok(WorkerConnection {
|
|
target: BackendRuntimeTarget::new(
|
|
self.base_url.clone(),
|
|
selector.runtime_id,
|
|
selector.worker_id,
|
|
),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn backend_target_builds_worker_list() {
|
|
let target = BackendTarget::new("http://127.0.0.1:8787", Some("workspace-a"));
|
|
let workers = target
|
|
.list_workers(WorkerListRequest::new(Some("runtime-a".to_string())))
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
workers.backend_target.as_ref().unwrap().base_url,
|
|
"http://127.0.0.1:8787"
|
|
);
|
|
assert_eq!(
|
|
workers
|
|
.backend_target
|
|
.as_ref()
|
|
.unwrap()
|
|
.workspace_id
|
|
.as_deref(),
|
|
Some("workspace-a")
|
|
);
|
|
assert_eq!(
|
|
workers
|
|
.backend_target
|
|
.as_ref()
|
|
.unwrap()
|
|
.runtime_id
|
|
.as_deref(),
|
|
Some("runtime-a")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn backend_target_builds_worker_connection() {
|
|
let target = BackendTarget::new("http://127.0.0.1:8787", Some("workspace-a"));
|
|
let connection = target
|
|
.connect_worker(WorkerConnectionSelector::new("runtime-a", "worker-b"))
|
|
.unwrap();
|
|
|
|
assert_eq!(connection.target.base_url, "http://127.0.0.1:8787");
|
|
assert_eq!(connection.target.runtime_id, "runtime-a");
|
|
assert_eq!(connection.target.worker_id, "worker-b");
|
|
}
|
|
|
|
#[test]
|
|
fn backend_target_rejects_local_worker_operations() {
|
|
let target = BackendTarget::new("http://127.0.0.1:8787", None::<String>);
|
|
let err = target.spawn_worker().unwrap_err();
|
|
|
|
assert_eq!(
|
|
err.to_string(),
|
|
"Worker spawn is not supported by Backend target"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn local_target_builds_local_worker_list() {
|
|
let target = LocalTarget::new();
|
|
let workers = target
|
|
.list_workers(WorkerListRequest::with_stopped(None))
|
|
.unwrap();
|
|
|
|
assert!(workers.local_runtime_command.is_some());
|
|
assert!(workers.backend_target.is_none());
|
|
assert!(workers.include_stopped);
|
|
}
|
|
}
|