bc_ur/
lib.rs

1#![doc(html_root_url = "https://docs.rs/bc-ur/0.7.0")]
2#![warn(rust_2018_idioms)]
3
4//! # Blockchain Commons Uniform Resources ("UR") for Rust
5//!
6//! [Uniform Resources
7//! (URs)](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-005-ur.md)
8//! are URI-encoded [CBOR](https://cbor.io) structures developed by [Blockchain
9//! Commons](https://blockchaincommons.com). This crate is an opinionated
10//! wrapper around the [ur](https://crates.io/crates/ur) crate by [Dominik
11//! Spicher](https://github.com/dspicher), and is intended primarily for use in
12//! higher-level Blockchain Commmons projects like [Gordian
13//! Envelope](https://crates.io/crates/bc-envelope).
14//!
15//! It is a requirement of the UR specification that the CBOR encoded as URs
16//! conform to Gordian dCBOR, which is a deterministic profile of CBOR currently
17//! specified in [this IETF Internet
18//! Draft](https://datatracker.ietf.org/doc/draft-mcnally-deterministic-cbor/).
19//! The dependency `dcbor` crate can be used directly for that purpose. This
20//! crate provides the traits `UREncodable`, `URDecodable`, and `URCodable` that
21//! are built on traits from the `dcbor` crate such as `CBORTaggedEncodable` and
22//! `CBORTaggedDecodable`. It is strongly recommended that adopters of URs
23//! implement these traits for their types.
24//!
25//! This crate does not currenly provide opinionated affordances for multi-part
26//! URs using fountain codes, but the dependency `ur` crate can be used directly
27//! for that purpose.
28//!
29//! # Getting Started
30//!
31//! Add the following to your `Cargo.toml`:
32//!
33//! ```toml
34//! [dependencies]
35//! bc-ur = "0.7.0"
36//! ```
37//!
38//! # Specification
39//!
40//! The primary specification for URs is [BCR-2020-005:
41//! Uniform Resources](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-005-ur.md)
42//! and the Swift implementation [URKit](https://github.com/BlockchainCommons/URKit).
43//!
44//! # Usage
45//!
46//! Encode a CBOR structure as a UR.
47//!
48//! ```
49//! # fn main() {
50//! # {
51//! use dcbor::prelude::*;
52//! use bc_ur::prelude::*;
53//! let cbor: CBOR = vec![1, 2, 3].into();
54//! let ur = UR::new("test", cbor).unwrap();
55//! let ur_string = ur.string();
56//! assert_eq!(ur_string, "ur:test/lsadaoaxjygonesw");
57//! # }
58//! # }
59//! ```
60//!
61//! Decode a UR back to a CBOR structure.
62//!
63//! ```
64//! # fn main() {
65//! # {
66//! use dcbor::prelude::*;
67//! use bc_ur::prelude::*;
68//! let ur_string = "ur:test/lsadaoaxjygonesw";
69//! let ur = UR::from_ur_string(ur_string).unwrap();
70//! assert_eq!(ur.ur_type_str(), "test");
71//! let ur_cbor = ur.cbor();
72//! let array_cbor: CBOR = vec![1, 2, 3].into();
73//! assert_eq!(ur_cbor, array_cbor);
74//! # }
75//! # }
76//! ```
77
78mod ur;
79pub use ur::UR;
80pub mod bytewords;
81
82mod ur_type;
83pub use ur_type::URType;
84
85mod error;
86pub use error::URError as Error;
87
88mod utils;
89
90mod ur_encodable;
91pub use ur_encodable::UREncodable;
92
93mod ur_decodable;
94pub use ur_decodable::URDecodable;
95
96mod ur_codable;
97pub use ur_codable::URCodable;
98
99mod multipart_decoder;
100pub use multipart_decoder::MultipartDecoder;
101
102mod multipart_encoder;
103pub use multipart_encoder::MultipartEncoder;
104
105pub mod prelude;
106
107#[cfg(test)]
108mod tests {
109    #[test]
110    fn test_readme_deps() {
111        version_sync::assert_markdown_deps_updated!("README.md");
112    }
113
114    #[test]
115    fn test_html_root_url() {
116        version_sync::assert_html_root_url_updated!("src/lib.rs");
117    }
118}
119
120#[cfg(test)]
121mod example_tests {
122    use dcbor::prelude::*;
123    use crate::*;
124
125    #[test]
126    fn encode() {
127        let cbor: CBOR = vec![1, 2, 3].into();
128        let ur = UR::new("test", cbor).unwrap();
129        let ur_string = ur.string();
130        assert_eq!(ur_string, "ur:test/lsadaoaxjygonesw");
131    }
132
133    #[test]
134    fn decode() {
135        let ur_string = "ur:test/lsadaoaxjygonesw";
136        let ur = UR::from_ur_string(ur_string).unwrap();
137        assert_eq!(ur.ur_type_str(), "test");
138        assert_eq!(<UR as Into<CBOR>>::into(ur), <Vec<i32> as Into<CBOR>>::into(vec![1, 2, 3]));
139    }
140
141    fn run_fountain_test(start_part: usize) -> usize {
142        let message = "The only thing we have to fear is fear itself.";
143        let cbor = CBOR::to_byte_string(message.as_bytes());
144        let ur = UR::new("bytes", cbor).unwrap();
145
146        let mut encoder = MultipartEncoder::new(&ur, 10).unwrap();
147        let mut decoder = MultipartDecoder::new();
148        for _ in 0..1000 {
149            let part = encoder.next_part().unwrap();
150            if encoder.current_index() >= start_part {
151                // println!("{}", part);
152                decoder.receive(&part).unwrap();
153            }
154            if decoder.is_complete() {
155                break;
156            }
157        }
158        let received_ur = decoder.message().unwrap().unwrap();
159        assert_eq!(received_ur, ur);
160        encoder.current_index()
161    }
162
163    #[test]
164    fn test_fountain() {
165        assert_eq!(run_fountain_test(1), 5);
166        assert_eq!(run_fountain_test(51), 61);
167        assert_eq!(run_fountain_test(101), 110);
168        assert_eq!(run_fountain_test(501), 507);
169    }
170}