use std::{ env, fs::{self, File}, io::Write, iter, path::{Path, PathBuf}, process::Command, }; use trixy_parser::parse_trixy_lang; use trixy_types::C_TYPE_HEADER; use crate::config::TrixyConfig; pub mod config; mod generate; const VIM_LINE_RUST: &'static str = "// vim: filetype=rust\n"; const VIM_LINE_C: &'static str = "// vim: filetype=c\n"; impl TrixyConfig { /// This is the heart of Trixy /// It mainly does one thing: /// - Generate a tree of modules from the input trixy file /// pub fn generate(&self) { let source_code = fs::read_to_string(&self.trixy_path).unwrap_or_else(|err| { panic! {"Can't read file at path: '{}'. The Error is: '{}'", self.trixy_path.display(), err}; }); let trixy_code = parse_trixy_lang(&source_code).unwrap_or_else(|err| { panic! {"Parsing of the trixy file failed: \n{}", err} }); // host code let tokens = generate::generate(&trixy_code, &self); eprintln!("{}", tokens); let host_code = prettyplease::unparse( &syn::parse2(tokens).expect("This code was generated, it should also be parsable"), ); let mut host_code_out = fs::File::create(PathBuf::from(format!( "{}/{}", env::var("OUT_DIR").expect("The build script should have this define"), &self.host_code_name.display() ))) .expect("This file should always be free to use"); write!(host_code_out, "{}\n{}", host_code, VIM_LINE_RUST).expect("Write should work"); // c header let c_header = generate::c_api::header::generate(&trixy_code, &self); let c_header_path = PathBuf::from(format!( "{}/{}", env::var("OUT_DIR").expect("The build script should have this define"), &self.c_header_name.display() )); let mut c_header_out = fs::File::create(&c_header_path).expect("This file should always be free to use"); write!(c_header_out, "{}\n{}", c_header, VIM_LINE_C).expect("Write should work"); Command::new("clang-format") .args(["-i", &c_header_path.to_str().unwrap()]) .status() .unwrap_or_else(|err| { panic!( "Failed to format the c header file with `clang-format`; Error: `{}`", err ) }); if let Some(dist_dir) = &self.dist_dir_path { if !dist_dir.is_dir() { fs::create_dir(dist_dir).unwrap_or_else(|err| { panic! { "Failed to create the dist directory ('{}') because of: `{}`", dist_dir.display(), err} }); } let c_header_dist = PathBuf::from(format!("{}/{}", dist_dir.display(), "generated.h")); fs::copy(c_header_path, c_header_dist).unwrap_or_else( |err| panic! {"Failed to copy the c header ('generated.h') to the dist dir because of: `{}`", err}, ); let (interface_name, interface_content) = { let interface_header = format!( "\ /* This file is automatcially generated by Trixy */ \n\ #ifndef TRIXY_INTERFACE_H \n\ #define TRIXY_INTERFACE_H \n\ #include \"generated.h\" \n\ #endif // TRIXY_INTERFACE_H \n\ " ); ("interface.h", interface_header) }; C_TYPE_HEADER .iter() .chain(iter::once(&(interface_name, &interface_content[..]))) .for_each(|(name, content)| { let path: &Path = &Path::new(name); if self.check_dist_dir { if path.exists() { panic! { "The file ('{}') already exists in your dist dir ('{}')! If you want to silence this check set `check_dist_dir` to false", path.display(), dist_dir.display() } } } let header_path = PathBuf::from(format!("{}/{}", dist_dir.display(), path.display())); let mut file = File::create(&header_path).unwrap_or_else(|err| { panic! { "Failed to create the file at '{}' because of: '{}'", header_path.display(), err } }); write!(file, "{}", content).unwrap_or_else(|err| { panic! { "Failed to copy the c header ('{}') to the dist dir because of: `{}`", path.display(), err} }); }); } } }