@@ -68,15 +68,21 @@ pub const MAX_SEGMENT_LENGTH: usize = 64;
6868pub const MAX_NAMESPACE_SEGMENTS : usize = 10 ;
6969
7070pub const NAMESPACE_SEPARATOR : u8 = b'.' ;
71+ pub const NAMESPACE_AGENT_PREFIX : & [ u8 ] = b"agent." ;
7172
7273pub type NamespacePathInner = BoundedVec < u8 , ConstU32 < { MAX_NAMESPACE_PATH_LENGTH as u32 } > > ;
7374
7475#[ derive( Encode , Decode , Clone , PartialEq , Eq , PartialOrd , Ord , TypeInfo , MaxEncodedLen ) ]
7576pub struct NamespacePath ( NamespacePathInner ) ;
7677
7778impl NamespacePath {
79+ /// The root agent namespace entry.
80+ pub fn agent_root ( ) -> NamespacePath {
81+ NamespacePath ( b"agent" . to_vec ( ) . try_into ( ) . unwrap ( ) )
82+ }
83+
7884 /// Create a new namespace path from bytes with validation
79- pub fn new ( bytes : & [ u8 ] ) -> Result < Self , & ' static str > {
85+ pub fn new_agent ( bytes : & [ u8 ] ) -> Result < Self , & ' static str > {
8086 if bytes. is_empty ( ) {
8187 return Err ( "empty namespace path" ) ;
8288 }
@@ -85,6 +91,10 @@ impl NamespacePath {
8591 return Err ( "path too long" ) ;
8692 }
8793
94+ if !bytes. starts_with ( NAMESPACE_AGENT_PREFIX ) {
95+ return Err ( "path must begin with agent prefix" ) ;
96+ }
97+
8898 let segments: Vec < & [ u8 ] > = bytes. split ( |& b| b == NAMESPACE_SEPARATOR ) . collect ( ) ;
8999 if segments. len ( ) > MAX_NAMESPACE_SEGMENTS {
90100 return Err ( "too many namespace segments" ) ;
@@ -128,10 +138,17 @@ impl NamespacePath {
128138 }
129139
130140 /// Parse a namespace path into segments
131- pub fn segments ( & self ) -> Vec < & [ u8 ] > {
132- self . as_bytes ( )
133- . split ( |& b| b == NAMESPACE_SEPARATOR )
134- . collect ( )
141+ pub fn segments ( & self ) -> impl Iterator < Item = & [ u8 ] > {
142+ self . as_bytes ( ) . split ( |& b| b == NAMESPACE_SEPARATOR )
143+ }
144+
145+ /// Returns the first segment of the path.
146+ pub fn root ( & self ) -> Option < Self > {
147+ let Some ( root) = self . as_bytes ( ) . split ( |& b| b == NAMESPACE_SEPARATOR ) . next ( ) else {
148+ return Some ( self . clone ( ) ) ;
149+ } ;
150+
151+ root. to_vec ( ) . try_into ( ) . ok ( ) . map ( Self )
135152 }
136153
137154 /// Get the parent path of this namespace
@@ -147,7 +164,7 @@ impl NamespacePath {
147164
148165 /// Get the depth of this namespace (number of segments)
149166 pub fn depth ( & self ) -> u32 {
150- self . segments ( ) . len ( ) as u32
167+ self . segments ( ) . count ( ) as u32
151168 }
152169
153170 /// Check if this path is a parent of another path
@@ -194,7 +211,7 @@ impl FromStr for NamespacePath {
194211 type Err = & ' static str ;
195212
196213 fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
197- Self :: new ( s. as_bytes ( ) )
214+ Self :: new_agent ( s. as_bytes ( ) )
198215 }
199216}
200217
@@ -206,54 +223,54 @@ mod tests {
206223
207224 #[ test]
208225 fn namespace_creation_validates_paths ( ) {
209- assert ! ( NamespacePath :: new ( b"agent" ) . is_ok( ) ) ;
210- assert ! ( NamespacePath :: new ( b "agent.alice" ) . is_ok( ) ) ;
211- assert ! ( NamespacePath :: new ( b"agent.alice .memory" ) . is_ok( ) ) ;
212- assert ! ( NamespacePath :: new ( b"agent-1.alice_2.key=val+1" ) . is_ok ( ) ) ;
213-
214- assert ! ( NamespacePath :: new ( b"" ) . is_err( ) ) ;
215- assert ! ( NamespacePath :: new ( b".agent" ) . is_err( ) ) ;
216- assert ! ( NamespacePath :: new ( b"agent." ) . is_err( ) ) ;
217- assert ! ( NamespacePath :: new ( b"agent..alice" ) . is_err( ) ) ;
218- assert ! ( NamespacePath :: new ( b"agent.-alice" ) . is_err( ) ) ;
219- assert ! ( NamespacePath :: new ( b"agent.alice!" ) . is_err( ) ) ;
220- assert ! ( NamespacePath :: new ( b"agent.alice memory" ) . is_err( ) ) ;
226+ assert ! ( NamespacePath :: new_agent ( b"agent.alice " ) . is_ok( ) ) ;
227+ assert ! ( NamespacePath :: new_agent ( "agent.alice.tørûs" . as_bytes ( ) ) . is_ok( ) ) ;
228+ assert ! ( NamespacePath :: new_agent ( b"agent.alice_2 .memory-1.key=val+1 " ) . is_ok( ) ) ;
229+
230+ assert ! ( NamespacePath :: new_agent ( b"" ) . is_err ( ) ) ;
231+ assert ! ( NamespacePath :: new_agent ( b"agent " ) . is_err( ) ) ;
232+ assert ! ( NamespacePath :: new_agent ( b".agent" ) . is_err( ) ) ;
233+ assert ! ( NamespacePath :: new_agent ( b"agent." ) . is_err( ) ) ;
234+ assert ! ( NamespacePath :: new_agent ( b"agent..alice" ) . is_err( ) ) ;
235+ assert ! ( NamespacePath :: new_agent ( b"agent.-alice" ) . is_err( ) ) ;
236+ assert ! ( NamespacePath :: new_agent ( b"agent.alice!" ) . is_err( ) ) ;
237+ assert ! ( NamespacePath :: new_agent ( b"agent.alice memory" ) . is_err( ) ) ;
221238 }
222239
223240 #[ test]
224241 fn namespace_segment_listing ( ) {
225- let path = NamespacePath :: new ( b"agent.alice.memory" ) . unwrap ( ) ;
226- let segments = path. segments ( ) ;
227- assert_eq ! ( segments. len ( ) , 3 ) ;
228- assert_eq ! ( segments[ 0 ] , b"agent" ) ;
229- assert_eq ! ( segments[ 1 ] , b"alice" ) ;
230- assert_eq ! ( segments[ 2 ] , b"memory" ) ;
242+ let path = NamespacePath :: new_agent ( b"agent.alice.memory" ) . unwrap ( ) ;
243+ let mut segments = path. segments ( ) ;
244+ assert_eq ! ( segments. next ( ) , Some ( b"agent" . as_slice ( ) ) ) ;
245+ assert_eq ! ( segments. next ( ) , Some ( b"alice" . as_slice ( ) ) ) ;
246+ assert_eq ! ( segments. next ( ) , Some ( b"memory" . as_slice ( ) ) ) ;
247+ assert_eq ! ( segments. next ( ) , None ) ;
231248 }
232249
233250 #[ test]
234251 fn namespace_parent_returns_correctly ( ) {
235- let path = NamespacePath :: new ( b"agent.alice.memory" ) . unwrap ( ) ;
252+ let path = NamespacePath :: new_agent ( b"agent.alice.memory" ) . unwrap ( ) ;
236253 let parent = path. parent ( ) . unwrap ( ) ;
237254 assert_eq ! ( parent. as_bytes( ) , b"agent.alice" ) ;
238255
239- let root = NamespacePath :: new ( b"agent" ) . unwrap ( ) ;
256+ let root = NamespacePath :: agent_root ( ) ;
240257 assert ! ( root. parent( ) . is_none( ) ) ;
241258 }
242259
243260 #[ test]
244261 fn namespace_depth_calculation ( ) {
245- let path1 = NamespacePath :: new ( b"agent" ) . unwrap ( ) ;
246- assert_eq ! ( path1. depth( ) , 1 ) ;
262+ let path1 = NamespacePath :: new_agent ( b"agent.alice " ) . unwrap ( ) ;
263+ assert_eq ! ( path1. depth( ) , 2 ) ;
247264
248- let path2 = NamespacePath :: new ( b"agent.alice.memory.twitter" ) . unwrap ( ) ;
265+ let path2 = NamespacePath :: new_agent ( b"agent.alice.memory.twitter" ) . unwrap ( ) ;
249266 assert_eq ! ( path2. depth( ) , 4 ) ;
250267 }
251268
252269 #[ test]
253270 fn test_is_parent_of ( ) {
254- let parent = NamespacePath :: new ( b"agent.alice" ) . unwrap ( ) ;
255- let child = NamespacePath :: new ( b"agent.alice.memory" ) . unwrap ( ) ;
256- let other = NamespacePath :: new ( b"agent.bob" ) . unwrap ( ) ;
271+ let parent = NamespacePath :: new_agent ( b"agent.alice" ) . unwrap ( ) ;
272+ let child = NamespacePath :: new_agent ( b"agent.alice.memory" ) . unwrap ( ) ;
273+ let other = NamespacePath :: new_agent ( b"agent.bob" ) . unwrap ( ) ;
257274
258275 assert ! ( parent. is_parent_of( & child) ) ;
259276 assert ! ( !parent. is_parent_of( & other) ) ;
@@ -262,7 +279,7 @@ mod tests {
262279
263280 #[ test]
264281 fn test_parents ( ) {
265- let path = NamespacePath :: new ( b"agent.alice.memory.twitter" ) . unwrap ( ) ;
282+ let path = NamespacePath :: new_agent ( b"agent.alice.memory.twitter" ) . unwrap ( ) ;
266283 let parents = path. parents ( ) ;
267284 assert_eq ! ( parents. len( ) , 3 ) ;
268285 assert_eq ! ( parents[ 0 ] . as_bytes( ) , b"agent.alice.memory" ) ;
0 commit comments