protogen/
lib.rs

1extern crate nom;
2
3use std::path::Path;
4use std::env;
5use std::fs;
6use std::io;
7use parser::Message;
8use std::path::PathBuf;
9
10pub mod generator;
11pub mod parser;
12
13pub fn process_current_dir() -> io::Result<()> {
14    process_dir(&env::current_dir()?)
15}
16
17fn convert_error<T>(r: Result<T, String>) -> Result<T, io::Error> {
18    r.map_err(|s| io::Error::new(io::ErrorKind::Other, s))
19}
20
21fn get_out_dir() -> io::Result<PathBuf> {
22    match env::var_os("OUT_DIR") {
23        Some(var) => Ok(PathBuf::from(&var)),
24        None => return convert_error(Err("missing OUT_DIR variable".to_string())),
25    }
26}
27
28pub fn process_dir(path: &Path) -> io::Result<()> {
29    let out_dir = get_out_dir()?;
30    let name = path.components().last().expect("not a valid path");
31    let out_file = out_dir.join(Path::new(&name)).with_extension("rs");
32
33    process_dir_to(path, &out_file, None)
34}
35
36pub fn process_dir_to(path: &Path, out_file: &Path, post_process: Option<&Fn(&str) -> String>) -> io::Result<()> {
37    fs::create_dir_all(out_file.parent().expect("No parent for out file"))?;
38    let messages = parse_dir_int(path)?;
39    process(messages, &out_file, post_process)?;
40
41    Ok(())
42}
43
44pub fn process_file(path: &Path, post_process: Option<&Fn(&str) -> String>) -> io::Result<()> {
45    let messages = parse_file(&path)?;
46
47    let out_dir = get_out_dir()?;
48    let name = path.components().last().expect("not a valid path");
49    let out_file = out_dir.join(Path::new(&name)).with_extension("rs");
50
51    process(messages, &out_file, post_process)
52}
53
54fn parse_dir_int(path: &Path) -> io::Result<Vec<Message>> {
55    println!("process dir {}", path.to_string_lossy());
56
57    let mut messages = vec![];
58
59    for entry in fs::read_dir(path)? {
60        let entry = entry?;
61
62        if entry.file_type()?.is_dir() {
63            messages.extend(parse_dir_int(&entry.path())?);
64        } else if entry.path().extension().iter().any(|ext| *ext == "protogen") {
65            messages.extend(parse_file(&entry.path())?);
66        }
67    };
68
69    Ok(messages)
70}
71
72fn parse_file(path: &Path) -> io::Result<Vec<Message>> {
73    let data = fs::read(path)?;
74
75    match parser::source_file(&data) {
76        Ok((rest, messages)) => {
77            let rest = String::from_utf8_lossy(&rest);
78
79            if rest.trim().is_empty() {
80                return Ok(messages);
81            } else {
82                return Err(io::Error::new(io::ErrorKind::Other,
83                                          format!("Parse error at {}", rest)));
84            }
85        }
86        Err(e) => {
87            return Err(io::Error::new(io::ErrorKind::Other,
88                                      format!("Parse error {:?}", e)));
89        }
90    }
91}
92
93fn process(messages: Vec<Message>, out_path: &Path, post_process: Option<&Fn(&str) -> String>) -> io::Result<()>  {
94    // let name = path.file_stem().expect("not a file");
95    // let out_file = out_dir.join(Path::new(name)).with_extension("rs");
96    let mut generated = convert_error(generator::Generator::from_messages(messages))?.to_string();
97    if let Some(process_fn) = post_process {
98        generated = process_fn(&generated);
99    }
100
101    fs::write(&out_path, generated)?;
102
103    Ok(())
104}