use log::debug;
pub fn ensure_project_root(ctx: &crate::runner::Context) -> anyhow::Result<()> {
if !std::path::Path::new("CMakeLists.txt").exists() {
anyhow::bail!("no CMakeLists.txt found. Run libra from the project root.");
}
debug!("CMakelists.txt found");
let has_presets = std::path::Path::new("CMakePresets.json").exists()
|| std::path::Path::new("CMakeUserPresets.json").exists();
if !has_presets && ctx.preset.is_none() {
anyhow::bail!(
"no CMakePresets.json or CMakeUserPresets.json found.\n\
libra requires CMake presets to function. Options:\n\
- Create CMakePresets.json manually\n\
- Use 'libra init' to scaffold a full preset hierarchy [Phase 3]"
);
}
debug!("CMakelists.txt and one of {{CMakePresets.json,CMakeUserPresets}} found");
Ok(())
}
pub fn resolve(ctx: &crate::runner::Context, default: Option<&str>) -> anyhow::Result<String> {
let preset = if let Some(preset) = &ctx.preset {
debug!("Preset={} resolved via --preset", preset);
preset.clone()
} else if let Some(preset) = read_preset(
"CMakeUserPresets.json",
"vendor.libra.defaultConfigurePreset",
)? {
debug!("Preset={} resolved via CMakeUserPresets.json", preset);
preset
} else if let Some(preset) =
read_preset("CMakePresets.json", "vendor.libra.defaultConfigurePreset")?
{
debug!("Preset={} resolved via CMakePresets.json", preset);
preset
} else if let Some(d) = default {
debug!("Preset resolved via default={}", default.unwrap());
d.to_string()
} else {
anyhow::bail!(
"no preset specified and no defaultConfigurePreset found.\n\
Options:\n\
- Pass --preset=<n> explicitly\n\
- Add vendor.libra.defaultConfigurePreset to CMakeUserPresets.json\n\
- Use 'libra preset default <n>' [Phase 3]"
);
};
Ok(preset)
}
pub fn read_preset(path: &str, preset: &str) -> anyhow::Result<Option<String>> {
let p = std::path::Path::new(path);
if !p.exists() {
return Ok(None);
}
let content = std::fs::read_to_string(p)?;
let value: serde_json::Value =
serde_json::from_str(&content).map_err(|e| anyhow::anyhow!("{path}: invalid JSON: {e}"))?;
let mut current = &value;
for component in preset.split(".") {
match current.get(component) {
Some(v) => current = v,
None => return Ok(None),
}
}
let result = current.as_str().map(str::to_owned);
Ok(result)
}
pub fn read_configure_preset_field(
path: &str,
preset_name: &str,
field: &str,
) -> anyhow::Result<Option<String>> {
let p = std::path::Path::new(path);
if !p.exists() {
return Ok(None);
}
let content = std::fs::read_to_string(p)?;
let value: serde_json::Value =
serde_json::from_str(&content).map_err(|e| anyhow::anyhow!("{path}: invalid JSON: {e}"))?;
let presets = match value.get("configurePresets").and_then(|a| a.as_array()) {
Some(p) => p,
None => return Ok(None),
};
let mut current = preset_name.to_string();
loop {
let found = presets
.iter()
.find(|p| p.get("name").and_then(|n| n.as_str()) == Some(¤t));
match found {
None => return Ok(None),
Some(p) => {
if let Some(val) = p.get(field).and_then(|v| v.as_str()) {
return Ok(Some(val.to_owned()));
}
match p.get("inherits").and_then(|v| v.as_str()) {
Some(parent) => current = parent.to_string(),
None => return Ok(None),
}
}
}
}
}
pub fn workflow_preset_exists(path: &str, preset_name: &str) -> anyhow::Result<bool> {
let p = std::path::Path::new(path);
if !p.exists() {
return Ok(false);
}
let content = std::fs::read_to_string(p)?;
let value: serde_json::Value =
serde_json::from_str(&content).map_err(|e| anyhow::anyhow!("{path}: invalid JSON: {e}"))?;
let exists = value
.get("workflowPresets")
.and_then(|a| a.as_array())
.map_or(false, |presets| {
presets.iter().any(|p| {
p.get("name").and_then(|n| n.as_str()) == Some(preset_name)
})
});
Ok(exists)
}