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();
 | ||
|  |     // }
 | ||
|  | }
 |