use std::path::PathBuf;
use rust_embed::RustEmbed;
use thiserror::Error;
use crate::manifest::{Manifest, ManifestError};
mod manifest;
#[derive(Debug, Error)]
pub enum StdLibError {
#[error("An error while processing manifest file: {0}")]
ManifestError(#[from] manifest::ManifestError),
#[error("An error during installation: {0}")]
InstallError(#[from] std::io::Error),
}
#[derive(RustEmbed)]
#[folder = "lib/std"]
pub struct StdLibEmbedded;
pub struct StdLib {
pub path: std::path::PathBuf,
pub manifest: manifest::Manifest,
}
impl StdLib {
pub fn new(path: impl AsRef<std::path::Path>) -> Result<Self, StdLibError> {
let path = PathBuf::from(path.as_ref());
let manifest = match manifest::Manifest::load(&path) {
Ok(manifest) => manifest,
Err(ManifestError::NotFound { path }) => Self::install(&path)?,
Err(err) => return Err(err.into()),
};
let manifest = if manifest.library.version != crate::version() {
eprintln!(
"µcad standard library version mismatch: {} != {}",
manifest.library.version,
crate::version()
);
Self::reinstall(true)?
} else {
manifest
};
Ok(Self { path, manifest })
}
pub fn reinstall(force: bool) -> Result<Manifest, StdLibError> {
let path = Self::default_path();
if force {
Self::uninstall(&path)?;
}
Self::install(path)
}
fn install(path: impl AsRef<std::path::Path>) -> Result<manifest::Manifest, StdLibError> {
let path = path.as_ref();
eprintln!(
"Installing µcad standard library {} into {:?}...",
crate::version(),
path
);
std::fs::create_dir_all(path)?;
StdLibEmbedded::iter().try_for_each(|file| {
let file_path = path.join(file.as_ref());
if let Some(parent) = file_path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(
file_path,
StdLibEmbedded::get(file.as_ref())
.expect("embedded folder 'lib/std'")
.data,
)
})?;
Manifest::default().save(path)?;
eprintln!("Successfully installed µcad standard library.");
Ok(manifest::Manifest::load(path)?)
}
fn uninstall(path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
let path = path.as_ref();
if !path.exists() {
eprintln!(
"µcad standard library not found in {:?}. Nothing to uninstall.",
path
);
return Ok(());
}
eprintln!("Removing µcad standard library from {:?}...", path);
StdLibEmbedded::iter().try_for_each(|file| {
let file_path = path.join(file.as_ref());
std::fs::remove_file(file_path)
})?;
std::fs::remove_file(Manifest::manifest_path(path))?;
std::fs::remove_dir(path)?;
eprintln!("Successfully uninstalled µcad standard library.");
Ok(())
}
pub fn default_path() -> std::path::PathBuf {
global_library_search_path().join("std")
}
}
pub fn global_library_search_path() -> std::path::PathBuf {
#[cfg(not(debug_assertions))]
return dirs::config_dir()
.expect("config directory")
.join("microcad")
.join("lib");
#[cfg(debug_assertions)]
return std::path::PathBuf::from("./crates/std/lib");
}
pub fn version() -> semver::Version {
use std::str::FromStr;
semver::Version::from_str(env!("CARGO_PKG_VERSION")).expect("Valid version")
}