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 crate::compiling;
use crate::{Diagnostics, Options};
use runestick::{Context, Unit};
use std::rc::Rc;
use thiserror::Error;

mod source_loader;
mod sources;

pub use self::source_loader::{FileSourceLoader, SourceLoader};
pub use self::sources::Sources;

/// Error raised when we failed to load sources.
///
/// Look at the passed in [Diagnostics] instance for details.
#[derive(Debug, Error)]
#[error("failed to load sources (see `errors` for details)")]
pub struct LoadSourcesError;

/// Load and compile the given sources.
///
/// Uses the [Source::name](runestick::Source::name) when generating diagnostics
/// to reference the file.
///
/// # Examples
///
/// Note: these must be built with the `diagnostics` feature enabled to give
/// access to `rune::termcolor`.
///
/// ```rust
/// use rune::termcolor::{ColorChoice, StandardStream};
/// use rune::EmitDiagnostics as _;
///
/// use std::path::Path;
/// use std::sync::Arc;
/// use std::error::Error;
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let context = runestick::Context::with_default_modules()?;
/// let mut options = rune::Options::default();
///
/// let mut sources = rune::Sources::new();
/// sources.insert(runestick::Source::new("entry", r#"
/// pub fn main() {
///     println("Hello World");
/// }
/// "#));
///
/// let mut diagnostics = rune::Diagnostics::new();
///
/// let result = rune::load_sources(&context, &options, &mut sources, &mut diagnostics);
///
/// if !diagnostics.is_empty() {
///     let mut writer = StandardStream::stderr(ColorChoice::Always);
///     diagnostics.emit_diagnostics(&mut writer, &sources)?;
/// }
///
/// let unit = result?;
/// let unit = Arc::new(unit);
/// let vm = runestick::Vm::new(Arc::new(context.runtime()), unit.clone());
/// # Ok(())
/// # }
/// ```
pub fn load_sources(
    context: &Context,
    options: &Options,
    sources: &mut Sources,
    diagnostics: &mut Diagnostics,
) -> Result<Unit, LoadSourcesError> {
    let visitor = Rc::new(compiling::NoopCompileVisitor::new());
    let source_loader = Rc::new(FileSourceLoader::new());

    load_sources_with_visitor(
        context,
        options,
        sources,
        diagnostics,
        visitor,
        source_loader,
    )
}

/// Load the specified sources with a visitor.
pub fn load_sources_with_visitor<'a>(
    context: &Context,
    options: &Options,
    sources: &mut Sources,
    diagnostics: &mut Diagnostics,
    visitor: Rc<dyn compiling::CompileVisitor>,
    source_loader: Rc<dyn SourceLoader + 'a>,
) -> Result<Unit, LoadSourcesError> {
    let unit = if context.has_default_modules() {
        compiling::UnitBuilder::with_default_prelude()
    } else {
        compiling::UnitBuilder::default()
    };

    let result = compiling::compile_with_options(
        &*context,
        sources,
        &unit,
        diagnostics,
        &options,
        visitor,
        source_loader,
    );

    if let Err(()) = result {
        return Err(LoadSourcesError);
    }

    if options.link_checks {
        unit.link(&*context, diagnostics);

        if diagnostics.has_error() {
            return Err(LoadSourcesError);
        }
    }

    match unit.build() {
        Ok(unit) => Ok(unit),
        Err(error) => {
            diagnostics.error(0, error);
            Err(LoadSourcesError)
        }
    }
}