libskyfall 0.1.0

Quantum-Safe P2P Communication System
Documentation
use std::collections::HashMap;

use bon::Builder;
use serde::{ Deserialize, Serialize };
use serde_json::Value;
use uuid::Uuid;

use crate::{ utils::StreamId, Channel, Client, Peer };

/// A remote peer command. The ID should be unique. Generally, this command should be generated by [Client]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Command {
    /// Unique command ID
    pub id: Uuid,

    /// Path to this command (`PREFIX/path/to/command`)
    pub path: String,

    /// Optional JSON data
    pub data: Option<Value>,

    /// Optional stream identifier
    pub stream: Option<(String, StreamId)>,
}

/// Route descriptor to target a command
/// 
/// # Examples
/// 
/// ```
/// use crate::handler::Route;
/// 
/// let s = Route::builder("echo", "ECHO/echo").stream(true).build();
/// ```
#[derive(Serialize, Deserialize, Clone, Debug, Builder)]
pub struct Route {
    /// Unique route selector
    #[builder(into, start_fn)]
    pub selector: String,

    /// Path description
    #[builder(into, start_fn)]
    pub path: String,

    /// Whether to provide JSON data
    #[builder(default)]
    pub data: bool,

    /// Whether to open a stream
    #[builder(default)]
    pub stream: bool,

    #[builder(into)]
    /// Optional about/help text
    pub about: Option<String>
}

impl Route {
    /// One-shot to construct a [Route]
    /// 
    /// # Arguments
    /// 
    /// - `selector` (`impl AsRef<str>`) - Selector ID
    /// - `path` (`impl AsRef<str>`) - Route path
    /// - `expects_data` (`bool`) - Whether to provide JSON data
    /// - `expects_stream` (`bool`) - Whether to open a stream
    /// - `about` (`Option<impl AsRef<str>>`) - Optional help/about text
    /// 
    /// # Returns
    /// 
    /// - `Self` - The constructed [Route]
    /// 
    pub fn new(
        selector: impl AsRef<str>,
        path: impl AsRef<str>,
        expects_data: bool,
        expects_stream: bool,
        about: Option<impl AsRef<str>>
    ) -> Self {
        Self {
            selector: selector.as_ref().to_string(),
            path: path.as_ref().to_string(),
            data: expects_data,
            stream: expects_stream,
            about: about.and_then(|a| Some(a.as_ref().to_string()))
        }
    }
}

/// Describes a route handler that can handle any number of routes under a namespace
#[async_trait::async_trait]
pub trait Handler {
    /// Handler namespace - should be unique
    fn id(&self) -> String;

    /// Handler about/help text
    fn about(&self) -> Option<String> {
        None
    }

    /// Returns an (ideally static) mapping of selector:route
    fn get_routes(&self) -> HashMap<String, Route>;

    /// Handler for messages to this Handler
    async fn on_message(
        &mut self,
        selector: String,
        path: String,
        client: Client,
        peer: Peer,
        route: Route,
        id: Uuid,
        captured_segments: Vec<(String, String)>,
        data: Option<Value>,
        stream: Option<Channel>
    ) -> anyhow::Result<()>;
}

#[derive(Clone, Debug)]
pub struct Request {
    pub selector: String,
    pub path: String,
    pub client: Client,
    pub peer: Peer,
    pub route: Route,
    pub id: Uuid
}