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
#![deny(clippy::all, clippy::pedantic)]
#![deny(missing_docs, warnings, intra_doc_link_resolution_failure)]
#![doc(deny(warnings))]

//! # artichoke-core
//!
//! artichoke-core crate provides a set of traits that, when implemented, create
//! a complete Ruby interpreter.
//!
//! artichoke-core is a work in progress. When fully functioning, artichoke-core
//! will provide interpreter-agnostic implementations of Ruby Core and Ruby
//! Standard Library that pass ruby/spec if an interpreter implements all of the
//! required traits.

use std::error;
use std::fmt;
use std::io;

pub mod convert;
pub mod def;
pub mod eval;
pub mod exception;
pub mod file;
pub mod load;
pub mod state;
pub mod top_self;
pub mod types;
pub mod value;
pub mod warn;

/// Errors returned by Artichoke interpreters.
#[derive(Debug)]
pub enum ArtichokeError {
    /// Failed to create an argspec.
    ArgSpec,
    /// Failed to convert from a Rust type to a [`Value`](value::Value).
    ConvertToRuby {
        /// Source type of conversion.
        from: types::Rust,
        /// Destination type of conversion.
        to: types::Ruby,
    },
    /// Failed to convert from a [`Value`](value::Value) to a Rust type.
    ConvertToRust {
        /// Source type of conversion.
        from: types::Ruby,
        /// Destination type of conversion.
        to: types::Rust,
    },
    /// Exception raised during eval.
    ///
    /// See [`Eval`](eval::Eval).
    // TODO: disabled for migration Exec(exception::Exception),
    Exec(String),
    /// Unable to initalize interpreter.
    New,
    /// Class or module with this name is not defined in the artichoke VM.
    NotDefined(String),
    /// Arg count exceeds maximum allowed by the VM.
    TooManyArgs {
        /// Number of arguments supplied.
        given: usize,
        /// Maximum number of arguments supported.
        max: usize,
    },
    /// Attempted to use an uninitialized interpreter.
    Uninitialized,
    /// Eval or funcall returned an interpreter-internal value.
    UnreachableValue,
    /// [`io::Error`] when interacting with virtual filesystem.
    ///
    /// See `artichoke_vfs`.
    Vfs(io::Error),
}

impl Eq for ArtichokeError {}

// TODO: remove this impl. I think this is only a kludge for tests.
impl PartialEq for ArtichokeError {
    fn eq(&self, other: &Self) -> bool {
        // this is a hack because io::Error does not impl PartialEq
        format!("{}", self) == format!("{}", other)
    }
}

impl fmt::Display for ArtichokeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::ArgSpec => write!(f, "could not generate argspec"),
            Self::ConvertToRuby { from, to } => {
                write!(f, "failed to convert from {} to {}", from, to)
            }
            Self::ConvertToRust { from, to } => {
                write!(f, "failed to convert from {} to {}", from, to)
            }
            Self::Exec(backtrace) => write!(f, "{}", backtrace),
            Self::New => write!(f, "failed to create mrb interpreter"),
            Self::NotDefined(fqname) => write!(f, "{} not defined", fqname),
            Self::TooManyArgs { given, max } => write!(
                f,
                "Too many args for funcall. Gave {}, but max is {}",
                given, max
            ),
            Self::Uninitialized => write!(f, "mrb interpreter not initialized"),
            Self::UnreachableValue => write!(f, "extracted unreachable type from interpreter"),
            Self::Vfs(err) => write!(f, "mrb vfs io error: {}", err),
        }
    }
}

impl error::Error for ArtichokeError {
    fn description(&self) -> &str {
        "artichoke interpreter error"
    }

    fn cause(&self) -> Option<&dyn error::Error> {
        match self {
            Self::Vfs(inner) => Some(inner),
            _ => None,
        }
    }
}