From a2c12dd06ae6d033d1883d7e707a5106ade5d352 Mon Sep 17 00:00:00 2001 From: Jonathan Creekmore Date: Thu, 2 Feb 2023 10:54:27 -0600 Subject: [PATCH] introduce methods to create and access the fields of the Pem --- benches/pem_benchmark.rs | 30 ++++----- src/lib.rs | 138 ++++++++++++++++++--------------------- 2 files changed, 76 insertions(+), 92 deletions(-) diff --git a/benches/pem_benchmark.rs b/benches/pem_benchmark.rs index 90351aa..53134fd 100644 --- a/benches/pem_benchmark.rs +++ b/benches/pem_benchmark.rs @@ -1,8 +1,4 @@ -#[macro_use] -extern crate criterion; -extern crate pem; - -use criterion::Criterion; +use criterion::{criterion_group, criterion_main, Criterion}; const SAMPLE: &str = "-----BEGIN RSA PRIVATE KEY-----\r MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc\r @@ -47,9 +43,9 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function("pem::parse_many", |b| b.iter(pem_parse_many)); // Encode - let pem = pem::Pem { - tag: String::from("RSA PRIVATE KEY"), - contents: vec![ + let pem = pem::Pem::new( + "RSA PRIVATE KEY", + [ 48, 130, 1, 61, 2, 1, 0, 2, 65, 0, 235, 31, 139, 144, 6, 98, 23, 81, 179, 252, 122, 171, 145, 251, 145, 39, 49, 3, 66, 179, 206, 167, 186, 88, 142, 160, 127, 175, 173, 115, 162, 47, 41, 2, 104, 230, 180, 28, 117, 101, 146, 67, 73, 209, 26, 221, 161, 56, @@ -68,13 +64,13 @@ fn criterion_benchmark(c: &mut Criterion) { 176, 5, 147, 135, 242, 20, 63, 206, 123, 56, 112, 24, 118, 214, 154, 143, 210, 249, 183, 222, 2, 34, 33, 217, 43, 54, 23, 18, 137, ], - }; + ); c.bench_function("pem::encode", move |b| b.iter(|| pem_encode(&pem))); let pems = vec![ - pem::Pem { - tag: String::from("RSA PRIVATE KEY"), - contents: vec![ + pem::Pem::new( + "RSA PRIVATE KEY", + vec![ 48, 130, 1, 61, 2, 1, 0, 2, 65, 0, 235, 31, 139, 144, 6, 98, 23, 81, 179, 252, 122, 171, 145, 251, 145, 39, 49, 3, 66, 179, 206, 167, 186, 88, 142, 160, 127, 175, 173, 115, 162, 47, 41, 2, 104, 230, 180, 28, 117, 101, 146, 67, 73, 209, 26, 221, 161, @@ -94,10 +90,10 @@ fn criterion_benchmark(c: &mut Criterion) { 112, 24, 118, 214, 154, 143, 210, 249, 183, 222, 2, 34, 33, 217, 43, 54, 23, 18, 137, ], - }, - pem::Pem { - tag: String::from("RSA PUBLIC KEY"), - contents: vec![ + ), + pem::Pem::new( + "RSA PUBLIC KEY", + vec![ 48, 130, 1, 58, 2, 1, 0, 2, 65, 0, 194, 30, 10, 121, 253, 27, 254, 224, 217, 158, 137, 250, 161, 206, 19, 101, 194, 44, 187, 143, 162, 30, 77, 29, 51, 182, 22, 111, 143, 111, 48, 111, 105, 240, 104, 210, 134, 40, 65, 85, 114, 237, 250, 243, 198, @@ -117,7 +113,7 @@ fn criterion_benchmark(c: &mut Criterion) { 151, 212, 173, 178, 23, 127, 236, 102, 237, 235, 159, 208, 98, 175, 175, 251, 27, 192, 121, 61, 2, 163, 34, 32, ], - }, + ), ]; c.bench_function("pem::encode_many", move |b| { b.iter(|| pem_encode_many(&pems)) diff --git a/src/lib.rs b/src/lib.rs index 71e9019..4bb9b42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ //! "; //! //! let pem = parse(SAMPLE).unwrap(); -//! assert_eq!(pem.tag, "RSA PRIVATE KEY"); +//! assert_eq!(pem.tag(), "RSA PRIVATE KEY"); //! ``` //! //! # Example: parse a set of PEM-encoded test @@ -89,8 +89,8 @@ //! //! let pems = parse_many(SAMPLE).unwrap(); //! assert_eq!(pems.len(), 2); -//! assert_eq!(pems[0].tag, "INTERMEDIATE CERT"); -//! assert_eq!(pems[1].tag, "CERTIFICATE"); +//! assert_eq!(pems[0].tag(), "INTERMEDIATE CERT"); +//! assert_eq!(pems[1].tag(), "CERTIFICATE"); //! ``` #![recursion_limit = "1024"] @@ -137,10 +137,8 @@ pub struct EncodeConfig { /// A representation of Pem-encoded data #[derive(PartialEq, Debug, Clone)] pub struct Pem { - /// The tag extracted from the Pem-encoded data - pub tag: String, - /// The binary contents of the Pem-encoded data - pub contents: Vec, + tag: String, + contents: Vec, } fn decode_data(raw_data: &str) -> Result> { @@ -157,6 +155,29 @@ fn decode_data(raw_data: &str) -> Result> { } impl Pem { + /// Create a new Pem struct + pub fn new(tag: impl ToString, contents: impl Into>) -> Pem { + Pem { + tag: tag.to_string(), + contents: contents.into(), + } + } + + /// Get the tag extracted from the Pem-encoded data + pub fn tag(&self) -> &str { + &self.tag + } + + /// Get the binary contents extracted from the Pem-encoded data + pub fn contents(&self) -> &[u8] { + &self.contents + } + + /// Consume the Pem struct to get an owned copy of the binary contents + pub fn into_contents(self) -> Vec { + self.contents + } + fn new_from_captures(caps: Captures) -> Result { fn as_utf8(bytes: &[u8]) -> Result<&str> { str::from_utf8(bytes).map_err(PemError::NotUtf8) @@ -183,10 +204,7 @@ impl Pem { let raw_data = as_utf8(caps.data)?; let contents = decode_data(raw_data)?; - Ok(Pem { - tag: tag.to_owned(), - contents, - }) + Ok(Pem::new(tag, contents)) } } @@ -232,7 +250,7 @@ impl fmt::Display for Pem { /// let SAMPLE_BYTES: Vec = SAMPLE.into(); /// /// let pem = parse(SAMPLE_BYTES).unwrap(); -/// assert_eq!(pem.tag, "RSA PRIVATE KEY"); +/// assert_eq!(pem.tag(), "RSA PRIVATE KEY"); /// ``` /// /// # Example: parse PEM-encoded data from a String @@ -253,7 +271,7 @@ impl fmt::Display for Pem { /// let SAMPLE_STRING: String = SAMPLE.into(); /// /// let pem = parse(SAMPLE_STRING).unwrap(); -/// assert_eq!(pem.tag, "RSA PRIVATE KEY"); +/// assert_eq!(pem.tag(), "RSA PRIVATE KEY"); /// ``` pub fn parse>(input: B) -> Result { parse_captures(input.as_ref()) @@ -293,8 +311,8 @@ pub fn parse>(input: B) -> Result { /// /// let pems = parse_many(SAMPLE_BYTES).unwrap(); /// assert_eq!(pems.len(), 2); -/// assert_eq!(pems[0].tag, "INTERMEDIATE CERT"); -/// assert_eq!(pems[1].tag, "CERTIFICATE"); +/// assert_eq!(pems[0].tag(), "INTERMEDIATE CERT"); +/// assert_eq!(pems[1].tag(), "CERTIFICATE"); /// ``` /// /// # Example: parse a set of PEM-encoded data from a String @@ -327,8 +345,8 @@ pub fn parse>(input: B) -> Result { /// /// let pems = parse_many(SAMPLE_STRING).unwrap(); /// assert_eq!(pems.len(), 2); -/// assert_eq!(pems[0].tag, "INTERMEDIATE CERT"); -/// assert_eq!(pems[1].tag, "CERTIFICATE"); +/// assert_eq!(pems[0].tag(), "INTERMEDIATE CERT"); +/// assert_eq!(pems[1].tag(), "CERTIFICATE"); /// ``` pub fn parse_many>(input: B) -> Result> { // Each time our regex matches a PEM section, we need to decode it. @@ -343,11 +361,8 @@ pub fn parse_many>(input: B) -> Result> { /// ```rust /// use pem::{Pem, encode}; /// -/// let pem = Pem { -/// tag: String::from("FOO"), -/// contents: vec![1, 2, 3, 4], -/// }; -/// encode(&pem); +/// let pem = Pem::new("FOO", [1, 2, 3, 4]); +/// encode(&pem); /// ``` pub fn encode(pem: &Pem) -> String { encode_config( @@ -365,11 +380,8 @@ pub fn encode(pem: &Pem) -> String { /// ```rust /// use pem::{Pem, encode_config, EncodeConfig, LineEnding}; /// -/// let pem = Pem { -/// tag: String::from("FOO"), -/// contents: vec![1, 2, 3, 4], -/// }; -/// encode_config(&pem, EncodeConfig { line_ending: LineEnding::LF }); +/// let pem = Pem::new("FOO", [1, 2, 3, 4]); +/// encode_config(&pem, EncodeConfig { line_ending: LineEnding::LF }); /// ``` pub fn encode_config(pem: &Pem, config: EncodeConfig) -> String { let line_ending = match config.line_ending { @@ -401,16 +413,10 @@ pub fn encode_config(pem: &Pem, config: EncodeConfig) -> String { /// use pem::{Pem, encode_many}; /// /// let data = vec![ -/// Pem { -/// tag: String::from("FOO"), -/// contents: vec![1, 2, 3, 4], -/// }, -/// Pem { -/// tag: String::from("BAR"), -/// contents: vec![5, 6, 7, 8], -/// }, -/// ]; -/// encode_many(&data); +/// Pem::new("FOO", [1, 2, 3, 4]), +/// Pem::new("BAR", [5, 6, 7, 8]), +/// ]; +/// encode_many(&data); /// ``` pub fn encode_many(pems: &[Pem]) -> String { pems.iter() @@ -429,14 +435,8 @@ pub fn encode_many(pems: &[Pem]) -> String { /// use pem::{Pem, encode_many_config, EncodeConfig, LineEnding}; /// /// let data = vec![ -/// Pem { -/// tag: String::from("FOO"), -/// contents: vec![1, 2, 3, 4], -/// }, -/// Pem { -/// tag: String::from("BAR"), -/// contents: vec![5, 6, 7, 8], -/// }, +/// Pem::new("FOO", [1, 2, 3, 4]), +/// Pem::new("BAR", [5, 6, 7, 8]), /// ]; /// encode_many_config(&data, EncodeConfig { line_ending: LineEnding::LF }); /// ``` @@ -547,7 +547,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg #[test] fn test_parse_works() { let pem = parse(SAMPLE_CRLF).unwrap(); - assert_eq!(pem.tag, "RSA PRIVATE KEY"); + assert_eq!(pem.tag(), "RSA PRIVATE KEY"); } #[test] @@ -612,15 +612,15 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg let input = "-----BEGIN DATA----- -----END DATA-----"; let pem = parse(input).unwrap(); - assert_eq!(pem.contents.len(), 0); + assert_eq!(pem.contents().len(), 0); } #[test] fn test_parse_many_works() { let pems = parse_many(SAMPLE_CRLF).unwrap(); assert_eq!(pems.len(), 2); - assert_eq!(pems[0].tag, "RSA PRIVATE KEY"); - assert_eq!(pems[1].tag, "RSA PUBLIC KEY"); + assert_eq!(pems[0].tag(), "RSA PRIVATE KEY"); + assert_eq!(pems[1].tag(), "RSA PUBLIC KEY"); } #[test] @@ -631,10 +631,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg #[test] fn test_encode_empty_contents() { - let pem = Pem { - tag: String::from("FOO"), - contents: vec![], - }; + let pem = Pem::new("FOO", vec![]); let encoded = encode(&pem); assert!(!encoded.is_empty()); @@ -644,10 +641,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg #[test] fn test_encode_contents() { - let pem = Pem { - tag: String::from("FOO"), - contents: vec![1, 2, 3, 4], - }; + let pem = Pem::new("FOO", [1, 2, 3, 4]); let encoded = encode(&pem); assert!(!encoded.is_empty()); @@ -665,10 +659,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg #[test] fn test_encode_config_contents() { - let pem = Pem { - tag: String::from("FOO"), - contents: vec![1, 2, 3, 4], - }; + let pem = Pem::new("FOO", [1, 2, 3, 4]); let config = EncodeConfig { line_ending: LineEnding::LF, }; @@ -693,10 +684,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg #[cfg(feature = "serde")] #[test] fn test_serde() { - let pem = Pem { - tag: String::from("Mock tag"), - contents: "Mock contents".as_bytes().to_vec(), - }; + let pem = Pem::new("Mock tag", "Mock contents".as_bytes()); let value = serde_json::to_string_pretty(&pem).unwrap(); let result = serde_json::from_str(&value).unwrap(); assert_eq!(pem, result); @@ -794,14 +782,14 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg", fn test_parse_many_with_headers_crlf() { let pems = parse_many(HEADER_CRLF).unwrap(); assert_eq!(pems.len(), 2); - assert_eq!(pems[0].tag, "CERTIFICATE"); + assert_eq!(pems[0].tag(), "CERTIFICATE"); assert!(cmp_data( - &pems[0].contents, + pems[0].contents(), &decode_data(HEADER_CRLF_DATA[0]).unwrap() )); - assert_eq!(pems[1].tag, "RSA PRIVATE KEY"); + assert_eq!(pems[1].tag(), "RSA PRIVATE KEY"); assert!(cmp_data( - &pems[1].contents, + pems[1].contents(), &decode_data(HEADER_CRLF_DATA[1]).unwrap() )); } @@ -810,22 +798,22 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg", fn test_parse_many_with_headers_lf() { let pems = parse_many(HEADER_LF).unwrap(); assert_eq!(pems.len(), 2); - assert_eq!(pems[0].tag, "CERTIFICATE"); + assert_eq!(pems[0].tag(), "CERTIFICATE"); assert!(cmp_data( - &pems[0].contents, + pems[0].contents(), &decode_data(HEADER_LF_DATA[0]).unwrap() )); - assert_eq!(pems[1].tag, "RSA PRIVATE KEY"); + assert_eq!(pems[1].tag(), "RSA PRIVATE KEY"); assert!(cmp_data( - &pems[1].contents, + pems[1].contents(), &decode_data(HEADER_LF_DATA[1]).unwrap() )); } proptest! { #[test] - fn test_str_parse_and_display(tag in ".+", contents in prop::collection::vec(0..255u8, 0..200)) { - let pem = Pem { tag, contents }; + fn test_str_parse_and_display(tag in "[A-Z ]+", contents in prop::collection::vec(0..255u8, 0..200)) { + let pem = Pem::new(tag, contents); prop_assert_eq!(&pem, &pem.to_string().parse::().unwrap()); } }