use std::{collections::HashMap, fmt::Display, sync::Arc};
use futures::{future::BoxFuture, lock::Mutex, FutureExt};
use scout_parser::ast::{Block, FnParam, Identifier};
use serde_json::{json, Value};
use crate::env::EnvPointer;
#[derive(Debug)]
pub enum Object {
Null,
Map(Mutex<HashMap<Identifier, Arc<Object>>>),
Str(String),
Node(fantoccini::elements::Element),
List(Mutex<Vec<Arc<Object>>>),
Boolean(bool),
Number(f64),
Fn(Vec<FnParam>, Block),
Return(Arc<Object>),
Module(EnvPointer),
}
impl Object {
pub fn type_str(&self) -> &str {
use Object::*;
match self {
Null => "null",
Map(_) => "map",
Str(_) => "string",
Node(_) => "node",
List(_) => "list",
Boolean(_) => "bool",
Number(_) => "number",
Fn(_, _) => "fn",
_ => "object",
}
}
pub async fn into_iterable(&self) -> Option<impl IntoIterator<Item = Arc<Object>>> {
use Object::*;
match self {
List(v) => {
let inner = v.lock().await;
Some(inner.clone())
}
Str(s) => {
let new_vec: Vec<Arc<Object>> = s
.chars()
.map(|c| Arc::new(Object::Str(c.to_string())))
.collect();
Some(new_vec)
}
_ => None,
}
}
pub fn to_display(&self) -> BoxFuture<String> {
use Object::*;
async move {
match self {
Null => "Null".into(),
Map(hash) => {
let inner = hash.lock().await;
let mut out = "{ ".to_string();
for (idx, (i, o)) in inner.iter().enumerate() {
out.push_str(&format!("{}: {}", i, o.to_display().await));
if idx != inner.len() - 1 {
out.push_str(", ");
}
}
out.push_str(" }");
out
}
Str(s) => format!("\"{}\"", s),
Node(_) => "Node".into(),
List(mutex) => {
let inner = mutex.lock().await;
let mut out: String = "[".into();
for (i, obj) in inner.iter().enumerate() {
out.push_str(obj.to_display().await.as_str());
if i != inner.len() - 1 {
out.push_str(", ");
}
}
out.push(']');
out
}
Boolean(b) => format!("{}", b),
Number(n) => format!("{}", n),
_ => "object".into(),
}
}
.boxed()
}
pub fn eq<'a>(&'a self, other: &'a Self) -> BoxFuture<'a, bool> {
async move {
use Object::*;
match (self, other) {
(Null, Null) => true,
(Map(a), Map(b)) => {
let a_i = a.lock().await;
let b_i = b.lock().await;
for key in a_i.keys() {
match b_i.get(key) {
Some(obj) => {
if !a_i.get(key).unwrap().eq(obj).await {
return false;
}
}
None => return false,
}
}
true
}
(Str(a), Str(b)) => a == b,
(Node(a), Node(b)) => a.element_id() == b.element_id(),
(List(a), List(b)) => {
let a_i = a.lock().await;
let b_i = b.lock().await;
if a_i.len() != b_i.len() {
return false;
}
for idx in 0..(a_i.len() - 1) {
if !a_i[idx].eq(&b_i[idx]).await {
return false;
}
}
true
}
(Boolean(a), Boolean(b)) => a == b,
(Number(a), Number(b)) => a == b,
_ => false,
}
}
.boxed()
}
pub async fn to_json(&self) -> Value {
use Object::*;
match self {
Null => Value::Null,
Str(s) => Value::String(s.to_owned()),
Node(_) => Value::String("Node".to_owned()),
List(list) => vec_to_json(list).await,
Map(map) => {
let inner = map.lock().await;
Value::Object(obj_map_to_json(&inner).await)
}
Boolean(b) => Value::Bool(*b),
Number(n) => json!(n),
Fn(_, _) => panic!("cant serialize func"),
_ => panic!("cant serialize object"),
}
}
pub async fn is_truthy(&self) -> bool {
use Object::*;
match self {
Null => false,
Str(s) => !s.is_empty(),
Map(m) => !m.lock().await.is_empty(),
List(v) => !v.lock().await.is_empty(),
Boolean(b) => *b,
Number(n) => *n > 0.0,
_ => true,
}
}
}
impl Display for Object {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Object::*;
match self {
Null => write!(f, "Null"),
Str(s) => write!(f, "\"{}\"", s),
Node(_) => write!(f, "Node"),
List(_objs) => write!(f, "list"),
Boolean(b) => write!(f, "{}", b),
Number(n) => write!(f, "{}", n),
_ => write!(f, "object"),
}
}
}
fn vec_to_json(v: &Mutex<Vec<Arc<Object>>>) -> BoxFuture<'_, Value> {
async move {
let mut out = Vec::new();
let inner = v.lock().await;
for obj in &*inner {
out.push(obj.clone().to_json().await);
}
Value::Array(out)
}
.boxed()
}
pub fn obj_map_to_json(
map: &HashMap<Identifier, Arc<Object>>,
) -> BoxFuture<serde_json::Map<String, Value>> {
async move {
let mut out = serde_json::Map::new();
for (ident, obj) in map.iter() {
out.insert(ident.name.clone(), obj.to_json().await);
}
out
}
.boxed()
}
pub fn json_to_obj(json: &Value) -> Arc<Object> {
match json {
Value::Null => Arc::new(Object::Null),
Value::Bool(b) => Arc::new(Object::Boolean(*b)),
Value::String(s) => Arc::new(Object::Str(s.clone())),
Value::Array(vals) => Arc::new(Object::List(Mutex::new(
vals.iter().map(json_to_obj).collect(),
))),
Value::Object(in_map) => {
let mut map = HashMap::new();
for (k, v) in in_map.iter() {
let ident = Identifier::new(k.clone());
let val = json_to_obj(v);
map.insert(ident, val);
}
Arc::new(Object::Map(Mutex::new(map)))
}
Value::Number(n) => Arc::new(Object::Number(n.as_f64().unwrap_or(f64::NAN))),
}
}