auv-api-server 0.0.22

Protocol adapters for AUV daemon control and capability APIs
//! Conversion between transport protobufs and the shared AUV domain model.

use std::str::FromStr as _;

use auv_api_proto::auv::api::daemon::v1 as proto;

use crate::control::ControlError;

pub(crate) fn create_run(value: proto::CreateRunRequest) -> Result<auv::runs::CreateRun, ControlError> {
  Ok(auv::runs::CreateRun {
    devices: value
      .devices
      .into_iter()
      .map(|device| auv::resource::DeviceId::from_str(&device.device_id).map_err(|_| ControlError::InvalidArgument("invalid Device ID")))
      .collect::<Result<_, _>>()?,
    labels: value.labels,
  })
}

pub(crate) fn create_runner(value: proto::CreateRunnerRequest) -> Result<auv::runners::CreateRunner, ControlError> {
  let lifecycle = match proto::RunnerLifecycle::try_from(value.lifecycle).unwrap_or_default() {
    proto::RunnerLifecycle::Ephemeral => auv::runners::RunnerLifecycle::Ephemeral,
    proto::RunnerLifecycle::UnlessIdle => auv::runners::RunnerLifecycle::UnlessIdle,
    proto::RunnerLifecycle::UnlessShutdown => auv::runners::RunnerLifecycle::UnlessShutdown,
    proto::RunnerLifecycle::Unspecified => return Err(ControlError::InvalidArgument("Runner lifecycle is required")),
  };
  Ok(auv::runners::CreateRunner {
    device: value
      .device
      .map(|device| auv::resource::DeviceId::from_str(&device.device_id).map_err(|_| ControlError::InvalidArgument("invalid Device ID")))
      .transpose()?,
    class: auv::resource::RunnerClassId::from_str(
      &value.runner_class.ok_or(ControlError::InvalidArgument("runner_class is required"))?.runner_class,
    )
    .map_err(|_| ControlError::InvalidArgument("invalid RunnerClass ID"))?,
    labels: value.labels,
    lifecycle,
    idle_timeout: value.idle_timeout.map(duration_from_proto).transpose()?,
  })
}

pub(crate) fn run_outcome(value: proto::RunOutcome) -> Result<auv::runs::RunOutcome, ControlError> {
  match value {
    proto::RunOutcome::Succeeded => Ok(auv::runs::RunOutcome::Succeeded),
    proto::RunOutcome::Failed => Ok(auv::runs::RunOutcome::Failed),
    proto::RunOutcome::Canceled => Ok(auv::runs::RunOutcome::Canceled),
    proto::RunOutcome::Unspecified => Err(ControlError::InvalidArgument("Run outcome is required")),
  }
}

pub(crate) fn duration_from_proto(value: prost_types::Duration) -> Result<std::time::Duration, ControlError> {
  if value.seconds < 0 || value.nanos < 0 || value.nanos >= 1_000_000_000 {
    return Err(ControlError::InvalidArgument("duration is outside its valid range"));
  }
  Ok(std::time::Duration::new(value.seconds as u64, value.nanos as u32))
}

fn timestamp(value: auv::time::Timestamp) -> prost_types::Timestamp {
  prost_types::Timestamp {
    seconds: value.seconds,
    nanos: value.nanos,
  }
}

pub(crate) fn device(value: auv::devices::Device) -> proto::Device {
  proto::Device {
    r#ref: Some(proto::DeviceRef {
      device_id: value.id.to_string(),
    }),
    name: value.name,
    platform: match value.platform {
      auv::devices::DevicePlatform::Unspecified => proto::DevicePlatform::Unspecified,
      auv::devices::DevicePlatform::Linux => proto::DevicePlatform::Linux,
      auv::devices::DevicePlatform::Macos => proto::DevicePlatform::Macos,
      auv::devices::DevicePlatform::Windows => proto::DevicePlatform::Windows,
    } as i32,
    local: value.local,
    labels: value.labels,
  }
}

pub(crate) fn run(value: auv::runs::Run) -> proto::Run {
  proto::Run {
    r#ref: Some(proto::RunRef {
      run_id: value.id.to_string(),
    }),
    phase: match value.phase {
      auv::runs::RunPhase::Unspecified => proto::RunPhase::Unspecified,
      auv::runs::RunPhase::Pending => proto::RunPhase::Pending,
      auv::runs::RunPhase::Running => proto::RunPhase::Running,
      auv::runs::RunPhase::Succeeded => proto::RunPhase::Succeeded,
      auv::runs::RunPhase::Failed => proto::RunPhase::Failed,
      auv::runs::RunPhase::Canceled => proto::RunPhase::Canceled,
    } as i32,
    devices: value
      .devices
      .into_iter()
      .map(|id| proto::DeviceRef {
        device_id: id.to_string(),
      })
      .collect(),
    labels: value.labels,
    created_at: value.created_at.map(timestamp),
    completed_at: value.completed_at.map(timestamp),
  }
}

fn lifecycle(value: auv::runners::RunnerLifecycle) -> proto::RunnerLifecycle {
  match value {
    auv::runners::RunnerLifecycle::Ephemeral => proto::RunnerLifecycle::Ephemeral,
    auv::runners::RunnerLifecycle::UnlessIdle => proto::RunnerLifecycle::UnlessIdle,
    auv::runners::RunnerLifecycle::UnlessShutdown => proto::RunnerLifecycle::UnlessShutdown,
  }
}

pub(crate) fn runner(value: auv::runners::Runner) -> proto::Runner {
  proto::Runner {
    r#ref: Some(proto::RunnerRef {
      runner_id: value.id.to_string(),
    }),
    device: Some(proto::DeviceRef {
      device_id: value.device.to_string(),
    }),
    runner_class: Some(proto::RunnerClassRef {
      runner_class: value.class.to_string(),
    }),
    labels: value.labels,
    lifecycle: lifecycle(value.lifecycle) as i32,
    idle_timeout: value.idle_timeout.map(|value| prost_types::Duration {
      seconds: value.as_secs() as i64,
      nanos: value.subsec_nanos() as i32,
    }),
    phase: match value.phase {
      auv::runners::RunnerPhase::Unspecified => proto::RunnerPhase::Unspecified,
      auv::runners::RunnerPhase::Starting => proto::RunnerPhase::Starting,
      auv::runners::RunnerPhase::Ready => proto::RunnerPhase::Ready,
      auv::runners::RunnerPhase::Draining => proto::RunnerPhase::Draining,
      auv::runners::RunnerPhase::Stopped => proto::RunnerPhase::Stopped,
      auv::runners::RunnerPhase::Failed => proto::RunnerPhase::Failed,
    } as i32,
    created_at: value.created_at.map(timestamp),
    process_id: value.process_id.unwrap_or_default(),
    active_operations: value.active_operations,
    idle_deadline: value.idle_deadline.map(timestamp),
  }
}

pub(crate) fn runner_class(value: auv::runners::RunnerClass) -> proto::RunnerClass {
  proto::RunnerClass {
    r#ref: Some(proto::RunnerClassRef {
      runner_class: value.id.to_string(),
    }),
    device: value.device.map(|id| proto::DeviceRef {
      device_id: id.to_string(),
    }),
    display_name: value.display_name,
    supported_lifecycles: value.supported_lifecycles.into_iter().map(|value| lifecycle(value) as i32).collect(),
    available: value.available,
  }
}