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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
extern crate reproto_ast as ast;
extern crate reproto_core as core;
extern crate reproto_manifest as manifest;

use core::{ContextItem, Object, Resolver, RpPackage, RpVersionedPackage};
use manifest::Lang;
use std::any::Any;
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
use std::str;

/// Input to the compiler.
pub enum Input<'input> {
    /// Already derive file.
    File(ast::File<'input>, Option<RpVersionedPackage>),
    /// Object that should be parsed.
    Object(Box<Object>, Option<RpVersionedPackage>),
}

/// A simple compilation stage.
pub struct SimpleCompile<'input> {
    pub input: Input<'input>,
    pub package_prefix: Option<RpPackage>,
    pub resolver: Option<Box<Resolver>>,
    pub errors: Option<Rc<RefCell<Vec<ContextItem>>>>,
}

impl<'input> SimpleCompile<'input> {
    /// Build a new compilation stage.
    pub fn new(input: Input<'input>) -> SimpleCompile {
        Self {
            input: input,
            package_prefix: None,
            resolver: None,
            errors: None,
        }
    }

    /// Set package prefix.
    pub fn package_prefix(self, package: RpPackage) -> Self {
        Self {
            package_prefix: Some(package),
            ..self
        }
    }

    /// Set resolver.
    pub fn resolver(self, resolver: Box<Resolver>) -> Self {
        Self {
            resolver: Some(resolver),
            ..self
        }
    }

    /// Set a reference to collect errors.
    pub fn with_errors(self, errors: Rc<RefCell<Vec<ContextItem>>>) -> Self {
        Self {
            errors: Some(errors),
            ..self
        }
    }
}

/// Perform a simplified compilation that outputs the result into the provided Write
/// implementation.
pub fn simple_compile(
    out: &mut fmt::Write,
    config: SimpleCompile,
    modules: Vec<Box<Any>>,
    lang: &Lang,
) -> core::errors::Result<()> {
    let SimpleCompile {
        input,
        package_prefix,
        resolver,
        errors,
    } = config;

    let resolver = resolver.unwrap_or_else(|| Box::new(core::EmptyResolver));

    let capturing = core::CapturingFilesystem::new();

    let ctx = core::Context::new(capturing.filesystem());

    // Set errors reference, if configured.
    let ctx = if let Some(errors) = errors {
        ctx.with_errors(errors)
    } else {
        ctx
    };

    let ctx = Rc::new(ctx);

    let mut env = lang.into_env(ctx.clone(), package_prefix.clone(), resolver);

    match input {
        Input::File(file, package) => {
            env.import_file(file, package)?;
        }
        Input::Object(object, package) => {
            env.import_object(object.as_ref(), package)?;
        }
    }

    let preamble = manifest::ManifestPreamble::new(Some(manifest::Language::Java), None);
    let mut manifest = manifest::read_manifest(lang, preamble)?;
    manifest.modules = modules;
    manifest.package_prefix = package_prefix;

    lang.compile(ctx, env, manifest)?;

    let borrowed = capturing.files().try_borrow()?;

    let comment = format!(
        " {} file(s) generated by https://github.com/reproto",
        borrowed.len()
    );

    if let Some(comment) = lang.comment(comment.as_str()) {
        writeln!(out, "{}", comment.as_str())?;
        writeln!(out, "")?;
    }

    let mut it = borrowed.iter().peekable();

    while let Some((path, content)) = it.next() {
        if let Some(comment) = lang.comment(format!(" File: {}", path.display()).as_str()) {
            writeln!(out, "{}", comment)?;
            writeln!(out, "")?;
        }

        out.write_str(str::from_utf8(content)?)?;

        if it.peek().is_some() {
            writeln!(out, "")?;
        }
    }

    Ok(())
}