systemprompt_models/brain/
subject.rs1use std::fmt;
13use std::str::FromStr;
14
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17
18pub const MAX_SUBJECT_KIND_LEN: usize = 64;
22pub const MAX_SUBJECT_ID_LEN: usize = 255;
23
24#[derive(
27 Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
28)]
29#[serde(deny_unknown_fields)]
30pub struct SubjectKey {
31 pub kind: String,
32 pub id: String,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
37pub enum SubjectKeyError {
38 #[error("invalid subject key {0:?}: expected `<kind>:<id>`")]
39 Shape(String),
40 #[error(
41 "subject kind is {len} characters; between 1 and {} are allowed",
42 MAX_SUBJECT_KIND_LEN
43 )]
44 KindLength { len: usize },
45 #[error(
46 "subject id is {len} characters; between 1 and {} are allowed",
47 MAX_SUBJECT_ID_LEN
48 )]
49 IdLength { len: usize },
50}
51
52impl SubjectKey {
53 pub fn new(kind: impl Into<String>, id: impl Into<String>) -> Self {
54 Self {
55 kind: kind.into(),
56 id: id.into(),
57 }
58 }
59
60 pub fn try_new(
61 kind: impl Into<String>,
62 id: impl Into<String>,
63 ) -> Result<Self, SubjectKeyError> {
64 let key = Self::new(kind, id);
65 key.validate()?;
66 Ok(key)
67 }
68
69 pub fn validate(&self) -> Result<(), SubjectKeyError> {
70 let kind = self.kind.chars().count();
71 if !(1..=MAX_SUBJECT_KIND_LEN).contains(&kind) {
72 return Err(SubjectKeyError::KindLength { len: kind });
73 }
74 let id = self.id.chars().count();
75 if !(1..=MAX_SUBJECT_ID_LEN).contains(&id) {
76 return Err(SubjectKeyError::IdLength { len: id });
77 }
78 Ok(())
79 }
80}
81
82impl fmt::Display for SubjectKey {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "{}:{}", self.kind, self.id)
85 }
86}
87
88impl FromStr for SubjectKey {
89 type Err = SubjectKeyError;
90
91 fn from_str(s: &str) -> Result<Self, Self::Err> {
92 match s.split_once(':') {
93 Some((kind, id)) if !kind.is_empty() && !id.is_empty() => Self::try_new(kind, id),
94 _ => Err(SubjectKeyError::Shape(s.to_owned())),
95 }
96 }
97}