use crate::log::build::{building, downloading, importing};
use fs_extra::dir::{copy, CopyOptions};
use git2::Repository;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs::{create_dir_all, read_to_string};
use url::Url;
extern crate remove_dir_all;
use remove_dir_all::remove_dir_all;
use crate::{
assembler::assemble,
builder::{
FILE_EXTENSION, FOREIGN_FOLDER, INPUT_TOML, MAIN_FILE_NAME, OUTPUT_BUILD_DIR, SOURCE_FOLDER,
},
};
#[derive(Serialize, Deserialize, Debug)]
pub struct Package {
name: String,
version: String,
authors: Vec<String>,
include: Vec<String>,
foreign: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Config {
package: Package,
dependencies: HashMap<String, String>,
}
impl Config {
pub fn new(text: &str) -> Result<Self, String> {
let config: Self = match toml::from_str(text) {
Ok(c) => Ok(c),
Err(e) => {
if let Some(line_col) = e.line_col() {
Err(format!(
"Error parsing toml, failed to parse line \"{}\"",
text.lines().nth(line_col.0).unwrap()
))
} else {
Err(format!("Error parsing toml"))
}
}
}?;
Ok(config)
}
pub fn from_file(path: &str) -> Result<Self, String> {
match std::fs::read_to_string(path) {
Ok(contents) => Ok(Self::new(&contents)?),
Err(_) => Err(format!("Could not open file \"{}\"", path)),
}
}
pub fn assemble(&self, package_path: &str) -> Result<String, String> {
let mut imports = self.package.include.clone();
imports.push(String::from(MAIN_FILE_NAME));
let mut result = String::from("");
for import in imports {
importing(import.clone());
let path = format!(
"{}/{}/{}.{}",
package_path, SOURCE_FOLDER, import, FILE_EXTENSION
);
match read_to_string(&path) {
Ok(script) => match assemble(&script) {
Ok(assembled) => result += &assembled,
Err(e) => return Err(e),
},
Err(_) => return Err(format!("Could not open file \"{}\"", &path)),
}
}
Ok(result)
}
pub fn build(
&self,
package_path: Option<&str>,
base_build_dir: &str,
) -> Result<(String, Vec<String>), String> {
let mut copy_opts = CopyOptions::new();
copy_opts.overwrite = true;
let mut foreign_package_names = self.package.foreign.clone();
let mut result_xasm_script = String::from("");
let prefix = match package_path {
Some(path) => format!("{}/", path),
None => String::from(""),
};
let primary_build_dir = &format!("{}{}", prefix, OUTPUT_BUILD_DIR);
match remove_dir_all(primary_build_dir) {
_ => {}
};
match create_dir_all(primary_build_dir) {
Ok(_) => {}
Err(_) => {
return Err(format!(
"Could not create directory \"{}\"",
primary_build_dir
))
}
}
for name in self.dependencies.keys() {
let url = &self.dependencies[name];
if let Ok(_) = Url::parse(url) {
let path = &format!("{}/{}", primary_build_dir, name);
downloading(name, path);
match Repository::clone(url, path) {
Ok(_) => {
let package = Self::from_file(&format!("{}/{}.toml", path, INPUT_TOML))?;
let package_dependencies =
package.dependencies.values().collect::<Vec<&String>>();
for dep in self.dependencies.values() {
if package_dependencies.contains(&dep) {
return Err(format!("Reused dependency \"{}\"", dep));
}
}
let build_output = package.build(Some(&path), base_build_dir)?;
result_xasm_script += &build_output.0;
foreign_package_names.extend(build_output.1);
}
Err(_) => {
return Err(format!("Could not clone git repository from \"{}\"", url))
}
};
} else {
return Err(format!("\"{}\" is not a valid url", url));
}
}
for foreign_package in &self.package.foreign {
let from = format!("{}{}/{}", prefix, FOREIGN_FOLDER, foreign_package);
if let Err(_) = copy(&from, base_build_dir, ©_opts) {
return Err(format!(
"Could not copy foreign package from \"{}\" to \"{}\"",
from, base_build_dir
));
}
}
let assemble_path = match package_path {
Some(path) => path,
None => ".",
};
building(&self.package.name);
match self.assemble(assemble_path) {
Ok(script) => result_xasm_script += &script,
Err(e) => {
return Err(format!(
"Error while compiling package \"{}\" -->\n{}",
self.package.name, e
))
}
}
Ok((result_xasm_script, foreign_package_names))
}
}