#![forbid(unsafe_code)]
use std::env;
use std::ffi::OsString;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process;
const EMBEDDED: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/embedded_binary"));
const PLACEHOLDER_PREAMBLE: &[u8] = b"CG_STUB_PLACEHOLDER\n";
const BIN_NAME: &str = "code_graph";
fn main() {
if EMBEDDED.starts_with(PLACEHOLDER_PREAMBLE) {
eprintln!(
"error: this `{BIN_NAME}` build is a development placeholder.\n\
\n\
Install a real release via one of:\n \
cargo install renso-code-graph renso-code-graph-mcp\n \
curl -fsSL https://cg.renso.ai/install.sh | sh\n \
brew tap renso-ai/code-graph https://github.com/renso-ai/code-graph-homebrew && brew install renso-ai/code-graph/code-graph\n \
npm install -g @rensoai/code-graph\n \
pipx install rensoai-code-graph"
);
process::exit(126);
}
let cache_dir = resolve_cache_dir().unwrap_or_else(|err| {
eprintln!("error: cannot resolve cache directory: {err}");
process::exit(127);
});
let bin_path = cache_dir.join(bin_file_name());
if !bin_path.exists() {
if let Err(err) = extract(&cache_dir, &bin_path) {
eprintln!(
"error: failed to extract {BIN_NAME} to {}: {err}",
bin_path.display()
);
process::exit(127);
}
}
let args: Vec<OsString> = env::args_os().skip(1).collect();
exec(&bin_path, &args);
}
fn bin_file_name() -> &'static str {
if cfg!(windows) {
"code_graph.exe"
} else {
"code_graph"
}
}
fn resolve_cache_dir() -> io::Result<PathBuf> {
let version = env!("CARGO_PKG_VERSION");
#[cfg(windows)]
let base = env::var_os("LOCALAPPDATA")
.map(PathBuf::from)
.or_else(|| {
env::var_os("USERPROFILE").map(|p| PathBuf::from(p).join("AppData").join("Local"))
})
.ok_or_else(|| io::Error::other("LOCALAPPDATA and USERPROFILE both unset"))?;
#[cfg(not(windows))]
let base = env::var_os("XDG_CACHE_HOME")
.map(PathBuf::from)
.or_else(|| env::var_os("HOME").map(|p| PathBuf::from(p).join(".cache")))
.ok_or_else(|| io::Error::other("XDG_CACHE_HOME and HOME both unset"))?;
Ok(base.join("code-graph").join(version))
}
fn extract(cache_dir: &Path, bin_path: &Path) -> io::Result<()> {
fs::create_dir_all(cache_dir)?;
let tmp_path = bin_path.with_extension("partial");
fs::write(&tmp_path, EMBEDDED)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&tmp_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&tmp_path, perms)?;
}
fs::rename(&tmp_path, bin_path)?;
Ok(())
}
#[cfg(unix)]
fn exec(bin_path: &Path, args: &[OsString]) -> ! {
use std::os::unix::process::CommandExt;
let err = process::Command::new(bin_path).args(args).exec();
eprintln!("error: exec {} failed: {err}", bin_path.display());
process::exit(127);
}
#[cfg(windows)]
fn exec(bin_path: &Path, args: &[OsString]) -> ! {
let status = match process::Command::new(bin_path).args(args).status() {
Ok(s) => s,
Err(err) => {
eprintln!("error: spawn {} failed: {err}", bin_path.display());
process::exit(127);
}
};
process::exit(status.code().unwrap_or(127));
}