bc_ur/lib.rs
1#![doc(html_root_url = "https://docs.rs/bc-ur/0.12.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.11.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 bc_ur::prelude::*;
52//! use dcbor::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 bc_ur::prelude::*;
67//! use dcbor::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
126 use crate::*;
127
128 #[test]
129 fn encode() {
130 let cbor: CBOR = vec![1, 2, 3].into();
131 let ur = UR::new("test", cbor).unwrap();
132 let ur_string = ur.string();
133 assert_eq!(ur_string, "ur:test/lsadaoaxjygonesw");
134 }
135
136 #[test]
137 fn decode() {
138 let ur_string = "ur:test/lsadaoaxjygonesw";
139 let ur = UR::from_ur_string(ur_string).unwrap();
140 assert_eq!(ur.ur_type_str(), "test");
141 assert_eq!(
142 <UR as Into<CBOR>>::into(ur),
143 <Vec<i32> as Into<CBOR>>::into(vec![1, 2, 3])
144 );
145 }
146
147 fn run_fountain_test(start_part: usize) -> usize {
148 let message = "The only thing we have to fear is fear itself.";
149 let cbor = CBOR::to_byte_string(message.as_bytes());
150 let ur = UR::new("bytes", cbor).unwrap();
151
152 let mut encoder = MultipartEncoder::new(&ur, 10).unwrap();
153 let mut decoder = MultipartDecoder::new();
154 for _ in 0..1000 {
155 let part = encoder.next_part().unwrap();
156 if encoder.current_index() >= start_part {
157 // println!("{}", part);
158 decoder.receive(&part).unwrap();
159 }
160 if decoder.is_complete() {
161 break;
162 }
163 }
164 let received_ur = decoder.message().unwrap().unwrap();
165 assert_eq!(received_ur, ur);
166 encoder.current_index()
167 }
168
169 #[test]
170 fn test_fountain() {
171 assert_eq!(run_fountain_test(1), 5);
172 assert_eq!(run_fountain_test(51), 61);
173 assert_eq!(run_fountain_test(101), 110);
174 assert_eq!(run_fountain_test(501), 507);
175 }
176}