use crate::{CuaError, Result};
use cua_sandbox_core::placement;
use cua_sandbox_core::settings::{self, Entry, Settings};
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Record)]
pub struct ConfigEntry {
pub key: String,
pub value: String,
pub source: String,
pub from: String,
pub env: String,
pub default_value: String,
pub description: String,
}
impl From<Entry> for ConfigEntry {
fn from(e: Entry) -> Self {
Self {
key: e.key.name.into(),
value: e.value,
source: e.source.kind().into(),
from: e.source.to_string(),
env: e.key.env.into(),
default_value: e.key.default.into(),
description: e.key.description.into(),
}
}
}
fn err(e: settings::SettingsError) -> CuaError {
CuaError::InvalidArgument(e.0)
}
fn load() -> Result<Settings> {
Settings::load().map_err(err)
}
#[uniffi::export]
pub fn config_list() -> Result<Vec<ConfigEntry>> {
Ok(load()?.list().into_iter().map(Into::into).collect())
}
#[uniffi::export]
pub fn config_get(key: String) -> Result<ConfigEntry> {
let k = settings::key(&key).map_err(err)?;
Ok(load()?.entry(k).into())
}
#[uniffi::export]
pub fn config_set(key: String, value: String) -> Result<ConfigEntry> {
let k = settings::key(&key).map_err(err)?;
let mut s = load()?;
s.set(k, &value).map_err(err)?;
Ok(s.entry(k).into())
}
#[uniffi::export]
pub fn config_unset(key: String) -> Result<ConfigEntry> {
let k = settings::key(&key).map_err(err)?;
let mut s = load()?;
s.unset(k).map_err(err)?;
Ok(s.entry(k).into())
}
#[uniffi::export]
pub fn config_path() -> String {
settings::config_path().display().to_string()
}
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Record)]
pub struct LocationInfo {
pub name: String,
pub description: String,
pub kinds: Vec<String>,
pub runtimes: std::collections::HashMap<String, Vec<String>>,
}
#[uniffi::export]
pub fn locations() -> Vec<LocationInfo> {
placement::locations()
.into_iter()
.map(|c| LocationInfo {
kinds: c.kinds().iter().map(|k| k.to_string()).collect(),
runtimes: c
.kinds
.iter()
.map(|k| {
(
k.kind.to_string(),
k.runtimes.iter().map(|r| r.to_string()).collect(),
)
})
.collect(),
name: c.name,
description: c.description,
})
.collect()
}
#[uniffi::export]
pub fn check_placement(on: String, kind: String, runtime: String) -> Result<()> {
let place = |e: placement::PlacementError| CuaError::InvalidPlacement(e.message);
let on = match on.trim() {
"" => load()?.default_on().map_err(err)?.0,
o => placement::On::parse(o).map_err(place)?,
};
let kind = placement::Kind::parse(&kind).map_err(place)?;
let runtime = placement::Runtime::parse(&runtime).map_err(place)?;
placement::validate(&on, kind, &runtime).map_err(place)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn set_get_unset_and_sources() {
let env = super::super::test_env::EnvGuard::isolated();
assert_eq!(config_get("default.on".into()).unwrap().value, "local");
let e = config_set("default.on".into(), "aws".into()).unwrap();
assert_eq!((e.value.as_str(), e.source.as_str()), ("aws", "config"));
let e = config_set("default.on".into(), "cloud".into()).unwrap_err();
assert!(e.to_string().contains("Cua Cloud has ended"), "{e}");
env.set("CUA_DEFAULT_ON", "local");
let e = config_get("default.on".into()).unwrap();
assert_eq!((e.value.as_str(), e.source.as_str()), ("local", "env"));
assert_eq!(e.env, "CUA_DEFAULT_ON");
let e = config_unset("default.on".into()).unwrap();
assert_eq!(e.source, "env");
assert!(matches!(
config_set("default.on".into(), "qemu".into()),
Err(CuaError::InvalidArgument(_))
));
assert!(
config_list()
.unwrap()
.iter()
.any(|e| e.key == "runtime.linux")
);
assert!(config_path().ends_with("config.toml"));
}
#[test]
fn locations_and_placement_checks() {
let _env = super::super::test_env::EnvGuard::isolated();
let l = locations();
let local = l.iter().find(|l| l.name == "local").unwrap();
assert_eq!(local.runtimes["container"], ["gvisor", "runc"]);
assert!(!l.iter().any(|l| l.name == "cloud"));
check_placement("".into(), "".into(), "".into()).unwrap();
}
}