#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ProviderOutcome {
FixtureStaticCandidate,
BrowserWorkerRenderedText,
BrowserWorkerRenderedRecipe,
TmdbMovieEnriched,
TmdbMoviePartiallyEnriched,
TmdbMovieDegraded,
TmdbTvEnriched,
TmdbTvPartiallyEnriched,
TmdbTvDegraded,
TmdbPartialTitleVariantSearchFailure,
BangumiSubjectEnriched,
BangumiSubjectDegraded,
BangumiPartialTitleVariantSearchFailure,
AniListMediaMapped,
DoubanRenderedHtmlParsed,
JavdbRenderedHtmlParsed,
DmmRenderedHtmlParsed,
XcityRenderedHtmlParsed,
Fc2RenderedHtmlParsed,
Fc2ppvdbRenderedHtmlParsed,
CaribbeanRenderedHtmlParsed,
OnePondoRenderedHtmlParsed,
TenMusumeRenderedHtmlParsed,
Jav321RawHtmlParsed,
JavbusRenderedHtmlParsed,
JavlibraryRenderedHtmlParsed,
AiravRenderedHtmlParsed,
AvsoxRenderedHtmlParsed,
MgstageRenderedHtmlParsed,
PrestigeOfficialApiParsed,
ThePornDbOfficialApiParsed,
}
#[must_use]
pub fn render_provider_note(
outcomes: &[ProviderOutcome],
legacy_note: Option<&str>,
) -> Option<String> {
let mut fragments = outcomes
.iter()
.map(|outcome| outcome.safe_note())
.collect::<Vec<_>>();
if let Some(legacy_note) = legacy_note.map(str::trim).filter(|note| !note.is_empty()) {
fragments.push(legacy_note);
}
(!fragments.is_empty()).then(|| fragments.join(" "))
}
impl ProviderOutcome {
const fn safe_note(self) -> &'static str {
match self {
Self::FixtureStaticCandidate => {
"Fixture provider echoes normalized title for smoke testing."
}
Self::BrowserWorkerRenderedText => {
"Browser worker rendered a page and returned normalized text."
}
Self::BrowserWorkerRenderedRecipe => {
"Browser worker rendered a page and mapped it through a metadata extraction recipe."
}
Self::TmdbMovieEnriched => {
"TMDB movie candidate enriched with search, detail, and external ID responses."
}
Self::TmdbMoviePartiallyEnriched => {
"TMDB movie candidate partially enriched with search and detail responses after secondary enrichment failure."
}
Self::TmdbMovieDegraded => {
"TMDB movie candidate degraded from search response after enrichment failure."
}
Self::TmdbTvEnriched => {
"TMDB TV candidate enriched with search, detail, and external ID responses."
}
Self::TmdbTvPartiallyEnriched => {
"TMDB TV candidate partially enriched with search and detail responses after secondary enrichment failure."
}
Self::TmdbTvDegraded => {
"TMDB TV candidate degraded from search response after enrichment failure."
}
Self::TmdbPartialTitleVariantSearchFailure => {
"TMDB provider preserved candidates after partial title-variant search failure."
}
Self::BangumiSubjectEnriched => {
"Bangumi subject candidate enriched with search and detail responses."
}
Self::BangumiSubjectDegraded => {
"Bangumi subject candidate degraded from search response after enrichment failure."
}
Self::BangumiPartialTitleVariantSearchFailure => {
"Bangumi provider preserved candidates after partial title-variant search failure."
}
Self::AniListMediaMapped => {
"AniList provider mapped anime media from the official GraphQL API."
}
Self::DoubanRenderedHtmlParsed => {
"Douban candidate parsed from browser-worker rendered HTML."
}
Self::JavdbRenderedHtmlParsed => {
"JavDB AV candidate parsed from browser-worker rendered HTML."
}
Self::DmmRenderedHtmlParsed => {
"DMM AV candidate parsed from browser-worker rendered HTML."
}
Self::XcityRenderedHtmlParsed => {
"XCity AV candidate parsed from browser-worker rendered HTML."
}
Self::Fc2RenderedHtmlParsed => {
"FC2 AV candidate parsed from browser-worker rendered HTML."
}
Self::Fc2ppvdbRenderedHtmlParsed => {
"FC2PPVDB AV candidate parsed from browser-worker rendered HTML."
}
Self::CaribbeanRenderedHtmlParsed => {
"Caribbeancom AV candidate parsed from browser-worker rendered HTML."
}
Self::OnePondoRenderedHtmlParsed => {
"1Pondo AV candidate parsed from browser-worker rendered HTML."
}
Self::TenMusumeRenderedHtmlParsed => {
"10Musume AV candidate parsed from browser-worker rendered HTML."
}
Self::Jav321RawHtmlParsed => "Jav321 AV candidate parsed from raw HTML.",
Self::JavbusRenderedHtmlParsed => {
"JavBus AV candidate parsed from browser-worker rendered HTML."
}
Self::JavlibraryRenderedHtmlParsed => {
"JavLibrary AV candidate parsed from browser-worker rendered HTML."
}
Self::AiravRenderedHtmlParsed => {
"AirAV AV candidate parsed from browser-worker rendered HTML."
}
Self::AvsoxRenderedHtmlParsed => {
"AVSox AV candidate parsed from browser-worker rendered HTML."
}
Self::MgstageRenderedHtmlParsed => {
"MGStage AV candidate parsed from browser-worker rendered HTML."
}
Self::PrestigeOfficialApiParsed => {
"Prestige AV candidate parsed from the official JSON API."
}
Self::ThePornDbOfficialApiParsed => {
"ThePornDB AV candidate parsed from the official JSON API."
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn provider_outcomes_render_redaction_safe_notes() {
let note = render_provider_note(
&[
ProviderOutcome::TmdbMoviePartiallyEnriched,
ProviderOutcome::TmdbPartialTitleVariantSearchFailure,
],
None,
)
.unwrap();
assert!(note.contains("partially enriched"));
assert!(note.contains("partial title-variant search failure"));
assert!(!note.contains("Bearer"));
assert!(!note.contains("https://"));
}
#[test]
fn provider_outcome_renderer_preserves_legacy_note_fallback() {
assert_eq!(
render_provider_note(&[], Some("safe legacy note")).as_deref(),
Some("safe legacy note")
);
}
}