use std::collections::HashMap;
use std::ops::Range;
use gaze_types::{
LeakReport, LeakSuspect, LocaleTag, PiiClass, SafetyNet, SafetyNetContext, SafetyNetError,
};
#[derive(Debug, Clone)]
pub struct MockSafetyNet {
id: String,
locales: Vec<LocaleTag>,
reports: HashMap<Option<String>, LeakReport>,
raw_suspects: HashMap<Option<String>, Vec<MockLeakSuspect>>,
error: Option<SafetyNetError>,
}
impl MockSafetyNet {
pub fn new(report: LeakReport) -> Self {
Self::with_reports(HashMap::from([(None, report)]))
}
pub fn with_reports(reports: HashMap<Option<String>, LeakReport>) -> Self {
Self {
id: "mock-safety-net".to_string(),
locales: vec![LocaleTag::Global],
reports,
raw_suspects: HashMap::new(),
error: None,
}
}
pub fn with_raw_suspects(raw_suspects: HashMap<Option<String>, Vec<MockLeakSuspect>>) -> Self {
Self {
id: "mock-safety-net".to_string(),
locales: vec![LocaleTag::Global],
reports: HashMap::new(),
raw_suspects,
error: None,
}
}
pub fn with_id(mut self, id: impl Into<String>) -> Self {
self.id = id.into();
self
}
pub fn with_locales(mut self, locales: Vec<LocaleTag>) -> Self {
self.locales = locales;
self
}
pub fn with_error(mut self, error: SafetyNetError) -> Self {
self.error = Some(error);
self
}
fn report_for_path(&self, field_path: Option<&str>) -> Option<&LeakReport> {
let key = field_path.map(str::to_string);
self.reports.get(&key)
}
fn raw_suspects_for_path(&self, field_path: Option<&str>) -> Option<&[MockLeakSuspect]> {
let key = field_path.map(str::to_string);
self.raw_suspects.get(&key).map(Vec::as_slice)
}
}
impl SafetyNet for MockSafetyNet {
fn id(&self) -> &str {
&self.id
}
fn supported_locales(&self) -> &[LocaleTag] {
&self.locales
}
fn check(
&self,
_clean_text: &str,
context: SafetyNetContext<'_>,
) -> Result<Vec<LeakSuspect>, SafetyNetError> {
if let Some(error) = &self.error {
return Err(error.clone());
}
if let Some(report) = self.report_for_path(context.field_path) {
return Ok(report.suspects.clone());
}
let Some(raw_suspects) = self.raw_suspects_for_path(context.field_path) else {
return Ok(Vec::new());
};
Ok(raw_suspects
.iter()
.filter_map(|raw| {
let kind = context.manifest.diff_against(&raw.span, &raw.class)?;
Some(LeakSuspect::new(
raw.span.clone(),
raw.class.clone(),
self.id.clone(),
raw.score,
kind,
raw.raw_label.clone(),
context.field_path.map(str::to_string),
))
})
.collect())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MockLeakSuspect {
pub span: Range<usize>,
pub class: PiiClass,
pub score: Option<f32>,
pub raw_label: String,
}
impl MockLeakSuspect {
pub fn new(span: Range<usize>, class: PiiClass, raw_label: impl Into<String>) -> Self {
Self {
span,
class,
score: None,
raw_label: raw_label.into(),
}
}
pub fn with_score(mut self, score: f32) -> Self {
self.score = Some(score);
self
}
}