use crate::config::{BuildSection, CStandard, ManifestConfig, ProjectSection, ProjectType};
use crate::error::{BuildError, Result};
use crate::toolchain::BuildOutput;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
const MAIN_C_TEMPLATE: &str = include_str!("../templates/main.c");
const GITIGNORE_TEMPLATE: &str = include_str!("../templates/.gitignore");
const LIB_C_TEMPLATE: &str = include_str!("../templates/lib.c");
const LIB_H_TEMPLATE: &str = include_str!("../templates/lib.h");
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,
project_type: ProjectType,
c_standard: Option<CStandard>,
) -> 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"))?;
match project_type {
ProjectType::Binary => {
std::fs::write(root.join("src/main.c"), MAIN_C_TEMPLATE)?;
}
ProjectType::StaticLibrary | ProjectType::SharedLibrary => {
std::fs::write(root.join("src/lib.c"), LIB_C_TEMPLATE)?;
std::fs::write(root.join("include/lib.h"), LIB_H_TEMPLATE)?;
}
}
let config = ManifestConfig {
project: ProjectSection {
name: name.to_string(),
version: "0.1.0".to_string(),
authors: Vec::new(),
authors_email: Vec::new(),
description: None,
license: None,
project_type,
c_standard: c_standard.unwrap_or_default(),
},
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"), GITIGNORE_TEMPLATE)?;
println!("Created project: {}", 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)
}
}