use feagi_structures::FeagiDataError;
use serde::{Deserialize, Serialize};
use std::fmt;
pub const AUTH_TOKEN_LENGTH: usize = 32;
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AuthToken {
value: [u8; AUTH_TOKEN_LENGTH],
}
impl AuthToken {
pub fn new(value: [u8; AUTH_TOKEN_LENGTH]) -> Self {
Self { value }
}
pub fn from_base64(b64: &str) -> Option<Self> {
use base64::Engine;
let decoded = base64::engine::general_purpose::STANDARD.decode(b64).ok()?;
if decoded.len() != AUTH_TOKEN_LENGTH {
return None;
}
let mut value = [0u8; AUTH_TOKEN_LENGTH];
value.copy_from_slice(&decoded);
Some(Self { value })
}
pub fn as_bytes(&self) -> &[u8; AUTH_TOKEN_LENGTH] {
&self.value
}
pub fn to_base64(&self) -> String {
use base64::Engine;
base64::engine::general_purpose::STANDARD.encode(self.value)
}
}
impl fmt::Debug for AuthToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AuthToken")
.field("value", &"[REDACTED]")
.finish()
}
}
impl fmt::Display for AuthToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let base64 = self.to_base64();
write!(f, "{}...{}", &base64[..4], &base64[base64.len() - 4..])
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub enum UnauthenticatedAgentRejectionBehavior {
#[default]
Ignore,
Error,
Log,
AllowAnyways,
}
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AgentCapabilities {
SendSensorData,
ReceiveMotorData,
ReceiveNeuronVisualizations,
ReceiveSystemMessages,
}
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FeagiApiVersion {
version: u64,
}
impl FeagiApiVersion {
pub const fn get_current_api_version() -> Self {
Self { version: 1 } }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AgentDescriptor {
manufacturer: String,
agent_name: String,
agent_version: u32,
}
impl AgentDescriptor {
pub const MAX_MANUFACTURER_NAME_BYTE_COUNT: usize = 128;
pub const MAX_AGENT_NAME_BYTE_COUNT: usize = 64;
pub const SIZE_BYTES: usize =
4 + Self::MAX_MANUFACTURER_NAME_BYTE_COUNT + Self::MAX_AGENT_NAME_BYTE_COUNT + 4;
pub fn new(
manufacturer: &str,
agent_name: &str,
agent_version: u32,
) -> Result<Self, FeagiDataError> {
Self::validate(manufacturer, agent_name, agent_version)?;
Ok(AgentDescriptor {
manufacturer: manufacturer.to_string(),
agent_name: agent_name.to_string(),
agent_version,
})
}
pub fn manufacturer(&self) -> &str {
&self.manufacturer
}
pub fn agent_name(&self) -> &str {
&self.agent_name
}
pub fn agent_version(&self) -> u32 {
self.agent_version
}
fn validate(
manufacturer: &str,
agent_name: &str,
agent_version: u32,
) -> Result<(), FeagiDataError> {
if !manufacturer.is_ascii() {
return Err(FeagiDataError::BadParameters(
"Manufacturer must contain ASCII characters only!".to_string(),
));
}
if !agent_name.is_ascii() {
return Err(FeagiDataError::BadParameters(
"Agent name must contain ASCII characters only!".to_string(),
));
}
if manufacturer.len() > Self::MAX_MANUFACTURER_NAME_BYTE_COUNT {
return Err(FeagiDataError::BadParameters(format!(
"Manufacturer is too long! Max length is {} bytes, got {}",
Self::MAX_MANUFACTURER_NAME_BYTE_COUNT,
manufacturer.len()
)));
}
if agent_name.len() > Self::MAX_AGENT_NAME_BYTE_COUNT {
return Err(FeagiDataError::BadParameters(format!(
"Agent name is too long! Max length is {} bytes, got {}",
Self::MAX_AGENT_NAME_BYTE_COUNT,
agent_name.len()
)));
}
if agent_version == 0 {
return Err(FeagiDataError::BadParameters(
"Agent version cannot be zero!".to_string(),
));
}
Ok(())
}
}