2026-03-22 11:16:14 -05:00
|
|
|
use std::{
|
|
|
|
|
env::set_current_dir,
|
|
|
|
|
path::{Path, PathBuf},
|
2026-03-22 18:04:40 -05:00
|
|
|
process::Command,
|
2026-03-22 11:16:14 -05:00
|
|
|
};
|
|
|
|
|
|
2026-03-22 18:04:40 -05:00
|
|
|
use glob::glob;
|
|
|
|
|
|
2026-03-22 11:16:14 -05:00
|
|
|
const MAIN_C: &str = include_str!("templates/main.c");
|
|
|
|
|
|
|
|
|
|
#[derive(clap::Parser)]
|
|
|
|
|
pub struct App {
|
|
|
|
|
#[clap(subcommand)]
|
|
|
|
|
command: Subcommand,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(clap::Subcommand)]
|
|
|
|
|
enum Subcommand {
|
|
|
|
|
/// Create a new C project
|
|
|
|
|
New {
|
|
|
|
|
/// The name of the new project
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
|
|
|
|
/// Initialize a new C project
|
|
|
|
|
Init,
|
|
|
|
|
/// Run the local project
|
|
|
|
|
Run {
|
|
|
|
|
/// The build mode to use
|
|
|
|
|
mode: Option<String>,
|
2026-03-22 18:04:40 -05:00
|
|
|
/// Arguments to pass to the project binary
|
|
|
|
|
args: Option<Vec<String>>,
|
2026-03-22 11:16:14 -05:00
|
|
|
},
|
|
|
|
|
/// Build the local project
|
|
|
|
|
Build {
|
|
|
|
|
/// The build mode to use
|
|
|
|
|
mode: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl App {
|
|
|
|
|
pub fn run(self) {
|
|
|
|
|
match self.command {
|
|
|
|
|
Subcommand::New { name } => match create_project(&name) {
|
|
|
|
|
Ok(_) => println!("Successfully created new project '{name}'."),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Error creating project '{name}': {e}");
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Subcommand::Init => match create_project(".") {
|
|
|
|
|
Ok(_) => println!("Successfully initialized new project."),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Error initializing project: {e}");
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-03-22 18:04:40 -05:00
|
|
|
Subcommand::Run { mode, args } => {
|
|
|
|
|
match build(&mode) {
|
|
|
|
|
Ok(_) => {
|
|
|
|
|
println!("Successfully built project.");
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Error building project: {e}");
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if let Err(e) = run(&mode, args) {
|
|
|
|
|
eprintln!("Error running project: {e}");
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Subcommand::Build { mode } => match build(&mode) {
|
|
|
|
|
Ok(_) => {
|
|
|
|
|
println!("Successfully built project.");
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Error building project: {e}");
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-03-22 11:16:14 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn create_project<P: AsRef<Path>>(directory: P) -> std::io::Result<()> {
|
|
|
|
|
let pathdir = directory.as_ref();
|
|
|
|
|
|
|
|
|
|
if pathdir.exists() && pathdir.to_string_lossy() != "." {
|
|
|
|
|
return Err(std::io::Error::new(
|
|
|
|
|
std::io::ErrorKind::AlreadyExists,
|
|
|
|
|
"specified directory already exists",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !pathdir.exists() && pathdir.to_string_lossy() != "." {
|
|
|
|
|
std::fs::create_dir(pathdir)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_current_dir(pathdir)?;
|
|
|
|
|
|
|
|
|
|
std::fs::create_dir("src")?;
|
|
|
|
|
|
|
|
|
|
std::fs::write("src/main.c", MAIN_C)?;
|
|
|
|
|
|
|
|
|
|
let config = crate::config::Config::new(&pathdir.to_string_lossy());
|
|
|
|
|
|
|
|
|
|
let serial = toml::to_string_pretty(&config).expect("a valid TOML structure");
|
|
|
|
|
|
|
|
|
|
std::fs::write("Pallet.toml", &serial)?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_config() -> Option<crate::config::Config> {
|
|
|
|
|
let p = PathBuf::from("Pallet.toml");
|
|
|
|
|
if !p.exists() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
let raw = std::fs::read_to_string(&p).expect("A valid config file");
|
|
|
|
|
|
|
|
|
|
toml::from_str(&raw).ok()
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 18:04:40 -05:00
|
|
|
fn build(mode: &Option<String>) -> std::io::Result<()> {
|
2026-03-22 11:16:14 -05:00
|
|
|
let conf = match get_config() {
|
|
|
|
|
Some(conf) => conf,
|
|
|
|
|
None => {
|
|
|
|
|
eprintln!("Error opening config file. No Pallet.toml in current directory.");
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let build_config = conf.get_or_default(mode).ok_or(std::io::Error::new(
|
|
|
|
|
std::io::ErrorKind::NotFound,
|
|
|
|
|
"build layout not found",
|
|
|
|
|
))?;
|
|
|
|
|
|
2026-03-22 18:04:40 -05:00
|
|
|
std::fs::create_dir_all(format!("target/{}", build_config.name))?;
|
|
|
|
|
|
|
|
|
|
let mut command = Command::new("gcc");
|
|
|
|
|
|
|
|
|
|
for arg in &build_config.args {
|
|
|
|
|
command.arg(arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for entry in glob("src/*.c")
|
|
|
|
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, format!("{e}").as_str()))?
|
|
|
|
|
{
|
|
|
|
|
if let Ok(path) = entry {
|
|
|
|
|
command.arg(path.to_string_lossy().to_string());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
command
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg(format!("target/{}/{}", build_config.name, conf.name));
|
|
|
|
|
|
|
|
|
|
let mut child = command.spawn()?;
|
|
|
|
|
|
|
|
|
|
child.wait()?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn run(mode: &Option<String>, args: Option<Vec<String>>) -> std::io::Result<()> {
|
|
|
|
|
let conf = match get_config() {
|
|
|
|
|
Some(conf) => conf,
|
|
|
|
|
None => {
|
|
|
|
|
eprintln!("Error opening config file. No Pallet.toml in current directory.");
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let build_config = conf.get_or_default(mode).ok_or(std::io::Error::new(
|
|
|
|
|
std::io::ErrorKind::NotFound,
|
|
|
|
|
"build layout not found",
|
|
|
|
|
))?;
|
|
|
|
|
|
|
|
|
|
let mut command = Command::new(format!("target/{}/{}", build_config.name, conf.name));
|
|
|
|
|
|
|
|
|
|
if let Some(args) = args {
|
|
|
|
|
for arg in args {
|
|
|
|
|
command.arg(arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut child = command.spawn()?;
|
|
|
|
|
|
|
|
|
|
child.wait()?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
2026-03-22 11:16:14 -05:00
|
|
|
}
|