1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use convert_case::{Case, Casing};
use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::Command;

mod check_recursive;
mod error;
mod generate_file_headers;
mod generate_from_pairs;
mod generate_misc;
mod generate_structs;
mod generate_trait_impls;
pub mod manager;

fn sanitize_identifier(id: &str) -> String {
    id.to_case(Case::UpperCamel)
}

/// Like a file, formats contents on closing
pub struct FormattingFile(Option<File>, PathBuf);

impl FormattingFile {
    pub fn create(p: impl AsRef<Path>) -> io::Result<Self> {
        Ok(Self(Some(File::create(&p)?), p.as_ref().to_path_buf()))
    }

    pub fn open(p: impl AsRef<Path>) -> io::Result<Self> {
        Ok(Self(Some(File::open(&p)?), p.as_ref().to_path_buf()))
    }
}

fn try_fmt(p: impl AsRef<Path>) -> io::Result<()> {
    println!("Formatting {:?}", p.as_ref());
    Command::new("rustfmt").arg(p.as_ref()).spawn()?.wait()?;

    Ok(())
}

impl Drop for FormattingFile {
    fn drop(&mut self) {
        drop(self.0.take());

        if let Err(e) = try_fmt(&self.1) {
            eprintln!("{}", e);
        }
    }
}

impl Read for FormattingFile {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.0.as_ref().unwrap().read(buf)
    }
}

impl Write for FormattingFile {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.0.as_ref().unwrap().write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.0.as_ref().unwrap().flush()
    }
}