use std::env;
use std::io::{self, Write, BufRead};
use std::fs::{self, DirEntry};
use std::ffi::OsString;
use std::path::{Path, PathBuf};

// one possible implementation of walking a directory only visiting files
fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry) -> io::Result<()>)
              -> io::Result<()> {
    if dir.is_dir() {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_dir() {
                visit_dirs(&path, cb)?;
            } else {
                cb(&entry)?;
            }
        }
    }
    Ok(())
}

fn manifest_dir() -> PathBuf {
    PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap())
}

fn out_dir() -> PathBuf {
    env::var_os("OUT_DIR").unwrap().into()
}

fn lib_path() -> PathBuf {
    out_dir().join("src/lib.rs")
}

fn translate_path(src: &Path) -> PathBuf {
    let src = src.to_str().unwrap();
    format!("{}rs", &src[..src.len() - 2]).into()
}

fn translate2rs(src: &DirEntry) -> io::Result<()> {
    let path = src.path();
    if path.extension() != Some(&OsString::from("md")) {
        return Ok(());
    }

    let sink_filename = out_dir().join(
        translate_path(&path).strip_prefix(&manifest_dir()).unwrap());

    eprintln!("{:?} -> {:?}", path, sink_filename);
    println!("rerun-if-changed={}", path.to_str().unwrap());

    fs::create_dir_all(sink_filename.parent().unwrap())?;
    let mut sink = fs::File::create(sink_filename)?;

    for line in io::BufReader::new(fs::File::open(&path)?).lines() {
        writeln!(&mut sink, "//! {}", line?)?;
    }

    let mut lib =
        fs::OpenOptions::new().create(true).append(true).open(&lib_path())?;
    writeln!(&mut lib, "pub mod {};", path.file_stem().unwrap().to_str().unwrap())?;

    Ok(())
}

fn main() {
    let _ = fs::remove_file(&lib_path());
    visit_dirs(&manifest_dir().join("src"), &translate2rs).unwrap();
}
