use crate::boundary::Action;
use crate::boundary::config::ConfigFile;
use crate::config_edit::branch::edit::EditOrigin;
use serde_json::{Map, Value, json};
use super::fields::{bool_of, opt_str_of};
use super::str_of;
use crate::model_pick::{LEVELS, Tuning};
pub(super) fn encode_file(file: &ConfigFile) -> Value {
match file {
ConfigFile::Brazen { workspace } => {
json!({ "file": "brazen", "workspace": workspace })
}
ConfigFile::LitanyModels => json!({ "file": "litany-models" }),
ConfigFile::LitanyWorkflow { name } => {
json!({ "file": "litany-workflow", "name": name })
}
ConfigFile::Cadence => json!({ "file": "cadence" }),
ConfigFile::Branch {
workspace,
lineage,
origin,
path,
} => {
let mut map = Map::new();
map.insert("file".to_owned(), json!("branch"));
map.insert("workspace".to_owned(), Value::String(workspace.clone()));
map.insert("lineage".to_owned(), json!(lineage));
map.insert("path".to_owned(), json!(path));
for (k, v) in origin_fields(origin) {
map.insert(k, v);
}
Value::Object(map)
}
}
}
pub(super) fn decode_action(op: &str, o: &Map<String, Value>) -> Option<Result<Action, String>> {
match op {
"config" => Some(decode_apply(o)),
"marks" => Some(decode_marks(o)),
"model" => Some(decode_pick(o)),
EFFORT => Some(decode_effort(o)),
PRIORITY => Some(decode_priority(o)),
_ => None,
}
}
fn decode_apply(o: &Map<String, Value>) -> Result<Action, String> {
Ok(Action::ApplyConfig {
file: decode_file(o.get("target").ok_or("config: missing target")?)?,
text: str_of(o, "text")?,
})
}
fn decode_marks(o: &Map<String, Value>) -> Result<Action, String> {
let branch = str_of(o, "branch")?;
if !crate::world::marks::lawful(&branch) {
return Err(format!("marks: {}", crate::world::marks::REFUSAL));
}
Ok(Action::SetMarks {
workspace: str_of(o, "workspace")?,
branch,
})
}
pub(super) const EFFORT: &str = "effort";
pub(super) const PRIORITY: &str = "priority";
pub(super) fn encode_tuning(tuning: &Tuning) -> Value {
match tuning {
Tuning::Effort {
workspace,
role,
level,
} => json!({ "op": EFFORT, "workspace": workspace, "role": role,
"level": level.map(|l| l.as_str()) }),
Tuning::Priority {
workspace,
role,
on,
} => json!({ "op": PRIORITY, "workspace": workspace, "role": role, "on": on }),
}
}
fn decode_effort(o: &Map<String, Value>) -> Result<Action, String> {
let level = match opt_str_of(o, "level")? {
None => None,
Some(word) => Some(
crate::model_pick::Effort::parse(&word)
.ok_or_else(|| format!("{EFFORT}: level must be one of {LEVELS}, got {word:?}"))?,
),
};
Ok(Action::Tune(Tuning::Effort {
workspace: str_of(o, "workspace")?,
role: str_of(o, "role")?,
level,
}))
}
fn decode_priority(o: &Map<String, Value>) -> Result<Action, String> {
Ok(Action::Tune(Tuning::Priority {
workspace: str_of(o, "workspace")?,
role: str_of(o, "role")?,
on: bool_of(o, "on")?,
}))
}
fn decode_pick(o: &Map<String, Value>) -> Result<Action, String> {
Ok(Action::PickModel {
workspace: str_of(o, "workspace")?,
role: str_of(o, "role")?,
provider: str_of(o, "provider")?,
model: str_of(o, "model")?,
})
}
pub(super) fn decode_file(v: &Value) -> Result<ConfigFile, String> {
let obj = v.as_object().ok_or("config: target is not an object")?;
match str_of(obj, "file")?.as_str() {
"brazen" => Ok(ConfigFile::Brazen {
workspace: str_of(obj, "workspace")?,
}),
"litany-models" => Ok(ConfigFile::LitanyModels),
"litany-workflow" => Ok(ConfigFile::LitanyWorkflow {
name: str_of(obj, "name")?,
}),
"cadence" => Ok(ConfigFile::Cadence),
"branch" => Ok(ConfigFile::Branch {
workspace: str_of(obj, "workspace")?,
lineage: str_of(obj, "lineage")?,
origin: decode_origin(obj)?,
path: str_of(obj, "path")?,
}),
other => Err(format!("config: unknown target file {other:?}")),
}
}
fn origin_fields(origin: &EditOrigin) -> Vec<(String, Value)> {
match origin {
EditOrigin::Advance => vec![("origin".to_owned(), json!("advance"))],
EditOrigin::Fork { source } => vec![
("origin".to_owned(), json!("fork")),
("source".to_owned(), json!(source)),
],
EditOrigin::Orphan => vec![("origin".to_owned(), json!("orphan"))],
}
}
fn decode_origin(obj: &Map<String, Value>) -> Result<EditOrigin, String> {
match str_of(obj, "origin")?.as_str() {
"advance" => Ok(EditOrigin::Advance),
"fork" => Ok(EditOrigin::Fork {
source: str_of(obj, "source")?,
}),
"orphan" => Ok(EditOrigin::Orphan),
other => Err(format!("config: unknown origin {other:?}")),
}
}
#[cfg(test)]
pub(crate) mod tests;