st-protocol 0.1.0

Binary wire protocol for smart-tree daemon communication
Documentation
//! Verb definitions using control ASCII codes as opcodes
//!
//! Each verb is a single byte in the control ASCII range (0x01-0x1F).
//! 0x00 is reserved for END marker.

/// Protocol verbs mapped to control ASCII codes
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Verb {
    // === Core Operations ===
    /// SOH (0x01) - Scan directory
    Scan = 0x01,
    /// STX (0x02) - Format output
    Format = 0x02,
    /// ETX (0x03) - Search files/content
    Search = 0x03,
    /// EOT (0x04) - End stream/session
    EndStream = 0x04,

    // === Heartbeat & Status ===
    /// ENQ (0x05) - Ping/health check
    Ping = 0x05,
    /// ACK (0x06) - OK/acknowledgment
    Ok = 0x06,
    /// BEL (0x07) - Alert/notification
    Alert = 0x07,

    // === Navigation & History ===
    /// BS (0x08) - Back/undo
    Back = 0x08,
    /// HT (0x09) - Request context
    Context = 0x09,
    /// LF (0x0A) - Next item in sequence
    Next = 0x0A,

    // === Stats & Completion ===
    /// VT (0x0B) - Statistics request
    Stats = 0x0B,
    /// FF (0x0C) - Clear/reset
    Clear = 0x0C,
    /// CR (0x0D) - Complete/commit
    Complete = 0x0D,

    // === Authentication ===
    /// SO (0x0E) - Start auth block
    AuthStart = 0x0E,
    /// SI (0x0F) - End auth block
    AuthEnd = 0x0F,

    // === Audio/Media ===
    /// DLE (0x10) - Audio data (AcousticMemory from liquid-rust)
    Audio = 0x10,

    // === Access Control ===
    /// DC1 (0x11) - Permit access
    Permit = 0x11,
    /// DC2 (0x12) - Deny access
    Deny = 0x12,
    /// DC3 (0x13) - Elevate privileges
    Elevate = 0x13,
    /// DC4 (0x14) - Audit log
    Audit = 0x14,

    // === Error & Subscription ===
    /// NAK (0x15) - Error response
    Error = 0x15,
    /// SYN (0x16) - Subscribe to updates
    Subscribe = 0x16,
    /// ETB (0x17) - Unsubscribe
    Unsubscribe = 0x17,
    /// CAN (0x18) - Cancel operation
    Cancel = 0x18,

    // === M8 Memory Operations ===
    /// EM (0x19) - Wave signal (M8 memory)
    M8Wave = 0x19,
    /// SUB (0x1A) - Remember/store
    Remember = 0x1A,
    /// FS (0x1C) - Recall from memory
    Recall = 0x1C,
    /// GS (0x1D) - Forget/delete
    Forget = 0x1D,

    // === Session Management ===
    /// RS (0x1E) - Session control
    Session = 0x1E,
    /// US (0x1F) - User identification
    User = 0x1F,
}

impl Verb {
    /// Create verb from raw byte
    pub fn from_byte(b: u8) -> Option<Self> {
        match b {
            0x01 => Some(Verb::Scan),
            0x02 => Some(Verb::Format),
            0x03 => Some(Verb::Search),
            0x04 => Some(Verb::EndStream),
            0x05 => Some(Verb::Ping),
            0x06 => Some(Verb::Ok),
            0x07 => Some(Verb::Alert),
            0x08 => Some(Verb::Back),
            0x09 => Some(Verb::Context),
            0x0A => Some(Verb::Next),
            0x0B => Some(Verb::Stats),
            0x0C => Some(Verb::Clear),
            0x0D => Some(Verb::Complete),
            0x0E => Some(Verb::AuthStart),
            0x0F => Some(Verb::AuthEnd),
            0x10 => Some(Verb::Audio),
            0x11 => Some(Verb::Permit),
            0x12 => Some(Verb::Deny),
            0x13 => Some(Verb::Elevate),
            0x14 => Some(Verb::Audit),
            0x15 => Some(Verb::Error),
            0x16 => Some(Verb::Subscribe),
            0x17 => Some(Verb::Unsubscribe),
            0x18 => Some(Verb::Cancel),
            0x19 => Some(Verb::M8Wave),
            0x1A => Some(Verb::Remember),
            0x1C => Some(Verb::Recall),
            0x1D => Some(Verb::Forget),
            0x1E => Some(Verb::Session),
            0x1F => Some(Verb::User),
            _ => None,
        }
    }

    /// Get raw byte value
    #[inline]
    pub fn as_byte(self) -> u8 {
        self as u8
    }

    /// Check if verb requires authentication
    pub fn requires_auth(self) -> bool {
        matches!(
            self,
            Verb::Format
                | Verb::Clear
                | Verb::Permit
                | Verb::Deny
                | Verb::Elevate
                | Verb::Remember
                | Verb::Forget
        )
    }

    /// Get the security level required for this verb
    pub fn security_level(self) -> u8 {
        match self {
            // Level 0: Read-only (no auth) + memory ops (TODO: add auth in Phase 4)
            Verb::Scan | Verb::Search | Verb::Stats | Verb::Ping |
            Verb::Context | Verb::Recall | Verb::Format |
            Verb::Remember | Verb::Forget | Verb::M8Wave | Verb::Audio => 0,

            // Level 1: Local write (session required)
            Verb::Clear | Verb::Subscribe | Verb::Unsubscribe => 1,

            // Level 2: Mutate (FIDO required)
            Verb::Elevate => 2,

            // Level 3: Admin (FIDO + PIN)
            Verb::Permit | Verb::Deny | Verb::Audit => 3,

            // Everything else: session required
            _ => 1,
        }
    }

    /// Human-readable name
    pub fn name(self) -> &'static str {
        match self {
            Verb::Scan => "SCAN",
            Verb::Format => "FORMAT",
            Verb::Search => "SEARCH",
            Verb::EndStream => "END_STREAM",
            Verb::Ping => "PING",
            Verb::Ok => "OK",
            Verb::Alert => "ALERT",
            Verb::Back => "BACK",
            Verb::Context => "CONTEXT",
            Verb::Next => "NEXT",
            Verb::Stats => "STATS",
            Verb::Clear => "CLEAR",
            Verb::Complete => "COMPLETE",
            Verb::AuthStart => "AUTH_START",
            Verb::AuthEnd => "AUTH_END",
            Verb::Audio => "AUDIO",
            Verb::Permit => "PERMIT",
            Verb::Deny => "DENY",
            Verb::Elevate => "ELEVATE",
            Verb::Audit => "AUDIT",
            Verb::Error => "ERROR",
            Verb::Subscribe => "SUBSCRIBE",
            Verb::Unsubscribe => "UNSUBSCRIBE",
            Verb::Cancel => "CANCEL",
            Verb::M8Wave => "M8_WAVE",
            Verb::Remember => "REMEMBER",
            Verb::Recall => "RECALL",
            Verb::Forget => "FORGET",
            Verb::Session => "SESSION",
            Verb::User => "USER",
        }
    }
}

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

    #[test]
    fn test_verb_roundtrip() {
        for b in 0x01..=0x1F {
            if let Some(verb) = Verb::from_byte(b) {
                assert_eq!(verb.as_byte(), b);
            }
        }
    }

    #[test]
    fn test_ping_is_0x05() {
        assert_eq!(Verb::Ping.as_byte(), 0x05);
    }

    #[test]
    fn test_security_levels() {
        // Read-only operations are level 0
        assert_eq!(Verb::Scan.security_level(), 0);
        assert_eq!(Verb::Search.security_level(), 0);
        assert_eq!(Verb::Ping.security_level(), 0);

        // Admin operations are level 3
        assert_eq!(Verb::Permit.security_level(), 3);
        assert_eq!(Verb::Deny.security_level(), 3);
    }
}