terrazzo-terminal 0.2.7

A simple web-based terminal emulator built on Terrazzo.
#![cfg(feature = "server")]

use std::sync::Arc;

use super::Direction;
use super::Side;
use super::Tile;
use super::Tiles;
use super::state::TREE;
use super::state::TilesStateError;
use crate::tiles::app::App;
use crate::tiles::id::TileId;

pub fn add_node(
    with_direction: Direction,
    next_to: TileId,
    side: Side,
) -> Result<Arc<Tiles>, TilesStateError> {
    let mut lock = TREE.lock().map_err(|_| TilesStateError::PoisonError)?;
    let tree = lock.clone().unwrap_or_default();
    let mut new_id = Some(TileId::new());
    let tree = add_node_aux(tree, with_direction, next_to, side, &mut new_id)?;
    if new_id.is_some() {
        return Err(TilesStateError::TileIdNotFound(next_to));
    }
    *lock = tree.clone().into();
    return Ok(tree);
}

fn add_node_aux(
    tree: Arc<Tiles>,
    with_direction: Direction,
    next_to: TileId,
    side: Side,
    new_id: &mut Option<TileId>,
) -> Result<Arc<Tiles>, TilesStateError> {
    Ok(match &*tree {
        Tiles::Tile(node) if node.id == next_to => {
            let new = Arc::new(Tiles::Tile(Tile {
                id: new_id
                    .take()
                    .ok_or(TilesStateError::DuplicateTileId(next_to))?,
                app: App::Default,
                remote: node.remote.clone(),
            }));
            Arc::new(Tiles::Array {
                id: TileId::new(),
                direction: with_direction,
                nodes: match side {
                    Side::Before => vec![new, tree],
                    Side::After => vec![tree, new],
                },
            })
        }
        Tiles::Array {
            id,
            direction,
            nodes,
        } if new_id.is_some() => {
            let mut nodes2 = Vec::with_capacity(nodes.len());
            for node in nodes {
                nodes2.extend(add_node_flatten(
                    node.clone(),
                    with_direction,
                    *direction,
                    next_to,
                    side,
                    new_id,
                )?)
            }
            Arc::new(Tiles::Array {
                id: *id,
                direction: *direction,
                nodes: nodes2,
            })
        }
        _ => tree.clone(),
    })
}

fn add_node_flatten(
    tree: Arc<Tiles>,
    with_direction: Direction,
    flatten_direction: Direction,
    next_to: TileId,
    side: Side,
    new_id: &mut Option<TileId>,
) -> Result<Vec<Arc<Tiles>>, TilesStateError> {
    if new_id.is_none() {
        return Ok(vec![tree]);
    }
    let tree = add_node_aux(tree, with_direction, next_to, side, new_id)?;
    if let Tiles::Array {
        id: _,
        direction,
        nodes,
    } = &*tree
        && *direction == flatten_direction
    {
        Ok(nodes.clone())
    } else {
        Ok(vec![tree])
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;

    pub fn add_node_for_tests(
        tree: Arc<Tiles>,
        with_direction: Direction,
        next_to: TileId,
        side: Side,
        new_id: &mut Option<TileId>,
    ) -> Result<Arc<Tiles>, TilesStateError> {
        super::add_node_aux(tree, with_direction, next_to, side, new_id)
    }
}