@@ -7,32 +7,39 @@ use std::{fmt::Display, ops::Deref, str::FromStr};
77use cbork_utils:: { decode_context:: DecodeCtx , map:: Map } ;
88use minicbor:: { Decode , Decoder , Encode } ;
99
10+ use crate :: cid_v1:: { Cid , CidError } ;
1011use crate :: metadata:: document_refs:: DocRefError ;
1112
12- /// CBOR tag of IPLD content identifiers (CIDs).
13- const CID_TAG : u64 = 42 ;
14-
1513/// CID map key.
1614const CID_MAP_KEY : & str = "cid" ;
1715
1816/// Document locator number of map item.
1917const DOC_LOC_MAP_ITEM : u64 = 1 ;
2018
21- /// Document locator, no size limit .
22- #[ derive( Clone , Debug , Default , PartialEq , Hash , Eq ) ]
23- pub struct DocLocator ( Vec < u8 > ) ;
19+ /// Document locator wrapping a CID (Content Identifier) .
20+ #[ derive( Clone , Debug , PartialEq , Hash , Eq ) ]
21+ pub struct DocLocator ( Cid ) ;
2422
2523impl Deref for DocLocator {
26- type Target = Vec < u8 > ;
24+ type Target = Cid ;
2725
2826 fn deref ( & self ) -> & Self :: Target {
2927 & self . 0
3028 }
3129}
3230
33- impl From < Vec < u8 > > for DocLocator {
34- fn from ( value : Vec < u8 > ) -> Self {
35- Self ( value)
31+ impl From < Cid > for DocLocator {
32+ fn from ( cid : Cid ) -> Self {
33+ Self ( cid)
34+ }
35+ }
36+
37+ impl TryFrom < & [ u8 ] > for DocLocator {
38+ type Error = CidError ;
39+
40+ fn try_from ( bytes : & [ u8 ] ) -> Result < Self , Self :: Error > {
41+ let cid = Cid :: try_from ( bytes) ?;
42+ Ok ( Self ( cid) )
3643 }
3744}
3845
@@ -41,19 +48,17 @@ impl Display for DocLocator {
4148 & self ,
4249 f : & mut std:: fmt:: Formatter < ' _ > ,
4350 ) -> std:: fmt:: Result {
44- write ! ( f, "0x {}" , hex :: encode ( self . 0 . as_slice ( ) ) )
51+ write ! ( f, "{}" , self . 0 )
4552 }
4653}
4754
4855impl FromStr for DocLocator {
4956 type Err = DocRefError ;
5057
5158 fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
52- s. strip_prefix ( "0x" )
53- . map ( hex:: decode)
54- . ok_or ( DocRefError :: HexDecode ( "missing 0x prefix" . to_string ( ) ) ) ?
55- . map ( Self )
56- . map_err ( |e| DocRefError :: HexDecode ( e. to_string ( ) ) )
59+ let cid = Cid :: from_str ( s)
60+ . map_err ( |e| DocRefError :: StringConversion ( e. to_string ( ) ) ) ?;
61+ Ok ( Self ( cid) )
5762 }
5863}
5964
@@ -77,7 +82,7 @@ impl serde::Serialize for DocLocator {
7782 }
7883}
7984
80- // document_locator = { "cid" => cid }
85+ // document_locator = { "cid" => tag(42)(cid_bytes) }
8186impl Decode < ' _ , ( ) > for DocLocator {
8287 fn decode (
8388 d : & mut Decoder ,
@@ -101,22 +106,14 @@ impl Decode<'_, ()> for DocLocator {
101106
102107 let mut value_decoder = minicbor:: Decoder :: new ( & entry. value ) ;
103108
104- let tag = value_decoder
105- . tag ( )
106- . map_err ( |e| e. with_message ( format ! ( "{CONTEXT}: expected tag" ) ) ) ?;
107-
108- if tag. as_u64 ( ) != CID_TAG {
109- return Err ( minicbor:: decode:: Error :: message ( format ! (
110- "{CONTEXT}: expected tag {CID_TAG}, found {tag}" ,
111- ) ) ) ;
112- }
109+ // Decode the Cid, which validates tag(42) and CID format
110+ let cid = Cid :: decode ( & mut value_decoder, & mut ( ) )
111+ . map_err ( |e| {
112+ let msg = format ! ( "{CONTEXT}: {e}" ) ;
113+ e. with_message ( msg)
114+ } ) ?;
113115
114- // No length limit
115- let cid_bytes = value_decoder
116- . bytes ( )
117- . map_err ( |e| e. with_message ( format ! ( "{CONTEXT}: expected bytes" ) ) ) ?;
118-
119- Ok ( DocLocator ( cid_bytes. to_vec ( ) ) )
116+ Ok ( DocLocator ( cid) )
120117 } ,
121118 _ => {
122119 Err ( minicbor:: decode:: Error :: message ( format ! (
@@ -132,26 +129,49 @@ impl Encode<()> for DocLocator {
132129 fn encode < W : minicbor:: encode:: Write > (
133130 & self ,
134131 e : & mut minicbor:: Encoder < W > ,
135- ( ) : & mut ( ) ,
132+ ctx : & mut ( ) ,
136133 ) -> Result < ( ) , minicbor:: encode:: Error < W :: Error > > {
137134 e. map ( DOC_LOC_MAP_ITEM ) ?;
138135 e. str ( CID_MAP_KEY ) ?;
139- e . tag ( minicbor :: data :: Tag :: new ( CID_TAG ) ) ? ;
140- e . bytes ( & self . 0 ) ?;
136+ // Delegate Cid encoding which handles tag(42) and CID bytes
137+ self . 0 . encode ( e , ctx ) ?;
141138 Ok ( ( ) )
142139 }
143140}
144141
145142#[ cfg( test) ]
146- mod tests {
143+ pub ( crate ) mod tests {
147144
148145 use minicbor:: { Decoder , Encoder } ;
149146
150147 use super :: * ;
148+ use crate :: { Builder , ContentType , UuidV7 } ;
149+
150+ pub ( crate ) fn create_dummy_doc_locator ( ) -> DocLocator {
151+ use crate :: UuidV4 ;
152+
153+ let id = UuidV7 :: new ( ) ;
154+ let ver = UuidV7 :: new ( ) ;
155+ let doc = Builder :: new ( )
156+ . with_json_metadata ( serde_json:: json!( {
157+ "id" : id. to_string( ) ,
158+ "ver" : ver. to_string( ) ,
159+ "type" : UuidV4 :: new( ) . to_string( ) ,
160+ "content-type" : ContentType :: Json ,
161+ } ) )
162+ . expect ( "Should create metadata" )
163+ . with_json_content ( & serde_json:: json!( { "test" : "content" } ) )
164+ . expect ( "Should set content" )
165+ . build ( )
166+ . expect ( "Should build document" ) ;
167+
168+ let cid = doc. to_cid_v1 ( ) . expect ( "Should generate CID" ) ;
169+ DocLocator :: from ( cid)
170+ }
151171
152172 #[ test]
153173 fn test_doc_locator_encode_decode ( ) {
154- let locator = DocLocator ( vec ! [ 1 , 2 , 3 , 4 ] ) ;
174+ let locator = create_dummy_doc_locator ( ) ;
155175 let mut buffer = Vec :: new ( ) ;
156176 let mut encoder = Encoder :: new ( & mut buffer) ;
157177 locator. encode ( & mut encoder, & mut ( ) ) . unwrap ( ) ;
@@ -160,15 +180,43 @@ mod tests {
160180 assert_eq ! ( locator, decoded_doc_loc) ;
161181 }
162182
163- // Empty doc locator should not fail
164183 #[ test]
165- fn test_doc_locator_encode_decode_empty ( ) {
166- let locator = DocLocator ( vec ! [ ] ) ;
167- let mut buffer = Vec :: new ( ) ;
168- let mut encoder = Encoder :: new ( & mut buffer) ;
169- locator. encode ( & mut encoder, & mut ( ) ) . unwrap ( ) ;
170- let mut decoder = Decoder :: new ( & buffer) ;
171- let decoded_doc_loc = DocLocator :: decode ( & mut decoder, & mut ( ) ) . unwrap ( ) ;
172- assert_eq ! ( locator, decoded_doc_loc) ;
184+ fn test_doc_locator_display ( ) {
185+ let locator = create_dummy_doc_locator ( ) ;
186+ let display_str = locator. to_string ( ) ;
187+ assert ! ( display_str. starts_with( 'b' ) , "Should use multibase format starting with 'b'" ) ;
188+ }
189+
190+ #[ test]
191+ fn test_doc_locator_from_str ( ) {
192+ let locator = create_dummy_doc_locator ( ) ;
193+ let display_str = locator. to_string ( ) ;
194+ let parsed = display_str. parse :: < DocLocator > ( ) . expect ( "Should parse multibase string" ) ;
195+ assert_eq ! ( locator, parsed) ;
196+ }
197+
198+ #[ test]
199+ fn test_doc_locator_from_cid ( ) {
200+ use crate :: UuidV4 ;
201+
202+ let id = UuidV7 :: new ( ) ;
203+ let ver = UuidV7 :: new ( ) ;
204+ let doc = Builder :: new ( )
205+ . with_json_metadata ( serde_json:: json!( {
206+ "id" : id. to_string( ) ,
207+ "ver" : ver. to_string( ) ,
208+ "type" : UuidV4 :: new( ) . to_string( ) ,
209+ "content-type" : ContentType :: Json ,
210+ } ) )
211+ . expect ( "Should create metadata" )
212+ . with_json_content ( & serde_json:: json!( { "test" : "content" } ) )
213+ . expect ( "Should set content" )
214+ . build ( )
215+ . expect ( "Should build document" ) ;
216+
217+ let cid = doc. to_cid_v1 ( ) . expect ( "Should generate CID" ) ;
218+ let locator = DocLocator :: from ( cid) ;
219+
220+ assert_eq ! ( & * locator, & cid) ;
173221 }
174222}
0 commit comments