use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(transparent)]
pub struct Subject(pub u16);
impl Subject {
pub const NULL: Self = Self(0x0000);
pub const SELF: Self = Self(0x0001);
pub const USER: Self = Self(0x0002);
pub const CONTEXT: Self = Self(0x0003);
pub const WEATHER: Self = Self(0x0100);
pub const TIME: Self = Self(0x0101);
pub const DATE: Self = Self(0x0102);
pub const SCHEDULE: Self = Self(0x0103);
pub const HEALTH: Self = Self(0x0104);
pub const HELP: Self = Self(0x0105);
pub const TIMEZONE: Self = Self(0x0106);
pub const NUMBER: Self = Self(0x0200);
pub const EQUATION: Self = Self(0x0201);
pub const PHYSICS: Self = Self(0x0202);
pub const CHEMISTRY: Self = Self(0x0203);
pub const COMPUTER: Self = Self(0x0300);
pub const SOFTWARE: Self = Self(0x0301);
pub const HARDWARE: Self = Self(0x0302);
pub const AI: Self = Self(0x0303);
pub const API: Self = Self(0x0304);
pub const DOCUMENTATION: Self = Self(0x0400);
pub const CONCEPT: Self = Self(0x0401);
pub const FEELINGS: Self = Self(0x0500);
pub const STRESS: Self = Self(0x0501);
pub const ANXIETY: Self = Self(0x0502);
pub const TRM_REF_START: u16 = 0x0600;
pub const TRM_REF_END: u16 = 0x06FF;
pub const RAG_START: u16 = 0xE000;
pub const RAG_END: u16 = 0xEFFF;
#[inline]
pub const fn from_u16(value: u16) -> Self {
Self(value)
}
#[inline]
pub const fn as_u16(&self) -> u16 {
self.0
}
#[inline]
pub const fn category(&self) -> u8 {
(self.0 >> 8) as u8
}
#[inline]
pub const fn subcategory(&self) -> u8 {
self.0 as u8
}
#[inline]
pub const fn is_rag_reference(&self) -> bool {
self.0 >= Self::RAG_START && self.0 <= Self::RAG_END
}
#[inline]
pub const fn is_trm_reference(&self) -> bool {
self.0 >= Self::TRM_REF_START && self.0 <= Self::TRM_REF_END
}
#[inline]
pub const fn is_system(&self) -> bool {
self.0 <= 0x00FF
}
#[inline]
pub const fn is_common_topic(&self) -> bool {
self.0 >= 0x0100 && self.0 <= 0x01FF
}
#[inline]
pub const fn is_math_science(&self) -> bool {
self.0 >= 0x0200 && self.0 <= 0x02FF
}
#[inline]
pub const fn is_technology(&self) -> bool {
self.0 >= 0x0300 && self.0 <= 0x03FF
}
#[inline]
pub const fn is_knowledge(&self) -> bool {
self.0 >= 0x0400 && self.0 <= 0x04FF
}
#[inline]
pub const fn is_emotion(&self) -> bool {
self.0 >= 0x0500 && self.0 <= 0x05FF
}
pub fn name(&self) -> &'static str {
match *self {
Self::NULL => "NULL",
Self::SELF => "SELF",
Self::USER => "USER",
Self::CONTEXT => "CONTEXT",
Self::WEATHER => "WEATHER",
Self::TIME => "TIME",
Self::DATE => "DATE",
Self::SCHEDULE => "SCHEDULE",
Self::HEALTH => "HEALTH",
Self::HELP => "HELP",
Self::TIMEZONE => "TIMEZONE",
Self::NUMBER => "NUMBER",
Self::EQUATION => "EQUATION",
Self::PHYSICS => "PHYSICS",
Self::CHEMISTRY => "CHEMISTRY",
Self::COMPUTER => "COMPUTER",
Self::SOFTWARE => "SOFTWARE",
Self::HARDWARE => "HARDWARE",
Self::AI => "AI",
Self::API => "API",
Self::DOCUMENTATION => "DOCUMENTATION",
Self::CONCEPT => "CONCEPT",
Self::FEELINGS => "FEELINGS",
Self::STRESS => "STRESS",
Self::ANXIETY => "ANXIETY",
_ if self.is_rag_reference() => "RAG_REF",
_ if self.is_trm_reference() => "TRM_REF",
_ => "UNKNOWN",
}
}
#[inline]
pub const fn rag_ref(doc_id: u16) -> Self {
let id = if doc_id > 0x0FFF { 0x0FFF } else { doc_id };
Self(Self::RAG_START + id)
}
#[inline]
pub const fn trm_ref(model_id: u8) -> Self {
Self(Self::TRM_REF_START + model_id as u16)
}
#[inline]
pub const fn rag_doc_id(&self) -> Option<u16> {
if self.is_rag_reference() {
Some(self.0 - Self::RAG_START)
} else {
None
}
}
#[inline]
pub const fn trm_model_id(&self) -> Option<u8> {
if self.is_trm_reference() {
Some((self.0 - Self::TRM_REF_START) as u8)
} else {
None
}
}
}
impl fmt::Display for Subject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_rag_reference() {
write!(f, "SUBJ(RAG:0x{:04X})", self.0)
} else if self.is_trm_reference() {
write!(f, "SUBJ(TRM:0x{:02X})", self.trm_model_id().unwrap())
} else {
write!(f, "SUBJ(0x{:04X}:{})", self.0, self.name())
}
}
}
impl From<u16> for Subject {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<Subject> for u16 {
fn from(subject: Subject) -> Self {
subject.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_subject_categories() {
assert!(Subject::NULL.is_system());
assert!(Subject::WEATHER.is_common_topic());
assert!(Subject::TIME.is_common_topic());
assert!(Subject::NUMBER.is_math_science());
assert!(Subject::API.is_technology());
assert!(Subject::DOCUMENTATION.is_knowledge());
assert!(Subject::STRESS.is_emotion());
}
#[test]
fn test_rag_reference() {
let rag_ref = Subject::rag_ref(0x0A3);
assert!(rag_ref.is_rag_reference());
assert_eq!(rag_ref.rag_doc_id(), Some(0x0A3));
assert_eq!(rag_ref.as_u16(), 0xE0A3);
assert!(!Subject::USER.is_rag_reference());
assert_eq!(Subject::USER.rag_doc_id(), None);
}
#[test]
fn test_trm_reference() {
let trm_ref = Subject::trm_ref(5);
assert!(trm_ref.is_trm_reference());
assert_eq!(trm_ref.trm_model_id(), Some(5));
assert_eq!(trm_ref.as_u16(), 0x0605);
assert!(!Subject::USER.is_trm_reference());
}
#[test]
fn test_serialization() {
let subject = Subject::TIME;
let json = serde_json::to_string(&subject).unwrap();
let deserialized: Subject = serde_json::from_str(&json).unwrap();
assert_eq!(subject, deserialized);
}
}