209 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use hbb_common::{bail, ResultType};
 | |
| use serde_derive::{Deserialize, Serialize};
 | |
| use std::{collections::HashMap, fs::File, io::prelude::*, path::Path};
 | |
| use walkdir::WalkDir;
 | |
| 
 | |
| //mod rc;
 | |
| 
 | |
| #[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
 | |
| pub struct ConfigItem {
 | |
|     // include directory or file
 | |
|     pub inc: String,
 | |
|     // exclude files
 | |
|     pub exc: Vec<String>,
 | |
|     // out_path = origin_path - suppressed_front
 | |
|     pub suppressed_front: String,
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
 | |
| pub struct Config {
 | |
|     // output source file
 | |
|     pub outfile: String,
 | |
|     // config items
 | |
|     pub confs: Vec<ConfigItem>,
 | |
| }
 | |
| 
 | |
| pub fn get_outin_files<'a>(item: &'a ConfigItem) -> ResultType<HashMap<String, String>> {
 | |
|     let mut outin_filemap = HashMap::new();
 | |
| 
 | |
|     for entry in WalkDir::new(&item.inc).follow_links(true) {
 | |
|         let path = entry?.into_path();
 | |
|         if path.is_file() {
 | |
|             let mut exclude = false;
 | |
|             for excfile in item.exc.iter() {
 | |
|                 if excfile.starts_with("*.") {
 | |
|                     if let Some(ext) = path.extension().and_then(|x| x.to_str()) {
 | |
|                         if excfile.ends_with(&format!(".{}", ext)) {
 | |
|                             exclude = true;
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
|                 } else {
 | |
|                     if path.ends_with(Path::new(excfile)) {
 | |
|                         exclude = true;
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             if exclude {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             let mut suppressed_front = item.suppressed_front.clone();
 | |
|             if !suppressed_front.is_empty() && suppressed_front.ends_with('/') {
 | |
|                 suppressed_front.push('/');
 | |
|             }
 | |
|             let outpath = path.strip_prefix(Path::new(&suppressed_front))?;
 | |
|             let outfile = if outpath.is_absolute() {
 | |
|                 match outpath
 | |
|                     .file_name()
 | |
|                     .and_then(|f| f.to_str())
 | |
|                     .map(|f| f.to_string())
 | |
|                 {
 | |
|                     None => {
 | |
|                         bail!("Failed to get filename of {}", outpath.display());
 | |
|                     }
 | |
|                     Some(s) => s,
 | |
|                 }
 | |
|             } else {
 | |
|                 match outpath.to_str() {
 | |
|                     None => {
 | |
|                         bail!("Failed to convert {} to string", outpath.display());
 | |
|                     }
 | |
|                     // Simple replace \ to / here.
 | |
|                     // A better way is to use lib [path-slash](https://github.com/rhysd/path-slash)
 | |
|                     Some(s) => s.to_string().replace("\\", "/"),
 | |
|                 }
 | |
|             };
 | |
|             let infile = match path.canonicalize()?.to_str() {
 | |
|                 None => {
 | |
|                     bail!("Failed to get file path of {}", path.display());
 | |
|                 }
 | |
|                 Some(s) => s.to_string(),
 | |
|             };
 | |
|             if let Some(_) = outin_filemap.insert(outfile.clone(), infile) {
 | |
|                 bail!("outfile {} is set before", outfile);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     Ok(outin_filemap)
 | |
| }
 | |
| 
 | |
| pub fn generate(conf_file: &str) -> ResultType<()> {
 | |
|     let conf = confy::load_path(conf_file)?;
 | |
|     generate_with_conf(&conf)?;
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| pub fn generate_with_conf<'a>(conf: &'a Config) -> ResultType<()> {
 | |
|     let mut outfile = File::create(&conf.outfile)?;
 | |
| 
 | |
|     outfile.write(
 | |
|         br##"use hbb_common::{bail, ResultType};
 | |
| use std::{
 | |
|     fs::{self, File},
 | |
|     io::prelude::*,
 | |
|     path::Path,
 | |
| };
 | |
| 
 | |
| "##,
 | |
|     )?;
 | |
| 
 | |
|     outfile.write(b"#[allow(dead_code)]\n")?;
 | |
|     outfile.write(b"pub fn extract_resources(root_path: &str) -> ResultType<()> {\n")?;
 | |
|     outfile.write(b"    let mut resources: Vec<(&str, &[u8])> = Vec::new();\n")?;
 | |
| 
 | |
|     let mut outin_files = HashMap::new();
 | |
|     for item in conf.confs.iter() {
 | |
|         for (o, i) in get_outin_files(item)?.into_iter() {
 | |
|             if let Some(_) = outin_files.insert(o.clone(), i) {
 | |
|                 bail!("outfile {} is set before", o);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     let mut count = 1;
 | |
|     for (o, i) in outin_files.iter() {
 | |
|         let mut infile = File::open(&i)?;
 | |
|         let mut buffer = Vec::<u8>::new();
 | |
|         infile.read_to_end(&mut buffer)?;
 | |
| 
 | |
|         let var_outfile = format!("outfile_{}", count);
 | |
|         let var_outdata = format!("outdata_{}", count);
 | |
| 
 | |
|         write!(outfile, "    let {} = \"{}\";\n", var_outfile, o)?;
 | |
|         write!(outfile, "    let {}: &[u8] = &[\n        ", var_outdata)?;
 | |
| 
 | |
|         let mut line_num = 20;
 | |
|         for v in buffer {
 | |
|             if line_num == 0 {
 | |
|                 write!(outfile, "\n        ")?;
 | |
|                 line_num = 20;
 | |
|             }
 | |
|             write!(outfile, "{:#04x}, ", v)?;
 | |
|             line_num -= 1;
 | |
|         }
 | |
|         write!(outfile, "\n    ];\n")?;
 | |
| 
 | |
|         write!(
 | |
|             outfile,
 | |
|             "    resources.push(({}, &{}));\n",
 | |
|             var_outfile, var_outdata
 | |
|         )?;
 | |
| 
 | |
|         count += 1;
 | |
|     }
 | |
| 
 | |
|     outfile.write(b"    do_extract(root_path, resources)?;\n")?;
 | |
|     outfile.write(b"    Ok(())\n")?;
 | |
|     outfile.write(b"}\n")?;
 | |
| 
 | |
|     outfile.write(
 | |
|         br##"
 | |
| #[allow(dead_code)]
 | |
| fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> {
 | |
|     let mut root_path = root_path.replace("\\", "/");
 | |
|     if !root_path.ends_with('/') {
 | |
|         root_path.push('/');
 | |
|     }
 | |
|     let root_path = Path::new(&root_path);
 | |
|     for (outfile, data) in resources {
 | |
|         let outfile_path = root_path.join(outfile);
 | |
|         match outfile_path.parent().and_then(|p| p.to_str()) {
 | |
|             None => {
 | |
|                 bail!("Failed to get parent of {}", outfile_path.display());
 | |
|             }
 | |
|             Some(p) => {
 | |
|                 fs::create_dir_all(p)?;
 | |
|                 let mut of = File::create(outfile_path)?;
 | |
|                 of.write_all(data)?;
 | |
|                 of.flush()?;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     Ok(())
 | |
| }
 | |
| "##,
 | |
|     )?;
 | |
| 
 | |
|     outfile.flush()?;
 | |
| 
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| #[cfg(test)]
 | |
| mod tests {
 | |
|     #[test]
 | |
|     fn it_works() {
 | |
|         let result = 2 + 2;
 | |
|         assert_eq!(result, 4);
 | |
|     }
 | |
| 
 | |
|     // #[test]
 | |
|     // fn test_extract() {
 | |
|     //     use super::*;
 | |
|     //     rc::extract_resources("D:").unwrap();
 | |
|     // }
 | |
| }
 |