trans-gen-core 0.1.3

Generating trans code (helper library)
Documentation
use std::collections::{HashMap, HashSet};

pub use trans_schema;
use trans_schema::*;

pub trait Generator {
    fn new(name: &str, version: &str) -> Self;
    fn add_only(&mut self, schema: &Schema);
    fn result(self) -> HashMap<String, String>;
}

pub struct GeneratorImpl<T: Generator> {
    inner: T,
    added: HashMap<Name, Schema>,
}

impl<T: Generator> GeneratorImpl<T> {
    pub fn new(name: &str, version: &str) -> Self {
        Self {
            inner: T::new(name, version),
            added: HashMap::new(),
        }
    }
    pub fn add(&mut self, schema: &Schema) {
        let schema_name = schema.full_name();
        let current = self.added.get(&schema_name);
        if let Some(current) = current {
            assert_eq!(
                current, schema,
                "Two schemas with same name but different structure"
            );
            return;
        }
        self.added.insert(schema_name, schema.clone());
        match schema {
            Schema::Struct(Struct { fields, .. }) => {
                for field in fields {
                    self.add(&field.schema);
                }
            }
            Schema::OneOf { variants, .. } => {
                for variant in variants {
                    for field in &variant.fields {
                        self.add(&field.schema);
                    }
                }
            }
            Schema::Option(inner) => {
                self.add(inner);
            }
            Schema::Vec(inner) => {
                self.add(inner);
            }
            Schema::Map(key_type, value_type) => {
                self.add(key_type);
                self.add(value_type);
            }
            Schema::Bool
            | Schema::Int32
            | Schema::Int64
            | Schema::Float32
            | Schema::Float64
            | Schema::String
            | Schema::Enum { .. } => {}
        }
        self.inner.add_only(schema);
    }
    pub fn result(self) -> HashMap<String, String> {
        self.inner.result()
    }
    pub fn write_to<P: AsRef<std::path::Path>>(self, dir: P) -> std::io::Result<()> {
        use std::path::Path;
        let dir = dir.as_ref();
        for (path, content) in self.result() {
            if let Some(parent) = Path::new(&path).parent() {
                std::fs::create_dir_all(dir.join(parent))?;
            }
            let mut file = std::fs::File::create(dir.join(path))?;
            use std::io::Write;
            file.write_all(content.as_bytes())?;
        }
        Ok(())
    }
}

pub fn add_all(files: &mut HashMap<String, String>, dir: &include_dir::Dir, prefix: &str) {
    for file in dir.files() {
        files.insert(
            format!("{}/{}", prefix, file.path().to_str().unwrap()),
            file.contents_utf8().unwrap().to_owned(),
        );
    }
    for subdir in dir.dirs() {
        add_all(files, subdir, prefix);
    }
}

pub struct Writer {
    content: String,
    ident_level: usize,
}

impl Writer {
    pub fn new() -> Self {
        Self {
            content: String::new(),
            ident_level: 0,
        }
    }
    pub fn inc_ident(&mut self) {
        self.ident_level += 1;
    }
    pub fn dec_ident(&mut self) {
        self.ident_level -= 1;
    }
    pub fn get(self) -> String {
        assert_eq!(self.ident_level, 0, "Incorrect indentation");
        self.content
    }
}

impl std::fmt::Write for Writer {
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
        for c in s.chars() {
            if c != '\n' && self.content.chars().last().unwrap_or('\n') == '\n' {
                for _ in 0..self.ident_level * 4 {
                    self.content.push(' ');
                }
            }
            self.content.push(c);
        }
        Ok(())
    }
}