osbuilder 0.3.0

OS-agnostic builder for building custom OS
Documentation
mod structs;
pub use crate::structs::*;
use anyhow::bail;
pub(crate) use anyhow::Ok;
pub use anyhow::{Error, Result};
pub use flate2::{write::GzEncoder, Compression};
pub use serde::{Deserialize, Serialize};
pub use std::{cell::RefCell, path::Path};
use url::Url;
fn main() -> anyhow::Result<(), anyhow::Error> {
    match std::path::Path::new("osbuilder.ron").exists() {
        true => {
            println!(">> Build File exists, everything's OK")
        }
        false => {
            println!(">> Creating the config file right now");
            let cfg = Cfg {
                buildopts: BuildOpts {
                    tb_name: "mtos_latest".to_string(),
                    distro_name: "MatuushOS".to_string(),
                    version: 0.9999,
                    threads: 6,
                    to_fetch: vec!["".to_string()].into(),
                    bdir: "".to_string(),
                    dir_structure: "MatuushOS".to_string(),
                    xcscriptdir: String::from(""),
                    bscriptdir: String::from(""),
                    support_url: String::from(""),
                }.into(),
            };
            println!(">> Creating config file at {:?}", std::env::current_dir());
            std::fs::write(
                Path::new("osbuilder.ron").to_path_buf(),
                ron::ser::to_string(&cfg.buildopts)?,
            )?;
            println!(">> DONE\n>> Make sure you edit this file according to your needs or you'll screw it up.");
            std::process::exit(0);
        }
    }

    let ron: Cfg = ron::de::from_str::<Cfg>(&std::fs::read_to_string("osbuilder.ron")?)?;
    match std::path::Path::new(std::path::Path::new(&ron.buildopts.bdir)).exists() {
        true => std::fs::remove_dir_all(Path::new(&ron.buildopts.bdir))?,
        false => {
            println!(">> Builddir does not exists, creating it right now");
            let mut clfs: String = String::new();
            clfs.push_str(&ron.buildopts.bdir);
            println!(">> Using {} as the build directory", clfs);
            println!(">> Creating build directory");
            std::fs::DirBuilder::new().create(clfs)?;
            std::fs::DirBuilder::new().create(Path::new(&ron.buildopts.bdir).join("vfs"))?;
            std::fs::DirBuilder::new()
                .create(Path::new(&ron.buildopts.bdir).join("vfs").join("scripts"))?;
            std::fs::DirBuilder::new()
                .create(Path::new(&ron.buildopts.bdir).join("vfs").join("src"))?;
        }
    }

    println!(">> Build directory created, moving on\n>> Creating the virtual filesystem");
    if &ron.buildopts.dir_structure == "Standard Linux" {
        std::env::set_current_dir(Path::new(&ron.buildopts.bdir).join("vfs"))?;
        for i in [
            "bin", "lib", "sbin", "usr", "etc", "var", 
        ]
        .iter()
        {
            std::fs::DirBuilder::new().recursive(true).create(i)?;
            println!("\t>> Created directory {:?}", i);
        }
        for sym in ["bin", "lib", "sbin"].iter() {
            #[cfg(target_os = "linux")]
            std::os::unix::fs::symlink(sym, Path::new("usr").join(sym))?;
        }
    }

    println!(">> Copying buildscripts");
    match std::path::Path::new(std::path::Path::new(&ron.buildopts.bscriptdir)).exists() {
        true => std::fs::copy(
            &ron.buildopts.bscriptdir,
            Path::new(&ron.buildopts.bdir).join("scripts"),
        )?,
        false => {
            bail!(
                "!! The directory {:?} doesn't exists",
                std::path::Path::new(&ron.buildopts.bscriptdir).to_str()
            );
        }
    };
    println!(">> Downloading sources");
    for dl in ron.buildopts.to_fetch.borrow().iter() {
        let u = Url::parse(dl)?;
        fetch_data::download(
            dl,
            Path::new(&ron.buildopts.bdir)
                .join("vfs")
                .join("src")
                .join(u.path()),
        )?;
        println!("\t>> Downloaded {} from {}", u.path(), dl);
    }
    println!(">> Let's finally build {:?}", ron.buildopts.distro_name);
    println!("\t>> Running crosscompiling scripts");
    for entry in
        walkdir::WalkDir::new(Path::new(&ron.buildopts.bdir).join(&ron.buildopts.xcscriptdir))
            .sort_by_file_name()
    {
        let path = std::path::PathBuf::from(entry?.path());
        if let Some(ext) = path.extension() {
            if ext == "sh" {
                let path_string = path.display().to_string();
                let args: Vec<&str> = vec![path_string.as_str()];
                std::process::Command::new("sh -c").args(args).output()?;
            }
        }
    }
    println!("\t>> Running build scripts");
    for entry in walkdir::WalkDir::new(Path::new(&ron.buildopts.bdir).join("vfs/scripts"))
        .sort_by_file_name()
    {
        let path = std::path::PathBuf::from(entry?.path());
        if let Some(ext) = path.extension() {
            if ext == "sh" {
                let path_string = path.display().to_string();
                let args: Vec<&str> = vec![path_string.as_str()];
                std::process::Command::new("bash -c").args(args).output()?;
            }
        }
    }
    println!(">> Compressing final file system");
    let mut filename = String::from(&ron.buildopts.tb_name);
    filename.push_str(".tar.gz");
    let f = std::fs::File::create(filename)?;
    let enc = GzEncoder::new(f, Compression::default());
    let mut tar = tar::Builder::new(enc);
    tar.append_dir_all(".", Path::new(&ron.buildopts.bdir).join("vfs"))?;
    Ok(())
}