erl_pp 0.2.0

Erlang source code preprocessor
Documentation
use clap::{App, Arg};
use erl_pp::{MacroDef, Preprocessor};
use erl_tokenize::tokens::AtomToken;
use erl_tokenize::{Lexer, Position, PositionRange};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::time::{Duration, Instant};

fn main() -> anyhow::Result<()> {
    let matches = App::new("pp")
        .arg(Arg::with_name("SOURCE_FILE").index(1).required(true))
        .arg(Arg::with_name("SILENT").long("silent"))
        .arg(
            Arg::with_name("CURRENT_DIR")
                .long("current-dir")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("ERL_LIBS")
                .long("libs")
                .takes_value(true)
                .multiple(true),
        )
        .get_matches();
    let src_file = Path::new(matches.value_of("SOURCE_FILE").unwrap());
    let silent = matches.is_present("SILENT");
    if let Some(dir) = matches.value_of("CURRENT_DIR") {
        env::set_current_dir(dir)?;
    }

    let mut src = String::new();
    let mut file = File::open(&src_file).expect("Cannot open file");
    file.read_to_string(&mut src).expect("Cannot read file");

    let start_time = Instant::now();
    let mut count = 0;

    let mut lexer = Lexer::new(&src);
    lexer.set_filepath(src_file.file_name().unwrap());

    let mut preprocessor = Preprocessor::new(lexer);
    if let Some(libs) = matches.values_of("ERL_LIBS") {
        for dir in libs {
            preprocessor.code_paths_mut().push_back(dir.into());
        }
    }
    preprocessor.macros_mut().insert(
        "MODULE".to_string(),
        MacroDef::Dynamic(vec![AtomToken::from_value(
            src_file.file_stem().unwrap().to_str().unwrap(),
            Position::new(),
        )
        .into()]),
    );

    for result in preprocessor {
        let token = result?;
        if !silent {
            println!("[{:?}] {:?}", token.start_position(), token.text());
        }
        count += 1;
    }
    println!("TOKEN COUNT: {}", count);
    println!(
        "ELAPSED: {:?} seconds",
        to_seconds(Instant::now() - start_time)
    );
    Ok(())
}

fn to_seconds(duration: Duration) -> f64 {
    duration.as_secs() as f64 + f64::from(duration.subsec_nanos()) / 1_000_000_000.0
}