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
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use super::into_bytes::IntoBytes;
use core::{Loc, RpDecl, RpEnumBody, RpInterfaceBody, RpName, RpPackage, RpServiceBody,
           RpTupleBody, RpTypeBody, RpVersionedPackage, WithPos};
use environment::Environment;
use errors::*;
use std::collections::BTreeMap;
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

pub trait PackageProcessor<'el>
where
    Self: 'el + Sized,
{
    type Out: Default + IntoBytes<Self>;

    fn ext(&self) -> &str;

    fn env(&self) -> &'el Environment;

    fn out_path(&self) -> &Path;

    fn default_process(&self, _: &mut Self::Out, name: &'el RpName) -> Result<()> {
        warn!("not supported: {}", name);
        Ok(())
    }

    fn processed_package(&self, package: &RpVersionedPackage) -> RpPackage;

    fn process_interface(
        &self,
        out: &mut Self::Out,
        body: &'el Loc<RpInterfaceBody>,
    ) -> Result<()> {
        self.default_process(out, &body.name)
    }

    fn process_type(&self, out: &mut Self::Out, body: &'el Loc<RpTypeBody>) -> Result<()> {
        self.default_process(out, &body.name)
    }

    fn process_tuple(&self, out: &mut Self::Out, body: &'el Loc<RpTupleBody>) -> Result<()> {
        self.default_process(out, &body.name)
    }

    fn process_enum(&self, out: &mut Self::Out, body: &'el Loc<RpEnumBody>) -> Result<()> {
        self.default_process(out, &body.name)
    }

    fn process_service(&self, out: &mut Self::Out, body: &'el Loc<RpServiceBody>) -> Result<()> {
        self.default_process(out, &body.name)
    }

    fn populate_files(&self) -> Result<BTreeMap<RpVersionedPackage, Self::Out>> {
        self.do_populate_files(|_| Ok(()))
    }

    fn do_populate_files<F>(
        &self,
        mut callback: F,
    ) -> Result<BTreeMap<RpVersionedPackage, Self::Out>>
    where
        F: FnMut(&'el RpDecl) -> Result<()>,
    {
        use self::RpDecl::*;

        let mut files = BTreeMap::new();

        // Process all types discovered so far.
        for decl in self.env().decl_iter() {
            callback(decl)
                .and_then(|_| {
                    let mut out = files
                        .entry(decl.name().package.clone())
                        .or_insert_with(Self::Out::default);

                    match *decl {
                        Interface(ref b) => self.process_interface(&mut out, b),
                        Type(ref b) => self.process_type(&mut out, b),
                        Tuple(ref b) => self.process_tuple(&mut out, b),
                        Enum(ref b) => self.process_enum(&mut out, b),
                        Service(ref b) => self.process_service(&mut out, b),
                    }
                })
                .with_pos(decl.pos())?;
        }

        Ok(files)
    }

    fn resolve_full_path(&self, package: &RpPackage) -> Result<PathBuf> {
        let full_path = self.out_path().to_owned();
        let mut full_path = package.parts.iter().fold(full_path, |a, b| a.join(b));
        full_path.set_extension(self.ext());
        Ok(full_path)
    }

    fn setup_module_path(&self, package: &RpPackage) -> Result<PathBuf> {
        let full_path = self.resolve_full_path(package)?;

        if let Some(parent) = full_path.parent() {
            if !parent.is_dir() {
                debug!("+dir: {}", parent.display());
                fs::create_dir_all(parent)?;
            }
        }

        Ok(full_path)
    }

    fn write_files(&'el self, files: BTreeMap<RpVersionedPackage, Self::Out>) -> Result<()> {
        for (package, out) in files {
            let package = self.processed_package(&package);
            let full_path = self.setup_module_path(&package)?;

            debug!("+module: {}", full_path.display());

            let mut f = File::create(full_path)?;
            let bytes = out.into_bytes(self)?;
            f.write_all(&bytes)?;
            f.flush()?;
        }

        Ok(())
    }
}