@@ -62,21 +62,28 @@ pub trait Torus0Api<AccountId, Balance> {
6262/// This might have to increase in the future, but is a good enough default value.
6363/// If it ends up being formalized, the length can be described as a u8.
6464pub const MAX_NAMESPACE_PATH_LENGTH : usize = 256 ;
65- /// Number of total bytes a segment can contain.
66- pub const MAX_SEGMENT_LENGTH : usize = 64 ;
67- /// Max number of segments in a path.
65+ /// Number of total bytes a segment can contain. 63 plus dot.
66+ pub const MAX_SEGMENT_LENGTH : usize = 63 ;
67+ /// Max number of segments in a path. In the common prefix case, an agent will have
68+ /// up to 8 levels of depth to use, 2 being allocated to the agent prefix notation.
6869pub const MAX_NAMESPACE_SEGMENTS : usize = 10 ;
6970
7071pub const NAMESPACE_SEPARATOR : u8 = b'.' ;
72+ pub const NAMESPACE_AGENT_PREFIX : & [ u8 ] = b"agent." ;
7173
7274pub type NamespacePathInner = BoundedVec < u8 , ConstU32 < { MAX_NAMESPACE_PATH_LENGTH as u32 } > > ;
7375
7476#[ derive( Encode , Decode , Clone , PartialEq , Eq , PartialOrd , Ord , TypeInfo , MaxEncodedLen ) ]
7577pub struct NamespacePath ( NamespacePathInner ) ;
7678
7779impl NamespacePath {
80+ /// The root agent namespace entry.
81+ pub fn agent_root ( ) -> NamespacePath {
82+ NamespacePath ( b"agent" . to_vec ( ) . try_into ( ) . unwrap ( ) )
83+ }
84+
7885 /// Create a new namespace path from bytes with validation
79- pub fn new ( bytes : & [ u8 ] ) -> Result < Self , & ' static str > {
86+ pub fn new_agent ( bytes : & [ u8 ] ) -> Result < Self , & ' static str > {
8087 if bytes. is_empty ( ) {
8188 return Err ( "empty namespace path" ) ;
8289 }
@@ -85,6 +92,10 @@ impl NamespacePath {
8592 return Err ( "path too long" ) ;
8693 }
8794
95+ if !bytes. starts_with ( NAMESPACE_AGENT_PREFIX ) {
96+ return Err ( "path must begin with agent prefix" ) ;
97+ }
98+
8899 let segments: Vec < & [ u8 ] > = bytes. split ( |& b| b == NAMESPACE_SEPARATOR ) . collect ( ) ;
89100 if segments. len ( ) > MAX_NAMESPACE_SEGMENTS {
90101 return Err ( "too many namespace segments" ) ;
@@ -93,19 +104,19 @@ impl NamespacePath {
93104 for segment in & segments {
94105 let segment = core:: str:: from_utf8 ( segment) . map_err ( |_| "path is invalid itf-8" ) ?;
95106
96- let first = segment. chars ( ) . next ( ) . ok_or ( "empty namespace segment" ) ?;
97- if !first. is_alphanumeric ( ) {
98- return Err ( "namespace segment must start with alphanumeric character" ) ;
99- }
100-
101107 if segment. len ( ) > MAX_SEGMENT_LENGTH {
102108 return Err ( "namespace segment too long" ) ;
103109 }
104110
105- if segment
106- . chars ( )
107- . any ( |c| !c. is_alphanumeric ( ) && c != '-' && c != '_' && c != '+' && c != '=' )
108- {
111+ let first = segment. chars ( ) . next ( ) . ok_or ( "empty namespace segment" ) ?;
112+ let last = segment. chars ( ) . last ( ) . ok_or ( "empty namespace segment" ) ?;
113+ if !first. is_ascii_alphanumeric ( ) || !last. is_ascii_alphanumeric ( ) {
114+ return Err ( "namespace segment must start and end with alphanumeric characters" ) ;
115+ }
116+
117+ if segment. chars ( ) . any ( |c| {
118+ !( c. is_ascii_digit ( ) || c. is_ascii_lowercase ( ) ) && c != '-' && c != '_' && c != '+'
119+ } ) {
109120 return Err ( "invalid character in namespace segment" ) ;
110121 }
111122 }
@@ -128,10 +139,17 @@ impl NamespacePath {
128139 }
129140
130141 /// 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 ( )
142+ pub fn segments ( & self ) -> impl Iterator < Item = & [ u8 ] > {
143+ self . as_bytes ( ) . split ( |& b| b == NAMESPACE_SEPARATOR )
144+ }
145+
146+ /// Returns the first segment of the path.
147+ pub fn root ( & self ) -> Option < Self > {
148+ let Some ( root) = self . as_bytes ( ) . split ( |& b| b == NAMESPACE_SEPARATOR ) . next ( ) else {
149+ return Some ( self . clone ( ) ) ;
150+ } ;
151+
152+ root. to_vec ( ) . try_into ( ) . ok ( ) . map ( Self )
135153 }
136154
137155 /// Get the parent path of this namespace
@@ -147,7 +165,7 @@ impl NamespacePath {
147165
148166 /// Get the depth of this namespace (number of segments)
149167 pub fn depth ( & self ) -> u32 {
150- self . segments ( ) . len ( ) as u32
168+ self . segments ( ) . count ( ) as u32
151169 }
152170
153171 /// Check if this path is a parent of another path
@@ -194,7 +212,7 @@ impl FromStr for NamespacePath {
194212 type Err = & ' static str ;
195213
196214 fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
197- Self :: new ( s. as_bytes ( ) )
215+ Self :: new_agent ( s. as_bytes ( ) )
198216 }
199217}
200218
@@ -206,54 +224,60 @@ mod tests {
206224
207225 #[ test]
208226 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( ) ) ;
227+ assert ! ( NamespacePath :: new_agent( b"agent.alice" ) . is_ok( ) ) ;
228+ assert ! ( NamespacePath :: new_agent( b"agent.alice_2.memory-1.func+1" ) . is_ok( ) ) ;
229+
230+ assert ! ( NamespacePath :: new_agent( format!( "agent.alice.{:0<63}" , 1 ) . as_bytes( ) ) . is_ok( ) ) ;
231+ assert ! ( NamespacePath :: new_agent( format!( "agent.alice.{:0<64}" , 1 ) . as_bytes( ) ) . is_err( ) ) ;
232+
233+ assert ! ( NamespacePath :: new_agent( b"" ) . is_err( ) ) ;
234+ assert ! ( NamespacePath :: new_agent( b"agent" ) . is_err( ) ) ;
235+ assert ! ( NamespacePath :: new_agent( b".agent" ) . is_err( ) ) ;
236+ assert ! ( NamespacePath :: new_agent( b"agent." ) . is_err( ) ) ;
237+ assert ! ( NamespacePath :: new_agent( b"agent.Alice" ) . is_err( ) ) ;
238+ assert ! ( NamespacePath :: new_agent( b"agent..alice" ) . is_err( ) ) ;
239+ assert ! ( NamespacePath :: new_agent( b"agent.-alice" ) . is_err( ) ) ;
240+ assert ! ( NamespacePath :: new_agent( b"agent.alice-" ) . is_err( ) ) ;
241+ assert ! ( NamespacePath :: new_agent( b"agent.-alice-" ) . is_err( ) ) ;
242+ assert ! ( NamespacePath :: new_agent( b"agent.alice!" ) . is_err( ) ) ;
243+ assert ! ( NamespacePath :: new_agent( b"agent.alice memory" ) . is_err( ) ) ;
244+ assert ! ( NamespacePath :: new_agent( "agent.alice.tørûs" . as_bytes( ) ) . is_err( ) ) ;
221245 }
222246
223247 #[ test]
224248 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" ) ;
249+ let path = NamespacePath :: new_agent ( b"agent.alice.memory" ) . unwrap ( ) ;
250+ let mut segments = path. segments ( ) ;
251+ assert_eq ! ( segments. next ( ) , Some ( b"agent" . as_slice ( ) ) ) ;
252+ assert_eq ! ( segments. next ( ) , Some ( b"alice" . as_slice ( ) ) ) ;
253+ assert_eq ! ( segments. next ( ) , Some ( b"memory" . as_slice ( ) ) ) ;
254+ assert_eq ! ( segments. next ( ) , None ) ;
231255 }
232256
233257 #[ test]
234258 fn namespace_parent_returns_correctly ( ) {
235- let path = NamespacePath :: new ( b"agent.alice.memory" ) . unwrap ( ) ;
259+ let path = NamespacePath :: new_agent ( b"agent.alice.memory" ) . unwrap ( ) ;
236260 let parent = path. parent ( ) . unwrap ( ) ;
237261 assert_eq ! ( parent. as_bytes( ) , b"agent.alice" ) ;
238262
239- let root = NamespacePath :: new ( b"agent" ) . unwrap ( ) ;
263+ let root = NamespacePath :: agent_root ( ) ;
240264 assert ! ( root. parent( ) . is_none( ) ) ;
241265 }
242266
243267 #[ test]
244268 fn namespace_depth_calculation ( ) {
245- let path1 = NamespacePath :: new ( b"agent" ) . unwrap ( ) ;
246- assert_eq ! ( path1. depth( ) , 1 ) ;
269+ let path1 = NamespacePath :: new_agent ( b"agent.alice " ) . unwrap ( ) ;
270+ assert_eq ! ( path1. depth( ) , 2 ) ;
247271
248- let path2 = NamespacePath :: new ( b"agent.alice.memory.twitter" ) . unwrap ( ) ;
272+ let path2 = NamespacePath :: new_agent ( b"agent.alice.memory.twitter" ) . unwrap ( ) ;
249273 assert_eq ! ( path2. depth( ) , 4 ) ;
250274 }
251275
252276 #[ test]
253277 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 ( ) ;
278+ let parent = NamespacePath :: new_agent ( b"agent.alice" ) . unwrap ( ) ;
279+ let child = NamespacePath :: new_agent ( b"agent.alice.memory" ) . unwrap ( ) ;
280+ let other = NamespacePath :: new_agent ( b"agent.bob" ) . unwrap ( ) ;
257281
258282 assert ! ( parent. is_parent_of( & child) ) ;
259283 assert ! ( !parent. is_parent_of( & other) ) ;
@@ -262,7 +286,7 @@ mod tests {
262286
263287 #[ test]
264288 fn test_parents ( ) {
265- let path = NamespacePath :: new ( b"agent.alice.memory.twitter" ) . unwrap ( ) ;
289+ let path = NamespacePath :: new_agent ( b"agent.alice.memory.twitter" ) . unwrap ( ) ;
266290 let parents = path. parents ( ) ;
267291 assert_eq ! ( parents. len( ) , 3 ) ;
268292 assert_eq ! ( parents[ 0 ] . as_bytes( ) , b"agent.alice.memory" ) ;
0 commit comments