dust-lang 0.4.2

General purpose programming language
use std::{
    cmp::Ordering,
    fmt::{self, Display, Formatter},
    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
};

use stanza::{
    renderer::{console::Console, Renderer},
    style::Styles,
    table::{Cell, Content, Row, Table},
};

use crate::{error::rw_lock_error::RwLockError, Value};

#[derive(Debug, Clone)]
pub struct List(Arc<RwLock<Vec<Value>>>);

impl Default for List {
    fn default() -> Self {
        Self::new()
    }
}

impl List {
    pub fn new() -> Self {
        List(Arc::new(RwLock::new(Vec::new())))
    }

    pub fn with_capacity(capacity: usize) -> Self {
        List(Arc::new(RwLock::new(Vec::with_capacity(capacity))))
    }

    pub fn with_items(items: Vec<Value>) -> Self {
        List(Arc::new(RwLock::new(items)))
    }

    pub fn items(&self) -> Result<RwLockReadGuard<Vec<Value>>, RwLockError> {
        Ok(self.0.read()?)
    }

    pub fn items_mut(&self) -> Result<RwLockWriteGuard<Vec<Value>>, RwLockError> {
        Ok(self.0.write()?)
    }

    pub fn as_text_table(&self) -> Table {
        let cells: Vec<Cell> = self
            .items()
            .unwrap()
            .iter()
            .map(|value| {
                if let Value::List(list) = value {
                    Cell::new(Styles::default(), Content::Nested(list.as_text_table()))
                } else if let Value::Map(map) = value {
                    Cell::new(Styles::default(), Content::Nested(map.as_text_table()))
                } else {
                    Cell::new(Styles::default(), Content::Label(value.to_string()))
                }
            })
            .collect();

        let row = if cells.is_empty() {
            Row::new(
                Styles::default(),
                vec![Cell::new(
                    Styles::default(),
                    Content::Label("empty list".to_string()),
                )],
            )
        } else {
            Row::new(Styles::default(), cells)
        };

        Table::default().with_row(row)
    }
}

impl Display for List {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let renderer = Console::default();

        f.write_str(&renderer.render(&self.as_text_table()))
    }
}

impl Eq for List {}

impl PartialEq for List {
    fn eq(&self, other: &Self) -> bool {
        if let (Ok(left), Ok(right)) = (self.items(), other.items()) {
            if left.len() != right.len() {
                return false;
            } else {
                for (i, j) in left.iter().zip(right.iter()) {
                    if i != j {
                        return false;
                    }
                }
            }
        }

        true
    }
}

impl PartialOrd for List {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for List {
    fn cmp(&self, other: &Self) -> Ordering {
        if let (Ok(left), Ok(right)) = (self.items(), other.items()) {
            left.cmp(&right)
        } else {
            Ordering::Equal
        }
    }
}