use crate::config::{BuildSection, DependencySpec, ManifestConfig, ProjectSection};
use crate::error::{BuildError, Result};
use crate::toolchain::BuildOutput;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
const MAIN_C_TEMPLATE: &str =
"#include <stdio.h>\n\nint main() {\n printf(\"Hello, World!\\n\");\n return 0;\n}\n";
pub struct Project {
pub root: PathBuf,
pub config: ManifestConfig,
pub src_dir: PathBuf,
pub build_dir: PathBuf,
pub install_dir: PathBuf,
pub resolved_deps: Vec<(String, BuildOutput)>,
}
impl Project {
pub fn load(project_dir: &Path) -> Result<Self> {
let config = ManifestConfig::load(project_dir)?;
Ok(Self {
root: project_dir.to_path_buf(),
src_dir: project_dir.join("src"),
build_dir: project_dir.join("target"),
install_dir: project_dir.join("target/deps"),
config,
resolved_deps: Vec::new(),
})
}
pub fn init(name: &str) -> Result<()> {
if name.is_empty()
|| name.contains('/')
|| name.contains('\\')
|| name == "."
|| name == ".."
{
return Err(BuildError::InvalidProjectName(name.to_string()));
}
let root = PathBuf::from(name);
if root.exists() {
return Err(BuildError::ProjectAlreadyExists(root));
}
std::fs::create_dir_all(root.join("src"))?;
std::fs::create_dir_all(root.join("include"))?;
std::fs::write(root.join("src/main.c"), MAIN_C_TEMPLATE)?;
let config = ManifestConfig {
project: ProjectSection {
name: name.to_string(),
version: "0.1.0".to_string(),
authors: Vec::new(),
authors_email: Vec::new(),
c_standard: None,
},
build: BuildSection {
compiler: Default::default(),
warnings: Default::default(),
cflags: Vec::new(),
},
dependencies: BTreeMap::new(),
};
std::fs::write(root.join("smidr.toml"), config.to_toml_string()?)?;
std::fs::write(root.join(".gitignore"), "target/\ncompile_commands.json\n")?;
println!("Smidr project created: {}", name);
Ok(())
}
pub fn source_files(&self) -> Result<Vec<PathBuf>> {
let mut sources = Vec::new();
let entries = std::fs::read_dir(&self.src_dir).map_err(BuildError::Io)?;
for entry in entries {
let entry = entry.map_err(BuildError::Io)?;
let path = entry.path();
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("c") {
sources.push(path);
}
}
if sources.is_empty() {
return Err(BuildError::NoSourceFiles);
}
Ok(sources)
}
pub fn dep_prefix(&self, dep_name: &str) -> PathBuf {
self.install_dir.join(dep_name)
}
}