zantetsu 0.2.0

Ultra-fast, intelligent library for anime metadata extraction and normalization
Documentation
//! # Zantetsu
//!
//! Ultra-fast library for anime metadata extraction and normalization.
//!
//! Crates:
//! - [`zantetsu`](https://docs.rs/zantetsu) - unified API surface
//! - [`zantetsu-core`](https://docs.rs/zantetsu-core) - parsing engine
//! - [`zantetsu-vecdb`](https://docs.rs/zantetsu-vecdb) - canonical title matching
//!
//! ## Features
//!
//! - **Heuristic Parsing**: Regex-based parsing for fast, reliable extraction (92.38% accuracy)
//! - **Canonical Matching**: Local Kitsu dump matching or remote endpoint lookup
//! - **Quality Scoring**: Configurable quality profiles for release validation
//!
//! ## Quick Start
//!
//! ```rust
//! use zantetsu::{EpisodeSpec, Zantetsu};
//!
//! let engine = Zantetsu::new()?;
//! let result = engine.parse("[SubsPlease] Cowboy Bebop - 01 [1080p][HEVC].mkv")?;
//!
//! assert_eq!(result.title.as_deref(), Some("Cowboy Bebop"));
//! assert_eq!(result.group.as_deref(), Some("SubsPlease"));
//! assert_eq!(result.episode, Some(EpisodeSpec::Single(1)));
//! assert!(result.resolution.is_some());
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
pub use zantetsu_core::error::{Result, ZantetsuError};
pub use zantetsu_core::parser::HeuristicParser;
pub use zantetsu_core::scoring::{QualityProfile, QualityScores};
pub use zantetsu_core::types::{
    AudioCodec, EpisodeSpec, MediaSource, ParseMode, ParseResult, Resolution, VideoCodec,
};
pub use zantetsu_vecdb::{
    AnimeIds, AnimeTitleMatch, MatchProvider, MatchResult, MatchSource, MatcherError, TitleMatcher,
    default_kitsu_dump_dir,
};

/// Main entry point for the Zantetsu parsing engine.
///
/// Wraps the heuristic parser and exposes a simple API for filename parsing and quality scoring.
pub struct Zantetsu {
    heuristic: HeuristicParser,
}

impl Zantetsu {
    /// Create a new Zantetsu engine instance.
    pub fn new() -> Result<Self> {
        let heuristic = HeuristicParser::new()?;
        Ok(Self { heuristic })
    }

    /// Parse an anime filename using the heuristic parser.
    pub fn parse(&self, input: &str) -> Result<ParseResult> {
        self.heuristic.parse(input)
    }

    /// Parse using the heuristic parser explicitly.
    pub fn parse_heuristic(&self, input: &str) -> Result<ParseResult> {
        self.heuristic.parse(input)
    }

    /// Score a parse result using the given quality profile.
    pub fn score(&self, result: &ParseResult, _profile: &QualityProfile) -> QualityScores {
        QualityScores::from_metadata(
            result.resolution,
            result.video_codec,
            result.audio_codec,
            result.source,
            result.confidence,
        )
    }
}

impl Default for Zantetsu {
    fn default() -> Self {
        Self::new().expect("Failed to create Zantetsu engine")
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_standard() {
        let engine = Zantetsu::new().unwrap();
        let result = engine
            .parse("[SubsPlease] Cowboy Bebop - 01 [1080p][HEVC].mkv")
            .unwrap();

        assert_eq!(result.group.as_deref(), Some("SubsPlease"));
        assert_eq!(result.title.as_deref(), Some("Cowboy Bebop"));
        assert_eq!(result.episode, Some(EpisodeSpec::Single(1)));
    }

    #[test]
    fn test_parse_empty_input() {
        let engine = Zantetsu::new().unwrap();
        let result = engine.parse("");
        assert!(result.is_err());
    }

    #[test]
    fn test_heuristic_only() {
        let engine = Zantetsu::new().unwrap();
        let result = engine.parse_heuristic("[Erai-raws] One Piece - 1071 [720p].mkv");
        assert!(result.is_ok());
    }
}