Skip to content

Commit 92b640e

Browse files
committed
feat: add hasher and matcher supports function
1 parent e8ef1ec commit 92b640e

File tree

5 files changed

+143
-205
lines changed

5 files changed

+143
-205
lines changed

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),

postgresql_archive/src/hasher/registry.rs

Lines changed: 47 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,111 @@
11
use crate::hasher::{blake2b_512, blake2s_256, sha2_256, sha2_512, sha3_256, sha3_512};
2-
use crate::Error::PoisonedLock;
2+
use crate::Error::{PoisonedLock, UnsupportedRepository};
33
use crate::Result;
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(UnsupportedRepository(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(|_, extension| Ok(extension == "blake2s"), blake2s_256::hash);
69+
registry.register(|_, extension| Ok(extension == "blake2b"), blake2b_512::hash);
70+
registry.register(|_, extension| Ok(extension == "sha256"), sha2_256::hash);
71+
registry.register(|_, extension| Ok(extension == "sha512"), sha2_512::hash);
72+
registry.register(|_, extension| Ok(extension == "sha3-256"), sha3_256::hash);
73+
registry.register(|_, extension| Ok(extension == "sha3-512"), sha3_512::hash);
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-
///
93-
/// # Errors
94-
/// * If the registry is poisoned.
95-
pub fn get<S: AsRef<str>>(extension: S) -> Result<Option<HasherFn>> {
96-
let registry = REGISTRY
97-
.lock()
98-
.map_err(|error| PoisonedLock(error.to_string()))?;
99-
registry.get(extension)
100-
}
101-
102-
/// Get the number of matchers in the registry.
92+
/// Get a hasher for the specified url and extension.
10393
///
10494
/// # Errors
10595
/// * If the registry is poisoned.
106-
pub fn len() -> Result<usize> {
96+
pub fn get<S: AsRef<str>>(url: S, extension: S) -> Result<HasherFn> {
10797
let registry = REGISTRY
10898
.lock()
10999
.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,22 +114,11 @@ 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-
)
117+
register(
118+
|_, extension| Ok(extension == "foo"),
119+
|_| Ok("42".to_string()),
120+
)?;
121+
test_hasher("foo", "42")
154122
}
155123

156124
#[test]

0 commit comments

Comments
 (0)