terrazzo-terminal 0.2.7

A simple web-based terminal emulator built on Terrazzo.
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;

use nameth::NamedEnumValues as _;
use nameth::nameth;
use terrazzo::prelude::diagnostics;

use self::diagnostics::debug;
use crate::text_editor::side::SideViewList;
use crate::text_editor::side::SideViewNode;
use crate::text_editor::side::SvnItem;

#[nameth]
#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)]
pub enum RemoveFileError {
    #[error("[{n}] File can't be a child of file {0}", n = self.name())]
    #[cfg_attr(not(feature = "diagnostics"), serde(rename = "E"))]
    ExpectedFolder(Arc<str>),

    #[error("[{n}] Parent folder does not exist: {0}", n = self.name())]
    #[cfg_attr(not(feature = "diagnostics"), serde(rename = "P"))]
    ParentNotFound(PathBuf),

    #[error("[{n}] The file was not found", n = self.name())]
    #[cfg_attr(not(feature = "diagnostics"), serde(rename = "F"))]
    FileNotFound,
}

pub fn remove_node_rec<'l>(
    parent: &Path,
    mut relative_path: impl Iterator<Item = &'l Path>,
    node: Option<&SideViewNode>,
) -> Result<Option<SideViewNode>, RemoveFileError> {
    let next = relative_path.next();
    debug!(?next, ?node, "Remove node rec");
    let Some(next) = next else {
        return Ok(None);
    };
    let Some(node) = node else {
        return Err(RemoveFileError::ParentNotFound(parent.into()));
    };
    let (folder, notify) = match &node.item {
        SvnItem::Folder { folder, notify } => (folder.clone(), notify.clone()),
        SvnItem::File { metadata } => {
            return Err(RemoveFileError::ExpectedFolder(metadata.name.clone()));
        }
    };
    Ok(Some(SideViewNode {
        properties: node.properties.clone(),
        item: SvnItem::Folder {
            folder: remove_node_rec_folder(relative_path, &folder, next)?.into(),
            notify,
        },
    }))
}

fn remove_node_rec_folder<'l>(
    relative_path: impl Iterator<Item = &'l Path>,
    folder: &SideViewList,
    folder_name: &Path,
) -> Result<SideViewList, RemoveFileError> {
    let child = remove_node_rec(
        folder_name,
        relative_path,
        folder.get(folder_name).map(Arc::as_ref),
    )?;
    if let Some(child) = child {
        let mut folder = folder.clone();
        folder.insert(folder_name.into(), child.into());
        Ok(folder)
    } else if folder.contains_key(folder_name) {
        let mut folder = folder.clone();
        folder.remove(folder_name);
        Ok(folder)
    } else {
        Err(RemoveFileError::FileNotFound)
    }
}