use super::ConfigFile;
use crate::boundary::dispatch::Deps;
use crate::boundary::reply::{ConfigAnswer, ConfigView, Reply};
use crate::config_edit::branch::{Lineage, config_file, lineages};
use crate::config_edit::brazen::{BzOutcome, BzRunner, RealBzRunner, row_names};
use crate::config_edit::form::{self, Control, Row, schema_for};
use crate::config_edit::litany_global::LitanyGlobal;
use crate::config_edit::{RealFileIo, load_snapshot};
use crate::model_pick::query::{EMPTY_ROSTER, model_ids};
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Read {
File { file: ConfigFile },
Lineages { workspace: String },
Models { workspace: String, provider: String },
Marks { workspace: String },
Providers { workspace: String },
Proposals {
workspace: String,
id: Option<String>,
},
Roles { workspace: String },
}
impl Read {
pub(crate) fn workspace_slot(&mut self) -> Option<&mut String> {
match self {
Self::File { file } => file.workspace_slot(),
Self::Lineages { workspace }
| Self::Models { workspace, .. }
| Self::Marks { workspace }
| Self::Providers { workspace }
| Self::Proposals { workspace, .. }
| Self::Roles { workspace } => Some(workspace),
}
}
}
pub(super) fn text_at(path: &Path) -> Result<String, String> {
load_snapshot(&RealFileIo, path)
.map(|(text, _)| text)
.map_err(|e| e.to_string())
}
pub(super) fn branch_text(workspace: &Path, lineage: &str, path: &str) -> Result<String, String> {
config_file(workspace, &format!("config/{lineage}"), path)
.map(|bytes| String::from_utf8_lossy(&bytes).into_owned())
.map_err(|e| e.to_string())
}
pub(crate) fn file(deps: &Deps, ws: &Path, dest: &ConfigFile) -> Result<Reply, String> {
let text = match dest {
ConfigFile::Brazen { .. } => text_at(&super::brazen_paths(deps, ws).config)?,
ConfigFile::LitanyModels => text_at(&LitanyGlobal::resolve(&deps.world).models())?,
ConfigFile::LitanyWorkflow { name } => text_at(
&LitanyGlobal::resolve(&deps.world)
.new_workflow(name)
.map_err(|e| e.to_string())?,
)?,
ConfigFile::Cadence => text_at(&super::cadence_path(&deps.world))?,
ConfigFile::Branch { lineage, path, .. } => branch_text(ws, lineage, path)?,
};
let settings = settings(deps, ws, dest, &text);
Ok(Reply::Config(ConfigAnswer::File(ConfigView {
text,
settings,
})))
}
fn settings(deps: &Deps, ws: &Path, dest: &ConfigFile, text: &str) -> Vec<Row> {
let Some(schema) = schema_for(&dest.file_name()) else {
return Vec::new();
};
let providers = if schema.fields.iter().any(|f| f.control == Control::Provider) {
row_names(&RealBzRunner::resolve(&super::wall_env(deps, ws)).providers())
} else {
Vec::new()
};
form::read(&schema, text, &providers)
}
pub(super) fn browse(workspace: &Path) -> Result<Vec<Lineage>, String> {
lineages(workspace).map_err(|e| e.to_string())
}
pub(super) fn models(wall: &crate::xdg::Env, provider: &str) -> Result<Vec<String>, String> {
roster(&RealBzRunner::resolve(wall).list_models(provider))
}
pub(super) fn roster(out: &BzOutcome) -> Result<Vec<String>, String> {
if !out.success {
let stderr = out.stderr.trim();
if stderr.is_empty() {
return Err("bz --list-models failed".to_owned());
}
return Err(stderr.to_owned());
}
let models = model_ids(&out.stdout);
if models.is_empty() {
return Err(EMPTY_ROSTER.to_owned());
}
Ok(models)
}