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
use std::fmt;
use std::fmt::Debug;
use std::error::Error;
use neon_runtime;
use neon_runtime::raw;
use context::{Context};
use context::internal::Env;
use result::{JsResult, JsResultExt};
use object::{Object};
use handle::{Handle, Managed};
use super::{Value, ValueInternal};

/// A JavaScript Date object
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct JsDate(raw::Local);

impl Value for JsDate { }

impl Managed for JsDate {
    fn to_raw(self) -> raw::Local { self.0 }

    fn from_raw(_: Env, h: raw::Local) -> Self { JsDate(h) }
}

/// The Error struct for a Date
#[derive(Debug)]
pub struct DateError(DateErrorKind);

impl DateError {
    pub fn kind(&self) -> DateErrorKind {
        self.0
    }
}

impl fmt::Display for DateError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.0.as_str())
    }
}

impl Error for DateError {}

/// The error kinds corresponding to `DateError`
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum DateErrorKind {
    Overflow,
    Underflow,
}

impl DateErrorKind {
    fn as_str(&self) -> &'static str {
        match *self {
            DateErrorKind::Overflow => "Date overflow",
            DateErrorKind::Underflow => "Date underflow",
        }
    }
}

impl<'a, T: Value> JsResultExt<'a, T> for Result<Handle<'a, T>, DateError> {
    /// Creates an `Error` on error
    fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, T> {
        self.or_else(|e| cx.throw_range_error(e.0.as_str()))
    }
}

impl JsDate {
    /// The smallest possible Date value, defined by ECMAScript. See https://www.ecma-international.org/ecma-262/5.1/#sec-15.7.3.3
    pub const MIN_VALUE: f64 = -8.64e15;
    /// The largest possible Date value, defined by ECMAScript. See https://www.ecma-international.org/ecma-262/5.1/#sec-15.7.3.2
    pub const MAX_VALUE: f64 = 8.64e15;

    /// Creates a new Date. It errors when `value` is outside the range of valid JavaScript Date values. When `value`
    /// is `NaN`, the operation will succeed but with an invalid Date
    pub fn new<'a, C: Context<'a>, T: Into<f64>>(cx: &mut C, value: T) -> Result<Handle<'a, JsDate>, DateError> {
        let env = cx.env().to_raw();
        let time = value.into();

        if time > JsDate::MAX_VALUE {
            return Err(DateError(DateErrorKind::Overflow))
        } else if time < JsDate::MIN_VALUE {
            return Err(DateError(DateErrorKind::Underflow))
        }

        let local = unsafe {
            neon_runtime::date::new_date(env, time)
        };
        let date = Handle::new_internal(JsDate(local));
        Ok(date)
    }

    /// Creates a new Date with lossy conversion for out of bounds Date values. Out of bounds
    /// values will be treated as NaN
    pub fn new_lossy<'a, C: Context<'a>, V: Into<f64>>(cx: &mut C, value: V) -> Handle<'a, JsDate> {
        let env = cx.env().to_raw();
        let local = unsafe {
            neon_runtime::date::new_date(env, value.into())
        };
        Handle::new_internal(JsDate(local))
    }

    /// Gets the Date's value. An invalid Date will return `std::f64::NaN`
    pub fn value<'a, C: Context<'a>>(self, cx: &mut C) -> f64 {
        let env = cx.env().to_raw();
        unsafe {
            neon_runtime::date::value(env, self.to_raw())
        }
    }

    /// Checks if the Date's value is valid. A Date is valid if its value is between
    /// `JsDate::MIN_VALUE` and `JsDate::MAX_VALUE` or if it is `NaN`
    pub fn is_valid<'a, C: Context<'a>>(self, cx: &mut C) -> bool {
        let value = self.value(cx);
        value <= JsDate::MAX_VALUE && value >= JsDate::MIN_VALUE
    }
}

impl ValueInternal for JsDate {
    fn name() -> String { "object".to_string() }

    fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
        unsafe { neon_runtime::tag::is_date(env.to_raw(), other.to_raw()) }
    }
}

impl Object for JsDate { }