Skip to content

Commit 3d74b9a

Browse files
Merge pull request #91 from theseus-rs/add-hasher-and-matcher-supports-fn
feat: add hasher and matcher supports function
2 parents e8ef1ec + 9dfc011 commit 3d74b9a

File tree

16 files changed

+219
-352
lines changed

16 files changed

+219
-352
lines changed

examples/archive_async/src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
#![forbid(unsafe_code)]
22
#![deny(clippy::pedantic)]
33

4-
use postgresql_archive::{extract, get_archive, Result, VersionReq, DEFAULT_POSTGRESQL_URL};
4+
use postgresql_archive::{
5+
extract, get_archive, Result, VersionReq, THESEUS_POSTGRESQL_BINARIES_URL,
6+
};
57

68
#[tokio::main]
79
async fn main() -> Result<()> {
810
let version_req = VersionReq::STAR;
9-
let (archive_version, archive) = get_archive(DEFAULT_POSTGRESQL_URL, &version_req).await?;
11+
let (archive_version, archive) =
12+
get_archive(THESEUS_POSTGRESQL_BINARIES_URL, &version_req).await?;
1013
let out_dir = tempfile::tempdir()?.into_path();
1114
extract(&archive, &out_dir).await?;
1215
println!(

examples/archive_sync/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
#![deny(clippy::pedantic)]
33

44
use postgresql_archive::blocking::{extract, get_archive};
5-
use postgresql_archive::{Result, VersionReq, DEFAULT_POSTGRESQL_URL};
5+
use postgresql_archive::{Result, VersionReq, THESEUS_POSTGRESQL_BINARIES_URL};
66

77
fn main() -> Result<()> {
88
let version_req = VersionReq::STAR;
9-
let (archive_version, archive) = get_archive(DEFAULT_POSTGRESQL_URL, &version_req)?;
9+
let (archive_version, archive) = get_archive(THESEUS_POSTGRESQL_BINARIES_URL, &version_req)?;
1010
let out_dir = tempfile::tempdir()?.into_path();
1111
extract(&archive, &out_dir)?;
1212
println!(

postgresql_archive/benches/archive.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use criterion::{criterion_group, criterion_main, Criterion};
22
use postgresql_archive::blocking::{extract, get_archive};
3-
use postgresql_archive::{Result, VersionReq, DEFAULT_POSTGRESQL_URL};
3+
use postgresql_archive::{Result, VersionReq, THESEUS_POSTGRESQL_BINARIES_URL};
44
use std::fs::{create_dir_all, remove_dir_all};
55
use std::time::Duration;
66

@@ -10,7 +10,7 @@ fn benchmarks(criterion: &mut Criterion) {
1010

1111
fn bench_extract(criterion: &mut Criterion) -> Result<()> {
1212
let version_req = VersionReq::STAR;
13-
let (_archive_version, archive) = get_archive(DEFAULT_POSTGRESQL_URL, &version_req)?;
13+
let (_archive_version, archive) = get_archive(THESEUS_POSTGRESQL_BINARIES_URL, &version_req)?;
1414

1515
criterion.bench_function("extract", |bencher| {
1616
bencher.iter(|| {

postgresql_archive/src/archive.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use std::time::Duration;
1616
use tar::Archive;
1717
use tracing::{debug, instrument, warn};
1818

19-
pub const DEFAULT_POSTGRESQL_URL: &str = "https://github.com/theseus-rs/postgresql-binaries";
19+
pub const THESEUS_POSTGRESQL_BINARIES_URL: &str =
20+
"https://github.com/theseus-rs/postgresql-binaries";
2021

2122
/// Gets the version for the specified [version requirement](VersionReq). If a version for the
2223
/// [version requirement](VersionReq) is not found, then an error is returned.
@@ -213,15 +214,15 @@ mod tests {
213214
#[tokio::test]
214215
async fn test_get_version() -> Result<()> {
215216
let version_req = VersionReq::parse("=16.3.0")?;
216-
let version = get_version(DEFAULT_POSTGRESQL_URL, &version_req).await?;
217+
let version = get_version(THESEUS_POSTGRESQL_BINARIES_URL, &version_req).await?;
217218
assert_eq!(Version::new(16, 3, 0), version);
218219
Ok(())
219220
}
220221

221222
#[tokio::test]
222223
async fn test_get_archive() -> Result<()> {
223224
let version_req = VersionReq::parse("=16.3.0")?;
224-
let (version, bytes) = get_archive(DEFAULT_POSTGRESQL_URL, &version_req).await?;
225+
let (version, bytes) = get_archive(THESEUS_POSTGRESQL_BINARIES_URL, &version_req).await?;
225226
assert_eq!(Version::new(16, 3, 0), version);
226227
assert!(!bytes.is_empty());
227228
Ok(())

postgresql_archive/src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ pub enum Error {
3131
/// Unexpected error
3232
#[error("{0}")]
3333
Unexpected(String),
34+
/// Unsupported hasher
35+
#[error("unsupported hasher for '{0}'")]
36+
UnsupportedHasher(String),
37+
/// Unsupported hasher
38+
#[error("unsupported matcher for '{0}'")]
39+
UnsupportedMatcher(String),
3440
/// Unsupported repository
3541
#[error("unsupported repository for '{0}'")]
3642
UnsupportedRepository(String),
Lines changed: 63 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,111 @@
1-
use crate::hasher::{blake2b_512, blake2s_256, sha2_256, sha2_512, sha3_256, sha3_512};
2-
use crate::Error::PoisonedLock;
3-
use crate::Result;
1+
use crate::hasher::sha2_256;
2+
use crate::Error::{PoisonedLock, UnsupportedHasher};
3+
use crate::{Result, THESEUS_POSTGRESQL_BINARIES_URL};
44
use lazy_static::lazy_static;
5-
use std::collections::HashMap;
65
use std::sync::{Arc, Mutex, RwLock};
76

87
lazy_static! {
98
static ref REGISTRY: Arc<Mutex<HasherRegistry>> =
109
Arc::new(Mutex::new(HasherRegistry::default()));
1110
}
1211

12+
pub type SupportsFn = fn(&str, &str) -> Result<bool>;
1313
pub type HasherFn = fn(&Vec<u8>) -> Result<String>;
1414

1515
/// Singleton struct to store hashers
16+
#[allow(clippy::type_complexity)]
1617
struct HasherRegistry {
17-
hashers: HashMap<String, Arc<RwLock<HasherFn>>>,
18+
hashers: Vec<(Arc<RwLock<SupportsFn>>, Arc<RwLock<HasherFn>>)>,
1819
}
1920

2021
impl HasherRegistry {
2122
/// Creates a new hasher registry.
2223
fn new() -> Self {
2324
Self {
24-
hashers: HashMap::new(),
25+
hashers: Vec::new(),
2526
}
2627
}
2728

28-
/// Registers a hasher for an extension. Newly registered hashers with the same extension will
29-
/// override existing ones.
30-
fn register<S: AsRef<str>>(&mut self, extension: S, hasher_fn: HasherFn) {
31-
let extension = extension.as_ref().to_string();
32-
self.hashers
33-
.insert(extension, Arc::new(RwLock::new(hasher_fn)));
29+
/// Registers a hasher for a supports function. Newly registered hashers will take precedence
30+
/// over existing ones.
31+
fn register(&mut self, supports_fn: SupportsFn, hasher_fn: HasherFn) {
32+
self.hashers.insert(
33+
0,
34+
(
35+
Arc::new(RwLock::new(supports_fn)),
36+
Arc::new(RwLock::new(hasher_fn)),
37+
),
38+
);
3439
}
3540

36-
/// Get a hasher for the specified extension.
41+
/// Get a hasher for the specified url and extension.
3742
///
3843
/// # Errors
3944
/// * If the registry is poisoned.
40-
fn get<S: AsRef<str>>(&self, extension: S) -> Result<Option<HasherFn>> {
41-
let extension = extension.as_ref().to_string();
42-
if let Some(hasher) = self.hashers.get(&extension) {
43-
let hasher = *hasher
45+
fn get<S: AsRef<str>>(&self, url: S, extension: S) -> Result<HasherFn> {
46+
let url = url.as_ref();
47+
let extension = extension.as_ref();
48+
for (supports_fn, hasher_fn) in &self.hashers {
49+
let supports_function = supports_fn
4450
.read()
4551
.map_err(|error| PoisonedLock(error.to_string()))?;
46-
return Ok(Some(hasher));
52+
if supports_function(url, extension)? {
53+
let hasher_function = hasher_fn
54+
.read()
55+
.map_err(|error| PoisonedLock(error.to_string()))?;
56+
return Ok(*hasher_function);
57+
}
4758
}
4859

49-
Ok(None)
50-
}
51-
52-
/// Get the number of hashers in the registry.
53-
fn len(&self) -> usize {
54-
self.hashers.len()
55-
}
56-
57-
/// Check if the registry is empty.
58-
fn is_empty(&self) -> bool {
59-
self.hashers.is_empty()
60+
Err(UnsupportedHasher(url.to_string()))
6061
}
6162
}
6263

6364
impl Default for HasherRegistry {
6465
/// Creates a new hasher registry with the default hashers registered.
6566
fn default() -> Self {
6667
let mut registry = Self::new();
67-
registry.register("blake2s", blake2s_256::hash);
68-
registry.register("blake2b", blake2b_512::hash);
69-
registry.register("sha256", sha2_256::hash);
70-
registry.register("sha512", sha2_512::hash);
71-
registry.register("sha3-256", sha3_256::hash);
72-
registry.register("sha3-512", sha3_512::hash);
68+
registry.register(
69+
|url, extension| {
70+
Ok(url.starts_with(THESEUS_POSTGRESQL_BINARIES_URL) && extension == "sha256")
71+
},
72+
sha2_256::hash,
73+
);
7374
registry
7475
}
7576
}
7677

77-
/// Registers a hasher for an extension. Newly registered hashers with the same extension will
78-
/// override existing ones.
78+
/// Registers a hasher for a supports function. Newly registered hashers will take precedence
79+
/// over existing ones.
7980
///
8081
/// # Errors
8182
/// * If the registry is poisoned.
8283
#[allow(dead_code)]
83-
pub fn register<S: AsRef<str>>(extension: S, hasher_fn: HasherFn) -> Result<()> {
84+
pub fn register(supports_fn: SupportsFn, hasher_fn: HasherFn) -> Result<()> {
8485
let mut registry = REGISTRY
8586
.lock()
8687
.map_err(|error| PoisonedLock(error.to_string()))?;
87-
registry.register(extension, hasher_fn);
88+
registry.register(supports_fn, hasher_fn);
8889
Ok(())
8990
}
9091

91-
/// Get a hasher for the specified extension.
92+
/// Get a hasher for the specified url and extension.
9293
///
9394
/// # Errors
9495
/// * If the registry is poisoned.
95-
pub fn get<S: AsRef<str>>(extension: S) -> Result<Option<HasherFn>> {
96+
pub fn get<S: AsRef<str>>(url: S, extension: S) -> Result<HasherFn> {
9697
let registry = REGISTRY
9798
.lock()
9899
.map_err(|error| PoisonedLock(error.to_string()))?;
99-
registry.get(extension)
100-
}
101-
102-
/// Get the number of matchers in the registry.
103-
///
104-
/// # Errors
105-
/// * If the registry is poisoned.
106-
pub fn len() -> Result<usize> {
107-
let registry = REGISTRY
108-
.lock()
109-
.map_err(|error| PoisonedLock(error.to_string()))?;
110-
Ok(registry.len())
111-
}
112-
113-
/// Check if the registry is empty.
114-
///
115-
/// # Errors
116-
/// * If the registry is poisoned.
117-
pub fn is_empty() -> Result<bool> {
118-
let registry = REGISTRY
119-
.lock()
120-
.map_err(|error| PoisonedLock(error.to_string()))?;
121-
Ok(registry.is_empty())
100+
registry.get(url, extension)
122101
}
123102

124103
#[cfg(test)]
125104
mod tests {
126105
use super::*;
127106

128107
fn test_hasher(extension: &str, expected: &str) -> Result<()> {
129-
let hasher = get(extension)?.unwrap();
108+
let hasher = get("https://foo.com", extension)?;
130109
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
131110
let hash = hasher(&data)?;
132111
assert_eq!(expected, hash);
@@ -135,69 +114,33 @@ mod tests {
135114

136115
#[test]
137116
fn test_register() -> Result<()> {
138-
let extension = "sha256";
139-
let hashers = len()?;
140-
assert!(!is_empty()?);
141-
REGISTRY
142-
.lock()
143-
.map_err(|error| PoisonedLock(error.to_string()))?
144-
.hashers
145-
.remove(extension);
146-
assert_ne!(hashers, len()?);
147-
register(extension, sha2_256::hash)?;
148-
assert_eq!(hashers, len()?);
149-
150-
test_hasher(
151-
extension,
152-
"9a89c68c4c5e28b8c4a5567673d462fff515db46116f9900624d09c474f593fb",
153-
)
154-
}
155-
156-
#[test]
157-
fn test_sha2_256() -> Result<()> {
158-
test_hasher(
159-
"sha256",
160-
"9a89c68c4c5e28b8c4a5567673d462fff515db46116f9900624d09c474f593fb",
161-
)
162-
}
163-
164-
#[test]
165-
fn test_sha2_512() -> Result<()> {
166-
test_hasher(
167-
"sha512",
168-
"3ad3f36979450d4f53366244ecf1010f4f9121d6888285ff14104fd5aded85d48aa171bf1e33a112602f92b7a7088b298789012fb87b9056321241a19fb74e0b",
169-
)
170-
}
171-
172-
#[test]
173-
fn test_sha3_256() -> Result<()> {
174-
test_hasher(
175-
"sha3-256",
176-
"c0188232190e0427fc9cc78597221c76c799528660889bd6ce1f3563148ff84d",
177-
)
117+
register(
118+
|_, extension| Ok(extension == "test"),
119+
|_| Ok("42".to_string()),
120+
)?;
121+
test_hasher("test", "42")
178122
}
179123

180124
#[test]
181-
fn test_sha3_512() -> Result<()> {
182-
test_hasher(
183-
"sha3-512",
184-
"9429fc1f9772cc1d8039fe75cc1b033cd60f0ec4face0f8a514d25b0649ba8a5954b6c7a41cc3697a56db3ff321475be1fa14b70c7eb78fec6ce62dbfc54c9d3",
185-
)
125+
fn test_get_invalid_url_error() {
126+
let error = get("https://foo.com", "foo").unwrap_err();
127+
assert_eq!(
128+
"unsupported hasher for 'https://foo.com'",
129+
error.to_string()
130+
);
186131
}
187132

188133
#[test]
189-
fn test_blake2s_256() -> Result<()> {
190-
test_hasher(
191-
"blake2s",
192-
"7125921e06071710350390fe902856dbea366a5d6f5ee26c18e741143ac80061",
193-
)
134+
fn test_get_invalid_extension_error() {
135+
let error = get(THESEUS_POSTGRESQL_BINARIES_URL, "foo").unwrap_err();
136+
assert_eq!(
137+
format!("unsupported hasher for '{THESEUS_POSTGRESQL_BINARIES_URL}'"),
138+
error.to_string()
139+
);
194140
}
195141

196142
#[test]
197-
fn test_blake2b_512() -> Result<()> {
198-
test_hasher(
199-
"blake2b",
200-
"67767f1cab415502dcceec9f099fb84539b1c73c5ebdcfe1bb8ca7411e3b6cb33e304f49222edac9bdaa74129e9e13f11f215b8560f9081f0e8f1f869162bf46",
201-
)
143+
fn test_get_theseus_postgresql_binaries() {
144+
assert!(get(THESEUS_POSTGRESQL_BINARIES_URL, "sha256").is_ok());
202145
}
203146
}

postgresql_archive/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
//! ### Asynchronous API
2222
//!
2323
//! ```no_run
24-
//! use postgresql_archive::{extract, get_archive, Result, VersionReq, DEFAULT_POSTGRESQL_URL};
24+
//! use postgresql_archive::{extract, get_archive, Result, VersionReq, THESEUS_POSTGRESQL_BINARIES_URL};
2525
//!
2626
//! #[tokio::main]
2727
//! async fn main() -> Result<()> {
28-
//! let (archive_version, archive) = get_archive(DEFAULT_POSTGRESQL_URL, &VersionReq::STAR).await?;
28+
//! let (archive_version, archive) = get_archive(THESEUS_POSTGRESQL_BINARIES_URL, &VersionReq::STAR).await?;
2929
//! let out_dir = std::env::temp_dir();
3030
//! extract(&archive, &out_dir).await
3131
//! }
@@ -34,10 +34,10 @@
3434
//! ### Synchronous API
3535
//! ```no_run
3636
//! #[cfg(feature = "blocking")] {
37-
//! use postgresql_archive::{VersionReq, DEFAULT_POSTGRESQL_URL};
37+
//! use postgresql_archive::{VersionReq, THESEUS_POSTGRESQL_BINARIES_URL};
3838
//! use postgresql_archive::blocking::{extract, get_archive};
3939
//!
40-
//! let (archive_version, archive) = get_archive(DEFAULT_POSTGRESQL_URL, &VersionReq::STAR).unwrap();
40+
//! let (archive_version, archive) = get_archive(THESEUS_POSTGRESQL_BINARIES_URL, &VersionReq::STAR).unwrap();
4141
//! let out_dir = std::env::temp_dir();
4242
//! let result = extract(&archive, &out_dir).unwrap();
4343
//! }
@@ -118,7 +118,7 @@ pub mod matcher;
118118
pub mod repository;
119119
mod version;
120120

121-
pub use archive::DEFAULT_POSTGRESQL_URL;
121+
pub use archive::THESEUS_POSTGRESQL_BINARIES_URL;
122122
pub use archive::{extract, get_archive, get_version};
123123
pub use error::{Error, Result};
124124
pub use semver::{Version, VersionReq};

postgresql_archive/src/matcher/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
pub mod default;
21
pub mod postgresql_binaries;
32
pub mod registry;
3+
pub mod target_os_arch;

0 commit comments

Comments
 (0)