#![allow(unused)]
extern crate std;
use crate::{Mutex, behavior::BehaviorState};
use bytes::{BufMut, Bytes, BytesMut};
use core::fmt::Display;
use uuid::Uuid;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum Groot2RequestType {
#[default]
Undefined = 0,
FullTree = b'T',
State = b'S',
BlackBoard = b'B',
HookInsert = b'I',
HookRemove = b'R',
HooksDump = b'D',
RemoveAllHooks = b'A',
DisableAllHooks = b'X',
BreakpointReached = b'N',
BreakpointUnlock = b'U',
ToggleRecording = b'r',
GetTransitions = b't',
}
const PROTOCOL_ID: u8 = 2;
const UNDEFINED: &str = "undefined";
const FULLTREE: &str = "full_tree";
const STATUS: &str = "status";
const BLACKBOARD: &str = "blackboard";
const HOOK_INSERT: &str = "hook_insert";
const HOOK_REMOVE: &str = "hook_remove";
const BREAKPOINT_REACHED: &str = "breakpoint_reached";
const BREAKPOINT_UNLOCK: &str = "breakpoint_unlock";
const REMOVE_ALL_HOOKS: &str = "hooks_remove_all";
const HOOKS_DUMP: &str = "hooks_dump";
const DISABLE_ALL_HOOKS: &str = "disable_hooks";
const TOGGLE_RECORDING: &str = "toggle_recording";
const GET_TRANSITIONS: &str = "get_transitions";
impl Display for Groot2RequestType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let text = match self {
Self::State => STATUS,
Self::GetTransitions => GET_TRANSITIONS,
Self::FullTree => FULLTREE,
Self::Undefined => UNDEFINED,
Self::BlackBoard => BLACKBOARD,
Self::HookInsert => HOOK_INSERT,
Self::HookRemove => HOOK_REMOVE,
Self::HooksDump => HOOKS_DUMP,
Self::RemoveAllHooks => REMOVE_ALL_HOOKS,
Self::DisableAllHooks => DISABLE_ALL_HOOKS,
Self::BreakpointReached => BREAKPOINT_REACHED,
Self::BreakpointUnlock => BREAKPOINT_UNLOCK,
Self::ToggleRecording => TOGGLE_RECORDING,
};
write!(f, "{text}")
}
}
impl From<&Groot2RequestType> for u8 {
fn from(value: &Groot2RequestType) -> Self {
*value as Self
}
}
impl TryFrom<u8> for Groot2RequestType {
type Error = crate::tree::error::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
b'S' => Ok(Self::State),
b't' => Ok(Self::GetTransitions),
b'T' => Ok(Self::FullTree),
b'B' => Ok(Self::BlackBoard),
b'I' => Ok(Self::HookInsert),
b'R' => Ok(Self::HookRemove),
b'D' => Ok(Self::HooksDump),
b'A' => Ok(Self::RemoveAllHooks),
b'X' => Ok(Self::DisableAllHooks),
b'N' => Ok(Self::BreakpointReached),
b'U' => Ok(Self::BreakpointUnlock),
b'r' => Ok(Self::ToggleRecording),
_ => Err(Self::Error::InvalidRequestType { value }),
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct Groot2RequestHeader {
protocol_id: u8,
rq_type: Groot2RequestType,
uid: [u8; 4],
}
impl From<&Groot2RequestHeader> for Bytes {
fn from(value: &Groot2RequestHeader) -> Self {
let mut bytes = BytesMut::zeroed(6);
bytes[0] = value.protocol_id;
bytes[1] = u8::from(&value.rq_type);
bytes[2] = value.uid[0];
bytes[3] = value.uid[1];
bytes[4] = value.uid[2];
bytes[5] = value.uid[3];
bytes.into()
}
}
impl TryFrom<&Bytes> for Groot2RequestHeader {
type Error = crate::tree::error::Error;
fn try_from(value: &Bytes) -> Result<Self, Self::Error> {
let uid: [u8; 4] = [value[2], value[3], value[4], value[5]];
let protocol_id = value[0];
let rq_type = Groot2RequestType::try_from(value[1])?;
Ok(Self {
protocol_id,
rq_type,
uid,
})
}
}
impl Groot2RequestHeader {
pub(crate) const fn rq_type(&self) -> Groot2RequestType {
self.rq_type
}
}
#[derive(Debug)]
#[repr(C)]
pub struct Groot2ReplyHeader {
rq_header: Groot2RequestHeader,
tree_id: Uuid,
}
impl From<&Groot2ReplyHeader> for Bytes {
fn from(value: &Groot2ReplyHeader) -> Self {
let mut bytes = BytesMut::with_capacity(22);
bytes.extend(Self::from(&value.rq_header));
bytes.extend(value.tree_id.as_bytes());
bytes.into()
}
}
impl Groot2ReplyHeader {
#[must_use]
pub(crate) const fn new(rq_header: Groot2RequestHeader, tree_id: Uuid) -> Self {
Self { rq_header, tree_id }
}
}
pub struct Groot2TransitionInfo {
timestamp: u64,
uid: u16,
state: u8,
}
impl From<&Groot2TransitionInfo> for Bytes {
fn from(value: &Groot2TransitionInfo) -> Self {
let mut bytes = BytesMut::with_capacity(9);
let timestamp = value.timestamp.to_le_bytes();
bytes.extend_from_slice(×tamp[..6]);
let uid = value.uid.to_ne_bytes();
bytes.extend_from_slice(&uid[..]);
bytes.put_u8(value.state);
bytes.into()
}
}
impl Groot2TransitionInfo {
#[must_use]
pub const fn new(timestamp: u64, uid: u16, state: BehaviorState) -> Self {
Self {
timestamp,
uid,
state: state as u8,
}
}
}
#[repr(C)]
enum Position {
Pre = 0,
Post = 1,
}
#[repr(C)]
enum Mode {
Breakpoint = 0,
Replace = 1,
}
pub struct Groot2Hook {
enabled: bool,
position: Position,
uid: u16,
mode: Mode,
wakeup: ConditionVariable,
mutex: Mutex<u32>,
ready: bool,
remove_when_done: bool,
desired_status: BehaviorState,
}
pub struct ConditionVariable {}
#[cfg(test)]
mod tests {
#![allow(missing_docs)]
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn de_serialization() {
let header = Groot2RequestHeader {
uid: [1, 2, 3, 4],
protocol_id: PROTOCOL_ID,
rq_type: Groot2RequestType::FullTree,
};
let bytes = Bytes::from(&header);
let deserialized = Groot2RequestHeader::try_from(&bytes).unwrap();
assert_eq!(deserialized.protocol_id, header.protocol_id);
assert_eq!(deserialized.rq_type, header.rq_type);
assert_eq!(deserialized.uid, header.uid);
}
}