use std::ffi::CStr;
use std::path::{Path, PathBuf};
use std::process::Command;
use crate::config::DocusaurusConfig;
use crate::error::DocusaurusError;
pub fn compile_config(site_dir: &Path) -> Result<PathBuf, DocusaurusError> {
let config_rs = site_dir.join("docusaurus.config.rs");
if !config_rs.exists() {
return Err(DocusaurusError::ConfigNotFound(site_dir.to_path_buf()));
}
let src_dir = site_dir.join("src");
std::fs::create_dir_all(&src_dir)?;
let lib_rs = src_dir.join("lib.rs");
let injected = !lib_rs.exists();
if injected {
std::fs::copy(&config_rs, &lib_rs)?;
}
let manifest = site_dir.join("Cargo.toml");
let output = Command::new("cargo")
.args(["build", "--lib", "--manifest-path"])
.arg(&manifest)
.output()?;
if injected {
let _ = std::fs::remove_file(&lib_rs);
}
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
return Err(DocusaurusError::CompileFailed(stderr));
}
let target_dir = site_dir.join("target").join("debug");
let dylib = find_dylib(&target_dir)?;
Ok(dylib)
}
fn find_dylib(target_dir: &Path) -> Result<PathBuf, DocusaurusError> {
let suffixes: &[&str] = if cfg!(target_os = "macos") {
&[".dylib"]
} else if cfg!(target_os = "windows") {
&[".dll"]
} else {
&[".so"]
};
let entries = std::fs::read_dir(target_dir)?;
for entry in entries.flatten() {
let path = entry.path();
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
let is_lib = name.starts_with("lib") || cfg!(target_os = "windows");
let has_suffix = suffixes.iter().any(|s| name.ends_with(s));
if is_lib && has_suffix {
return Ok(path);
}
}
}
Err(DocusaurusError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("no dylib found in {}", target_dir.display()),
)))
}
pub fn load_config(dylib_path: &Path) -> Result<DocusaurusConfig, DocusaurusError> {
unsafe {
let lib = libloading::Library::new(dylib_path)?;
let sym: libloading::Symbol<unsafe extern "C" fn() -> *mut std::os::raw::c_char> =
lib.get(b"config\0")?;
let raw = sym();
if raw.is_null() {
return Err(DocusaurusError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"config() returned null pointer",
)));
}
let cstr = CStr::from_ptr(raw);
let json = cstr.to_string_lossy();
let cfg: DocusaurusConfig = serde_json::from_str(&json)?;
drop(std::ffi::CString::from_raw(raw));
Ok(cfg)
}
}