orbital-ui 0.1.1

Leptos component library and design system for focused, accessible product UIs
use leptos::prelude::*;

const PERMISSION_DENIED_PREFIX: &str = "permission_denied::";
const PERMISSION_CHECK_FAILED_PREFIX: &str = "permission_check_failed::";

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PermissionServerError {
    Denied { permission: String },
    CheckFailed { permission: String, details: String },
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PermissionToastRequest {
    pub permission: String,
    pub description: String,
}

#[derive(Clone, Copy)]
pub struct PermissionToastBus {
    set_request: WriteSignal<Option<PermissionToastRequest>>,
}

impl PermissionToastBus {
    pub fn emit(&self, request: PermissionToastRequest) {
        self.set_request.set(Some(request));
    }
}

pub fn provide_permission_toast_bus(
    set_request: WriteSignal<Option<PermissionToastRequest>>,
) -> PermissionToastBus {
    let bus = PermissionToastBus { set_request };
    provide_context(bus);
    bus
}

pub fn use_permission_toast_bus() -> Option<PermissionToastBus> {
    use_context::<PermissionToastBus>()
}

pub fn parse_permission_server_error(message: &str) -> Option<PermissionServerError> {
    let denied_message = if let Some(permission) = message.strip_prefix(PERMISSION_DENIED_PREFIX) {
        Some(permission)
    } else {
        message
            .find(PERMISSION_DENIED_PREFIX)
            .map(|idx| &message[idx + PERMISSION_DENIED_PREFIX.len()..])
    };

    if let Some(permission) = denied_message {
        let permission = permission.trim();
        if !permission.is_empty() {
            return Some(PermissionServerError::Denied {
                permission: permission.to_string(),
            });
        }
    }

    let check_failed_message =
        if let Some(rest) = message.strip_prefix(PERMISSION_CHECK_FAILED_PREFIX) {
            Some(rest)
        } else {
            message
                .find(PERMISSION_CHECK_FAILED_PREFIX)
                .map(|idx| &message[idx + PERMISSION_CHECK_FAILED_PREFIX.len()..])
        };

    if let Some(rest) = check_failed_message {
        let mut parts = rest.splitn(2, "::");
        let permission = parts.next().unwrap_or_default().trim().to_string();
        let details = parts.next().unwrap_or_default().trim().to_string();
        if !permission.is_empty() {
            return Some(PermissionServerError::CheckFailed {
                permission,
                details,
            });
        }
    }

    None
}

pub fn report_server_fn_error(error: &ServerFnError) -> bool {
    report_server_fn_error_with_bus(use_permission_toast_bus(), error)
}

pub fn report_server_fn_error_with_bus(
    bus: Option<PermissionToastBus>,
    error: &ServerFnError,
) -> bool {
    let message = error.to_string();
    let Some(parsed) = parse_permission_server_error(&message) else {
        return false;
    };

    if let Some(bus) = bus {
        let request = match parsed {
            PermissionServerError::Denied { permission } => PermissionToastRequest {
                permission,
                description: "You do not have permission to perform this action.".to_string(),
            },
            PermissionServerError::CheckFailed {
                permission,
                details,
            } => PermissionToastRequest {
                permission,
                description: format!("Unable to verify permission: {details}"),
            },
        };
        bus.emit(request);
        return true;
    }

    false
}