use pest::Parser; use avdl_serde_code_generator::{Rule, AVDLParser, to_rust::{self, build_rust_code_from_avdl}}; use std::fs::{self, File, DirEntry}; use std::error::Error; use std::path::{Path, PathBuf}; use std::ffi::{OsStr}; use std::io::Write; use heck::MixedCase; use heck::KebabCase; fn is_kebab_case(s: &str) -> bool { s.to_kebab_case() == s } fn create_rust_version(input_path: &str, output_path: &str) -> Result<(), Box> { let input = fs::read_to_string(input_path)?; let parsed = AVDLParser::parse(Rule::avdl_protocol, &input)?; let mut output = File::create(output_path)?; // let derive_okay_paths = ["keybase1/common.rs","keybase1/upk.rs", "keybase1/teams.rs", "keybase1/reset.rs", "keybase1/tlf_keys", "stellar1/common"]; let derive_okay_paths = ["gregor1","keybase1/teams.rs", "keybase1/teamsearch.rs","chat1/gregor.rs","keybase1/kbfs_common.rs","keybase1/notify_runtimestats.rs","keybase1/simple_fs.rs","keybase1/identify_common.rs","keybase1/identify_ui.rs","keybase1/git.rs","keybase1/upk.rs", "chat1/chat_ui.rs","chat1/local.rs","keybase1/tlf_keys.rs","chat1/unfurl.rs", "chat1/api.rs","chat1/common.rs","stellar1/common.rs","chat1/commands.rs","chat1/remote.rs","keybase1/common.rs","keybase1/reset.rs", "kebayse1/teams.rs", "keybase1/favorite.rs","keybase1/identify.rs", "stellar1/local.rs"]; if derive_okay_paths.iter().any(|p| output_path.contains(p)) { to_rust::set_add_derive(true); } to_rust::set_derive_hash_for_ident(vec!["UserVersion"].into_iter().map(|s| s.into()).collect()); build_rust_code_from_avdl(parsed, &mut output)?; to_rust::set_add_derive(false); Ok(()) } fn visit_dirs(dir: &Path, cb: &F) -> Result<(), Box> where F: Fn(&DirEntry) -> Result<(), Box> { if dir.is_dir() { for entry in fs::read_dir(dir)? { let entry = entry?; let path = entry.path(); if path.is_dir() { cb(&entry)?; visit_dirs(&path, cb)?; } else { cb(&entry)?; } } } Ok(()) } fn map_to_output_file(input: &PathBuf) -> PathBuf { let file_name = String::from(input.file_name().unwrap().to_str().unwrap()); let file_name = if is_kebab_case(&file_name) { file_name.to_mixed_case() } else { file_name.into() }; let original_path = input.as_path().iter().skip_while(|part| { part != &OsStr::new("keybase-protocol") }).skip(1).collect::>(); let mut output_path = Path::new("src/protocol/").to_path_buf(); for part in original_path.iter() { output_path.push(part); } output_path.pop(); let output_filename = Path::new(&file_name).with_extension("rs"); output_path.push(output_filename); output_path } fn create_mod_file(path: &PathBuf) -> Result<(), Box> { println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); let inner_files = fs::read_dir(path).unwrap(); let inner_files: Vec = inner_files.map(|f| { let f = f.expect("Get DirEntry"); if f.path().extension() == Some(OsStr::new("md")) { return String::new(); } let file_name = f.file_name(); let file_name = file_name.to_str().unwrap(); let file_name = if is_kebab_case(file_name) { file_name.to_mixed_case() } else { file_name.into() }; let mod_use = format!("pub mod {};\npub use {}::*;", file_name, file_name); mod_use.replace(".avdl", "") }).collect(); let mut output_filename = Path::new(path).to_path_buf(); output_filename.push("mod.rs"); let output_filename = map_to_output_file(&output_filename); fs::create_dir_all(output_filename.parent().unwrap())?; let mut file = File::create(output_filename)?; write!(&mut file, "{}", inner_files.join("\n"))?; Ok(()) } fn main() { create_mod_file(&Path::new("keybase-protocol/").to_path_buf()).unwrap(); to_rust::set_derive_hash_for_ident(vec!["UserVersion"].into_iter().map(|s| s.into()).collect()); visit_dirs(Path::new("keybase-protocol/"), &|entry: &DirEntry| -> Result<(), Box> { let entry_path = entry.path(); if entry_path.is_dir() { create_mod_file(&entry_path).unwrap(); } else { // Skip MD if entry.path().extension().unwrap() == "md" { return Ok(()); } let output_path = map_to_output_file(&entry_path); fs::create_dir_all(output_path.parent().unwrap())?; create_rust_version(entry.path().to_str().unwrap(), output_path.to_str().unwrap())?; } Ok(()) }).unwrap(); }