11//! Utility functions for CIP-0134 address.
22
3+ // Ignore URIs that are used in tests and doc-examples.
4+ // cSpell:ignoreRegExp web\+cardano:.+
5+
6+ use std:: fmt:: { Display , Formatter } ;
7+
38use anyhow:: { anyhow, Context , Result } ;
49use pallas:: ledger:: addresses:: Address ;
510
6- /// Parses CIP-0134 URI and returns an address.
7- ///
8- /// # Errors
9- /// - Invalid URI.
11+ /// An URI in the CIP-0134 format.
1012///
11- /// # Examples
13+ /// See the [proposal] for more details.
1214///
13- /// ```
14- /// use pallas::ledger::addresses::{Address, Network};
15- /// use rbac_registration::cardano::cip509::utils::parse_cip0134_uri;
16- ///
17- /// let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
18- /// let Address::Stake(address) = parse_cip0134_uri(uri).unwrap() else {
19- /// panic!("Unexpected address type");
20- /// };
21- /// assert_eq!(address.network(), Network::Mainnet);
22- /// ```
23- pub fn parse_cip0134_uri ( uri : & str ) -> Result < Address > {
24- let bech32 = uri
25- . strip_prefix ( "web+cardano://addr/" )
26- . ok_or_else ( || anyhow ! ( "Missing schema part of URI" ) ) ?;
27- Address :: from_bech32 ( bech32) . context ( "Unable to parse bech32 part of URI" )
15+ /// [proposal]: https://github.com/cardano-foundation/CIPs/pull/888
16+ #[ derive( Debug ) ]
17+ pub struct Cip0134Uri {
18+ /// A URI string.
19+ uri : String ,
20+ /// An address parsed from the URI.
21+ address : Address ,
22+ }
23+
24+ impl Cip0134Uri {
25+ /// Creates a new `Cip0134Uri` instance by parsing the given URI.
26+ ///
27+ /// # Errors
28+ /// - Invalid URI.
29+ ///
30+ /// # Examples
31+ ///
32+ /// ```
33+ /// use rbac_registration::cardano::cip509::utils::Cip0134Uri;
34+ ///
35+ /// let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
36+ /// let cip0134_uri = Cip0134Uri::parse(uri).unwrap();
37+ /// ```
38+ pub fn parse ( uri : & str ) -> Result < Self > {
39+ let bech32 = uri
40+ . strip_prefix ( "web+cardano://addr/" )
41+ . ok_or_else ( || anyhow ! ( "Missing schema part of URI" ) ) ?;
42+ let address = Address :: from_bech32 ( bech32) . context ( "Unable to parse bech32 part of URI" ) ?;
43+
44+ Ok ( Self {
45+ uri : uri. to_owned ( ) ,
46+ address,
47+ } )
48+ }
49+
50+ /// Returns a URI string.
51+ ///
52+ /// # Examples
53+ ///
54+ /// ```
55+ /// use rbac_registration::cardano::cip509::utils::Cip0134Uri;
56+ ///
57+ /// let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
58+ /// let cip0134_uri = Cip0134Uri::parse(uri).unwrap();
59+ /// assert_eq!(cip0134_uri.uri(), uri);
60+ #[ must_use]
61+ pub fn uri ( & self ) -> & str {
62+ & self . uri
63+ }
64+
65+ /// Returns a URI string.
66+ ///
67+ /// # Examples
68+ ///
69+ /// ```
70+ /// use pallas::ledger::addresses::{Address, Network};
71+ /// use rbac_registration::cardano::cip509::utils::Cip0134Uri;
72+ ///
73+ /// let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
74+ /// let cip0134_uri = Cip0134Uri::parse(uri).unwrap();
75+ /// let Address::Stake(address) = cip0134_uri.address() else {
76+ /// panic!("Unexpected address type");
77+ /// };
78+ /// assert_eq!(address.network(), Network::Mainnet);
79+ #[ must_use]
80+ pub fn address ( & self ) -> & Address {
81+ & self . address
82+ }
83+ }
84+
85+ impl Display for Cip0134Uri {
86+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
87+ write ! ( f, "{}" , self . uri( ) )
88+ }
2889}
2990
3091#[ cfg( test) ]
@@ -35,23 +96,25 @@ mod tests {
3596
3697 #[ test]
3798 fn invalid_prefix ( ) {
99+ // cSpell:disable
38100 let test_uris = [
39101 "addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x" ,
40102 "//addr/addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x" ,
41103 "web+cardano:/addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x" ,
42104 "somthing+unexpected://addr/addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x" ,
43105 ] ;
106+ // cSpell:enable
44107
45108 for uri in test_uris {
46- let err = format ! ( "{:?}" , parse_cip0134_uri ( uri) . expect_err( & format! ( "{ uri}" ) ) ) ;
109+ let err = format ! ( "{:?}" , Cip0134Uri :: parse ( uri) . expect_err( uri) ) ;
47110 assert_eq ! ( "Missing schema part of URI" , err) ;
48111 }
49112 }
50113
51114 #[ test]
52115 fn invalid_bech32 ( ) {
53116 let uri = "web+cardano://addr/adr1qx2fxv2umyh" ;
54- let err = format ! ( "{:?}" , parse_cip0134_uri ( uri) . unwrap_err( ) ) ;
117+ let err = format ! ( "{:?}" , Cip0134Uri :: parse ( uri) . unwrap_err( ) ) ;
55118 assert ! ( err. starts_with( "Unable to parse bech32 part of URI" ) ) ;
56119 }
57120
@@ -76,7 +139,8 @@ mod tests {
76139 ] ;
77140
78141 for ( uri, network, payload) in test_data {
79- let Address :: Stake ( address) = parse_cip0134_uri ( uri) . unwrap ( ) else {
142+ let cip0134_uri = Cip0134Uri :: parse ( uri) . expect ( uri) ;
143+ let Address :: Stake ( address) = cip0134_uri. address else {
80144 panic ! ( "Unexpected address type ({uri})" ) ;
81145 } ;
82146 assert_eq ! ( network, address. network( ) ) ;
@@ -102,10 +166,19 @@ mod tests {
102166 ] ;
103167
104168 for ( uri, network) in test_data {
105- let Address :: Shelley ( address) = parse_cip0134_uri ( uri) . unwrap ( ) else {
169+ let cip0134_uri = Cip0134Uri :: parse ( uri) . expect ( uri) ;
170+ let Address :: Shelley ( address) = cip0134_uri. address else {
106171 panic ! ( "Unexpected address type ({uri})" ) ;
107172 } ;
108173 assert_eq ! ( network, address. network( ) ) ;
109174 }
110175 }
176+
177+ // The Display should return the original URI.
178+ #[ test]
179+ fn display ( ) {
180+ let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw" ;
181+ let cip0134_uri = Cip0134Uri :: parse ( uri) . expect ( uri) ;
182+ assert_eq ! ( uri, cip0134_uri. to_string( ) ) ;
183+ }
111184}
0 commit comments