1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright (C) 2023, 2024 Tsukasa OI <floss_ssdeep@irq.a4lg.com>.
//! Easy comparison functions and related error reporting utilities.
#![cfg(feature = "easy-functions")]
use crate::hash::parser_state::{ParseError, ParseErrorInfo, ParseErrorKind, ParseErrorOrigin};
use crate::hash::LongFuzzyHash;
/// The operand (side) which caused a parse error.
///
/// # Compatibility Note
///
/// Since the version 0.3, the representation of this enum is no longer
/// specified as specific representation of this enum is not important.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParseErrorSide {
/// The left hand side.
Left,
/// The right hand side.
Right,
}
/// The error type representing a parse error for one of the operands
/// specified to the [`compare()`] function.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParseErrorEither(ParseErrorSide, ParseError); // grcov-excl-br-line:STRUCT_MEMBER
impl ParseErrorEither {
/// Returns which operand caused a parse error.
pub fn side(&self) -> ParseErrorSide {
self.0
}
}
impl ParseErrorInfo for ParseErrorEither {
fn kind(&self) -> ParseErrorKind {
self.1.kind()
}
fn origin(&self) -> ParseErrorOrigin {
self.1.origin()
}
fn offset(&self) -> usize {
self.1.offset()
}
}
impl core::fmt::Display for ParseErrorEither {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"error occurred while parsing fuzzy hash {3} ({1}, at byte offset {2}): {0}",
self.kind(),
self.origin(),
self.offset(),
match self.side() {
ParseErrorSide::Left => 1,
ParseErrorSide::Right => 2,
}
)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseErrorEither {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.1)
}
}
#[cfg(all(not(feature = "std"), feature = "unstable"))]
impl core::error::Error for ParseErrorEither {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
Some(&self.1)
}
}
/// Compare two fuzzy hashes.
///
/// If a parse error occurs, [`Err`] containing
/// [a parse error](ParseErrorEither) is returned.
/// Otherwise, [`Ok`] containing the similarity score (`0..=100`) is returned.
///
/// # Example
///
/// ```
/// assert_eq!(
/// ssdeep::compare(
/// "6:3ll7QzDkmJmMHkQoO/llSZEnEuLszmbMAWn:VqDk5QtLbW",
/// "6:3ll7QzDkmQjmMoDHglHOxPWT0lT0lT0lB:VqDk+n"
/// ).unwrap(),
/// 46
/// );
/// ```
pub fn compare(lhs: &str, rhs: &str) -> Result<u32, ParseErrorEither> {
let lhs: LongFuzzyHash = match str::parse(lhs) {
Ok(value) => value,
Err(err) => {
return Err(ParseErrorEither(ParseErrorSide::Left, err));
}
};
let rhs: LongFuzzyHash = match str::parse(rhs) {
Ok(value) => value,
Err(err) => {
return Err(ParseErrorEither(ParseErrorSide::Right, err));
}
};
Ok(lhs.compare(rhs.as_ref()))
}
mod tests;