use anyhow::{Context, Result};
use std::{
fs,
path::{Path, PathBuf},
};
use super::super::MigrationContext;
pub fn config_has_key(ctx: &MigrationContext, key: &str) -> bool {
if let Ok(true) = config_file_has_key(&ctx.project_config_path, key) {
return true;
}
if let Some(global_path) = &ctx.global_config_path
&& let Ok(true) = config_file_has_key(global_path, key)
{
return true;
}
false
}
pub(crate) fn project_migration_config_disk_paths(repo_root: &Path) -> Vec<PathBuf> {
let cueloop = repo_root.join(".cueloop");
let jsonc = cueloop.join("config.jsonc");
let json = cueloop.join("config.json");
let mut paths = Vec::new();
if jsonc.exists() {
paths.push(jsonc);
}
if json.exists() {
paths.push(json);
}
paths
}
pub fn config_has_legacy_cursor_bin(ctx: &MigrationContext) -> bool {
for path in project_migration_config_disk_paths(&ctx.repo_root) {
if let Ok(true) = config_file_has_legacy_cursor_bin(path.as_path()) {
return true;
}
}
if let Some(global_path) = &ctx.global_config_path
&& let Ok(true) = config_file_has_legacy_cursor_bin(global_path)
{
return true;
}
false
}
pub(super) fn config_file_has_key(path: &Path, key: &str) -> Result<bool> {
if !path.exists() {
return Ok(false);
}
let raw =
fs::read_to_string(path).with_context(|| format!("read config file {}", path.display()))?;
let value =
match jsonc_parser::parse_to_serde_value::<serde_json::Value>(&raw, &Default::default()) {
Ok(v) => v,
Err(_) => return Ok(false),
};
let parts: Vec<&str> = key.split('.').collect();
let mut current = &value;
for part in &parts {
match current {
serde_json::Value::Object(map) => match map.get(*part) {
Some(v) => current = v,
None => return Ok(false),
},
_ => return Ok(false),
}
}
Ok(true)
}
fn config_file_has_legacy_cursor_bin(path: &Path) -> Result<bool> {
if !path.exists() {
return Ok(false);
}
let raw =
fs::read_to_string(path).with_context(|| format!("read config file {}", path.display()))?;
let value =
match jsonc_parser::parse_to_serde_value::<serde_json::Value>(&raw, &Default::default()) {
Ok(v) => v,
Err(_) => return Ok(false),
};
if value
.get("agent")
.and_then(serde_json::Value::as_object)
.is_some_and(|agent| agent.contains_key("cursor_bin"))
{
return Ok(true);
}
let has_profile_cursor_bin = value
.get("profiles")
.and_then(serde_json::Value::as_object)
.is_some_and(|profiles| {
profiles
.values()
.filter_map(serde_json::Value::as_object)
.any(|profile| profile.contains_key("cursor_bin"))
});
Ok(has_profile_cursor_bin)
}
pub fn get_config_value(ctx: &MigrationContext, key: &str) -> Option<serde_json::Value> {
let parts: Vec<&str> = key.split('.').collect();
let config_json = match serde_json::to_value(&ctx.resolved_config) {
Ok(v) => v,
Err(_) => return None,
};
let mut current = &config_json;
for part in &parts {
match current {
serde_json::Value::Object(map) => {
current = map.get(*part)?;
}
_ => return None,
}
}
Some(current.clone())
}