use std::fmt;
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Undefined,
Null,
Bool(bool),
Number(f64),
Str(String),
Array(Vec<Value>),
Object(Object),
Range(Box<Range>),
}
impl Value {
pub fn is_absent(&self) -> bool {
matches!(self, Value::Undefined | Value::Null)
}
pub fn truthy(&self) -> bool {
match self {
Value::Undefined | Value::Null => false,
Value::Bool(b) => *b,
Value::Number(n) => *n != 0.0 && !n.is_nan(),
Value::Str(s) => !s.is_empty(),
Value::Array(_) | Value::Object(_) | Value::Range(_) => true,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::Str(s) => Some(s),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Number(n) => Some(*n),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[Value]> {
match self {
Value::Array(a) => Some(a),
_ => None,
}
}
pub fn as_object(&self) -> Option<&Object> {
match self {
Value::Object(o) => Some(o),
_ => None,
}
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Bool(b)
}
}
impl From<f64> for Value {
fn from(n: f64) -> Self {
Value::Number(n)
}
}
impl From<i64> for Value {
fn from(n: i64) -> Self {
Value::Number(n as f64)
}
}
impl From<i32> for Value {
fn from(n: i32) -> Self {
Value::Number(n as f64)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::Str(s.to_owned())
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::Str(s)
}
}
impl From<Vec<Value>> for Value {
fn from(a: Vec<Value>) -> Self {
Value::Array(a)
}
}
impl From<Object> for Value {
fn from(o: Object) -> Self {
Value::Object(o)
}
}
impl From<Range> for Value {
fn from(r: Range) -> Self {
Value::Range(Box::new(r))
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Object {
entries: Vec<(String, Value)>,
}
impl Object {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(n: usize) -> Self {
Self {
entries: Vec::with_capacity(n),
}
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
pub fn contains_key(&self, key: &str) -> bool {
self.entries.iter().any(|(k, _)| k == key)
}
pub fn insert(&mut self, key: impl Into<String>, value: Value) -> Option<Value> {
let key = key.into();
if let Some(slot) = self.entries.iter_mut().find(|(k, _)| *k == key) {
return Some(std::mem::replace(&mut slot.1, value));
}
self.entries.push((key, value));
None
}
pub fn remove(&mut self, key: &str) -> Option<Value> {
let i = self.entries.iter().position(|(k, _)| k == key)?;
Some(self.entries.remove(i).1)
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
self.entries.iter().map(|(k, v)| (k.as_str(), v))
}
pub fn keys(&self) -> impl Iterator<Item = &str> {
self.entries.iter().map(|(k, _)| k.as_str())
}
pub fn values(&self) -> impl Iterator<Item = &Value> {
self.entries.iter().map(|(_, v)| v)
}
}
impl FromIterator<(String, Value)> for Object {
fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
let mut o = Object::new();
for (k, v) in iter {
o.insert(k, v);
}
o
}
}
impl IntoIterator for Object {
type Item = (String, Value);
type IntoIter = std::vec::IntoIter<(String, Value)>;
fn into_iter(self) -> Self::IntoIter {
self.entries.into_iter()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Range {
pub lo: Option<Value>,
pub hi: Option<Value>,
pub exclusive_end: bool,
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Undefined => f.write_str("undefined"),
Value::Null => f.write_str("null"),
Value::Bool(b) => write!(f, "{b}"),
Value::Number(n) => f.write_str(&js_number_to_string(*n)),
Value::Str(s) => f.write_str(s),
Value::Array(a) => {
for (i, v) in a.iter().enumerate() {
if i > 0 {
f.write_str(",")?;
}
if !v.is_absent() {
write!(f, "{v}")?;
}
}
Ok(())
}
Value::Object(_) => f.write_str("[object Object]"),
Value::Range(r) => {
if let Some(lo) = &r.lo {
write!(f, "{lo}")?;
}
f.write_str(if r.exclusive_end { "..." } else { ".." })?;
if let Some(hi) = &r.hi {
write!(f, "{hi}")?;
}
Ok(())
}
}
}
}
pub fn js_number_to_string(n: f64) -> String {
if n.is_nan() {
return "NaN".to_owned();
}
if n.is_infinite() {
return if n > 0.0 { "Infinity" } else { "-Infinity" }.to_owned();
}
if n == 0.0 {
return "0".to_owned();
}
let sci = format!("{:e}", n.abs());
let (mantissa, exp) = sci
.split_once('e')
.expect("LowerExp always emits an exponent");
let exp: i32 = exp.parse().expect("LowerExp exponent is an integer");
let digits: String = mantissa.chars().filter(|c| *c != '.').collect();
let k = digits.len() as i32;
let point = exp + 1; let mut out = String::with_capacity(digits.len() + 8);
if n < 0.0 {
out.push('-');
}
if k <= point && point <= 21 {
out.push_str(&digits);
out.extend(std::iter::repeat_n('0', (point - k) as usize));
} else if 0 < point && point <= 21 {
let split = point as usize;
out.push_str(&digits[..split]);
out.push('.');
out.push_str(&digits[split..]);
} else if -6 < point && point <= 0 {
out.push_str("0.");
out.extend(std::iter::repeat_n('0', (-point) as usize));
out.push_str(&digits);
} else {
let e = point - 1;
out.push_str(&digits[..1]);
if k > 1 {
out.push('.');
out.push_str(&digits[1..]);
}
out.push('e');
out.push(if e < 0 { '-' } else { '+' });
out.push_str(&e.abs().to_string());
}
out
}