assemblyline_models/types/
md5.rs

1use serde_with::{DeserializeFromStr, SerializeDisplay};
2use struct_metadata::Described;
3
4use crate::{random_hex, ElasticMeta, ModelError};
5
6
7/// MD5 hash of a file
8#[derive(Debug, SerializeDisplay, DeserializeFromStr, Described, Clone, PartialEq, Eq)]
9#[metadata_type(ElasticMeta)]
10#[metadata(normalizer="lowercase_normalizer")]
11pub struct MD5(String);
12
13impl std::fmt::Display for MD5 {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        f.write_str(&self.0)
16    }
17}
18
19impl std::ops::Deref for MD5 {
20    type Target = str;
21
22    fn deref(&self) -> &Self::Target {
23        &self.0
24    }
25}
26
27pub fn is_md5(hex: &str) -> bool {
28    hex.len() == 32 && hex.chars().all(|c|c.is_ascii_hexdigit())
29}
30
31impl std::str::FromStr for MD5 {
32    type Err = ModelError;
33
34    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
35        let hex = s.trim().to_ascii_lowercase();
36        if !is_md5(&hex) {
37            return Err(ModelError::InvalidMd5(hex))
38        }
39        Ok(MD5(hex))
40    }
41}
42
43impl TryFrom<&[u8]> for MD5 {
44    type Error = ModelError;
45
46    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
47        if value.len() == 16 {
48            Ok(MD5(hex::encode(value)))
49        } else {
50            Err(ModelError::InvalidMd5(format!("Wrong number of bytes {}", value.len())))
51        }
52    }
53}
54
55impl From<[u8; 16]> for MD5 {
56    fn from(value: [u8; 16]) -> Self {
57        MD5(hex::encode(value))
58    }
59}
60
61#[cfg(feature = "rand")]
62impl rand::distr::Distribution<MD5> for rand::distr::StandardUniform {
63    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> MD5 {
64        MD5(random_hex(rng, 32))
65    }
66}
67
68