Skip to main content

semdiff_differ_audio/
report_json.rs

1use crate::{AudioDiff, AudioDiffReporter, audio_extension};
2use semdiff_core::fs::FileLeaf;
3use semdiff_core::{DetailReporter, MayUnsupported};
4use semdiff_output::json::JsonReport;
5use serde::Serialize;
6use thiserror::Error;
7
8const COMPARES_NAME: &str = "audio";
9
10#[derive(Debug, Error)]
11pub enum AudioJsonReportError {
12    #[error("audio decode error: {0}")]
13    AudioDecode(#[from] crate::AudioDecodeError),
14}
15
16impl<W> DetailReporter<AudioDiff, FileLeaf, JsonReport<W>> for AudioDiffReporter {
17    type Error = AudioJsonReportError;
18
19    fn report_unchanged(
20        &self,
21        name: &str,
22        _diff: &AudioDiff,
23        reporter: &JsonReport<W>,
24    ) -> Result<MayUnsupported<()>, Self::Error> {
25        reporter.record_unchanged(name, COMPARES_NAME, ());
26        Ok(MayUnsupported::Ok(()))
27    }
28
29    fn report_modified(
30        &self,
31        name: &str,
32        diff: &AudioDiff,
33        reporter: &JsonReport<W>,
34    ) -> Result<MayUnsupported<()>, Self::Error> {
35        let (spectrogram_diff_rate, shift_samples, lufs_diff_db) = if let Some(detail) = diff.diff_detail() {
36            let stat = detail.stat();
37            (
38                Some(stat.spectrogram_diff_rate),
39                Some(stat.shift_samples),
40                Some(stat.lufs_diff_db),
41            )
42        } else {
43            (None, None, None)
44        };
45        let report = ModifiedReport {
46            status: diff.status().as_str().to_string(),
47            expected_sample_rate: diff.expected().sample_rate(),
48            expected_channels: diff.expected().channels(),
49            expected_duration_seconds: diff.expected().duration_seconds(),
50            actual_sample_rate: diff.actual().sample_rate(),
51            actual_channels: diff.actual().channels(),
52            actual_duration_seconds: diff.actual().duration_seconds(),
53            spectrogram_diff_rate,
54            shift_samples,
55            lufs_diff_db,
56        };
57        reporter.record_modified(name, COMPARES_NAME, report);
58        Ok(MayUnsupported::Ok(()))
59    }
60
61    fn report_added(
62        &self,
63        name: &str,
64        data: &FileLeaf,
65        reporter: &JsonReport<W>,
66    ) -> Result<MayUnsupported<()>, Self::Error> {
67        if audio_extension(&data.kind).is_none() {
68            return Ok(MayUnsupported::Unsupported);
69        }
70        let Ok(decoded) = self.spectrogram_analyzer.decode_audio(&data.kind, &data.content) else {
71            return Ok(MayUnsupported::Unsupported);
72        };
73        reporter.record_added(
74            name,
75            COMPARES_NAME,
76            SingleReport {
77                sample_rate: decoded.sample_rate,
78                channels: decoded.channels,
79                duration_seconds: decoded.duration_seconds,
80            },
81        );
82        Ok(MayUnsupported::Ok(()))
83    }
84
85    fn report_deleted(
86        &self,
87        name: &str,
88        data: &FileLeaf,
89        reporter: &JsonReport<W>,
90    ) -> Result<MayUnsupported<()>, Self::Error> {
91        if audio_extension(&data.kind).is_none() {
92            return Ok(MayUnsupported::Unsupported);
93        }
94        let Ok(decoded) = self.spectrogram_analyzer.decode_audio(&data.kind, &data.content) else {
95            return Ok(MayUnsupported::Unsupported);
96        };
97        reporter.record_deleted(
98            name,
99            COMPARES_NAME,
100            SingleReport {
101                sample_rate: decoded.sample_rate,
102                channels: decoded.channels,
103                duration_seconds: decoded.duration_seconds,
104            },
105        );
106        Ok(MayUnsupported::Ok(()))
107    }
108}
109
110#[derive(Serialize)]
111struct ModifiedReport {
112    status: String,
113    expected_sample_rate: u32,
114    expected_channels: u16,
115    expected_duration_seconds: f32,
116    actual_sample_rate: u32,
117    actual_channels: u16,
118    actual_duration_seconds: f32,
119    #[serde(skip_serializing_if = "Option::is_none")]
120    spectrogram_diff_rate: Option<f64>,
121    #[serde(skip_serializing_if = "Option::is_none")]
122    shift_samples: Option<i32>,
123    #[serde(skip_serializing_if = "Option::is_none")]
124    lufs_diff_db: Option<f32>,
125}
126
127#[derive(Serialize)]
128struct SingleReport {
129    sample_rate: u32,
130    channels: u16,
131    duration_seconds: f32,
132}