#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RootSelection {
pub device_name: Option<String>,
pub device_id: Option<String>,
pub run_id: Option<String>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ResolvedSelection {
pub device: Option<crate::devices::Device>,
pub run: Option<crate::runs::Run>,
}
#[derive(Debug, thiserror::Error)]
pub enum SelectionError {
#[error(transparent)]
Device(#[from] crate::devices::DeviceError),
#[error(transparent)]
Run(#[from] crate::runs::RunError),
#[error("--device and --device-id select different Devices ({by_name:?} and {by_id:?})")]
DeviceConflict {
by_name: String,
by_id: String,
},
}
#[derive(Debug, thiserror::Error)]
pub enum SelectedClientError {
#[error(transparent)]
Context(#[from] crate::ContextError),
#[error(transparent)]
Placement(#[from] crate::client::PlacementError),
#[error(transparent)]
Selection(#[from] SelectionError),
}
impl RootSelection {
pub fn is_empty(&self) -> bool {
self.device_name.is_none() && self.device_id.is_none() && self.run_id.is_none()
}
pub fn context(&self, endpoint: Option<&str>) -> crate::AuvContext {
crate::AuvContext {
device_id: self.device_id.clone(),
device_name: self.device_name.clone(),
run_id: self.run_id.clone(),
daemon_endpoint: endpoint.map(str::to_string),
..crate::AuvContext::default()
}
}
pub fn device_selector(&self) -> Result<Option<crate::resource::DeviceSelector>, crate::resource::IdentityError> {
match (&self.device_id, &self.device_name) {
(Some(id), Some(name)) => Ok(Some(crate::resource::DeviceSelector::parse_id(id)?.with_name(name.clone()))),
(Some(id), None) => Ok(Some(crate::resource::DeviceSelector::parse_id(id)?)),
(None, Some(name)) => Ok(Some(crate::resource::DeviceSelector::by_name(name.clone()))),
(None, None) => Ok(None),
}
}
}
impl crate::Client {
pub async fn selected(
endpoint: Option<&str>,
selection: &RootSelection,
) -> Result<Option<(Self, ResolvedSelection)>, SelectedClientError> {
let client = if selection.is_empty() {
let Some(client) = Self::discover(endpoint).await? else {
return Ok(None);
};
client
} else {
Self::from_context(selection.context(endpoint)).await?
};
let resolved = client.resolve(selection).await?;
Ok(Some((client, resolved)))
}
pub async fn resolve(&self, selection: &RootSelection) -> Result<ResolvedSelection, SelectionError> {
let by_id = match selection.device_id.as_deref() {
Some(value) => {
let selector = crate::resource::DeviceSelector::parse_id(value).map_err(crate::devices::DeviceError::from)?;
Some(self.devices().get(&selector).await?)
}
None => None,
};
let by_name = match selection.device_name.as_deref() {
Some(value) => Some(self.devices().get(&crate::resource::DeviceSelector::by_name(value)).await?),
None => None,
};
if let (Some(by_id), Some(by_name)) = (&by_id, &by_name)
&& by_id.id != by_name.id
{
return Err(SelectionError::DeviceConflict {
by_name: by_name.id.to_string(),
by_id: by_id.id.to_string(),
});
}
let run = match selection.run_id.as_deref() {
Some(value) => {
let selector = crate::resource::RunSelector::parse(value).map_err(crate::runs::RunError::from)?;
Some(self.runs().get(&selector).await?)
}
None => None,
};
let mut device = by_id.or(by_name);
if device.is_none()
&& let Some(run) = &run
&& let [device_id] = run.devices.as_slice()
{
device = Some(self.devices().get(&crate::resource::DeviceSelector::by_id(device_id.as_str())).await?);
}
if let (Some(run), Some(device)) = (&run, &device) {
run.validate_device(Some(&device.id))?;
}
Ok(ResolvedSelection { device, run })
}
}