1use std::path::PathBuf;
7
8use rust_embed::RustEmbed;
9use thiserror::Error;
10
11use crate::manifest::{Manifest, ManifestError};
12
13mod manifest;
14
15#[derive(Debug, Error)]
17pub enum StdLibError {
18 #[error("An error while processing manifest file: {0}")]
20 ManifestError(#[from] manifest::ManifestError),
21
22 #[error("An error during installation: {0}")]
24 InstallError(#[from] std::io::Error),
25}
26
27#[derive(RustEmbed)]
29#[folder = "lib/std"]
30pub struct StdLibEmbedded;
31
32pub struct StdLib {
34 pub path: std::path::PathBuf,
36 pub manifest: manifest::Manifest,
38}
39
40impl StdLib {
41 pub fn new(path: impl AsRef<std::path::Path>) -> Result<Self, StdLibError> {
45 let path = PathBuf::from(path.as_ref());
46
47 let manifest = match manifest::Manifest::load(&path) {
48 Ok(manifest) => manifest,
49 Err(ManifestError::NotFound { path }) => Self::install(&path)?,
51 Err(err) => return Err(err.into()),
52 };
53
54 let manifest = if manifest.library.version != crate::version() {
55 eprintln!(
56 "µcad standard library version mismatch: {} != {}",
57 manifest.library.version,
58 crate::version()
59 );
60
61 Self::reinstall(true)?
63 } else {
64 manifest
65 };
66
67 Ok(Self { path, manifest })
68 }
69
70 pub fn reinstall(force: bool) -> Result<Manifest, StdLibError> {
72 let path = Self::default_path();
73 if force {
74 Self::uninstall(&path)?;
75 }
76
77 Self::install(path)
78 }
79
80 fn install(path: impl AsRef<std::path::Path>) -> Result<manifest::Manifest, StdLibError> {
82 let path = path.as_ref();
83 eprintln!(
84 "Installing µcad standard library {} into {:?}...",
85 crate::version(),
86 path
87 );
88
89 std::fs::create_dir_all(path)?;
90
91 StdLibEmbedded::iter().try_for_each(|file| {
93 let file_path = path.join(file.as_ref());
94 if let Some(parent) = file_path.parent() {
95 std::fs::create_dir_all(parent)?;
96 }
97 std::fs::write(
98 file_path,
99 StdLibEmbedded::get(file.as_ref())
100 .expect("embedded folder 'lib/std'")
101 .data,
102 )
103 })?;
104
105 Manifest::default().save(path)?;
107
108 eprintln!("Successfully installed µcad standard library.");
109
110 Ok(manifest::Manifest::load(path)?)
111 }
112
113 fn uninstall(path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
115 let path = path.as_ref();
116
117 if !path.exists() {
118 eprintln!(
119 "µcad standard library not found in {:?}. Nothing to uninstall.",
120 path
121 );
122 return Ok(());
123 }
124
125 eprintln!("Removing µcad standard library from {:?}...", path);
126
127 StdLibEmbedded::iter().try_for_each(|file| {
129 let file_path = path.join(file.as_ref());
130 std::fs::remove_file(file_path)
131 })?;
132
133 std::fs::remove_file(Manifest::manifest_path(path))?;
134
135 std::fs::remove_dir(path)?;
137
138 eprintln!("Successfully uninstalled µcad standard library.");
139
140 Ok(())
141 }
142
143 pub fn default_path() -> std::path::PathBuf {
145 global_library_search_path().join("std")
146 }
147}
148
149pub fn global_library_search_path() -> std::path::PathBuf {
154 #[cfg(not(debug_assertions))]
155 return dirs::config_dir()
156 .expect("config directory")
157 .join("microcad")
158 .join("lib");
159
160 #[cfg(debug_assertions)]
161 return std::path::PathBuf::from("./crates/std/lib");
162}
163
164pub fn version() -> semver::Version {
166 use std::str::FromStr;
167 semver::Version::from_str(env!("CARGO_PKG_VERSION")).expect("Valid version")
168}