bc_ur/lib.rs
1#![doc(html_root_url = "https://docs.rs/bc-ur/0.9.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.9.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;
80
81pub mod bytewords;
82
83mod ur_type;
84pub use ur_type::URType;
85
86mod error;
87pub use error::{ Error, Result };
88
89mod utils;
90pub(crate) use utils::*;
91
92mod ur_encodable;
93pub use ur_encodable::UREncodable;
94
95mod ur_decodable;
96pub use ur_decodable::URDecodable;
97
98mod ur_codable;
99pub use ur_codable::URCodable;
100
101mod multipart_decoder;
102pub use multipart_decoder::MultipartDecoder;
103
104mod multipart_encoder;
105pub use multipart_encoder::MultipartEncoder;
106
107pub mod prelude;
108
109#[cfg(test)]
110mod tests {
111 #[test]
112 fn test_readme_deps() {
113 version_sync::assert_markdown_deps_updated!("README.md");
114 }
115
116 #[test]
117 fn test_html_root_url() {
118 version_sync::assert_html_root_url_updated!("src/lib.rs");
119 }
120}
121
122#[cfg(test)]
123mod example_tests {
124 use dcbor::prelude::*;
125 use crate::*;
126
127 #[test]
128 fn encode() {
129 let cbor: CBOR = vec![1, 2, 3].into();
130 let ur = UR::new("test", cbor).unwrap();
131 let ur_string = ur.string();
132 assert_eq!(ur_string, "ur:test/lsadaoaxjygonesw");
133 }
134
135 #[test]
136 fn decode() {
137 let ur_string = "ur:test/lsadaoaxjygonesw";
138 let ur = UR::from_ur_string(ur_string).unwrap();
139 assert_eq!(ur.ur_type_str(), "test");
140 assert_eq!(<UR as Into<CBOR>>::into(ur), <Vec<i32> as Into<CBOR>>::into(vec![1, 2, 3]));
141 }
142
143 fn run_fountain_test(start_part: usize) -> usize {
144 let message = "The only thing we have to fear is fear itself.";
145 let cbor = CBOR::to_byte_string(message.as_bytes());
146 let ur = UR::new("bytes", cbor).unwrap();
147
148 let mut encoder = MultipartEncoder::new(&ur, 10).unwrap();
149 let mut decoder = MultipartDecoder::new();
150 for _ in 0..1000 {
151 let part = encoder.next_part().unwrap();
152 if encoder.current_index() >= start_part {
153 // println!("{}", part);
154 decoder.receive(&part).unwrap();
155 }
156 if decoder.is_complete() {
157 break;
158 }
159 }
160 let received_ur = decoder.message().unwrap().unwrap();
161 assert_eq!(received_ur, ur);
162 encoder.current_index()
163 }
164
165 #[test]
166 fn test_fountain() {
167 assert_eq!(run_fountain_test(1), 5);
168 assert_eq!(run_fountain_test(51), 61);
169 assert_eq!(run_fountain_test(101), 110);
170 assert_eq!(run_fountain_test(501), 507);
171 }
172}