bpi-rs 0.2.0

Bilibili API client library for Rust
Documentation
use crate::{BpiError, BpiResult};

pub type CookiePair = (String, String);

pub fn parse_cookie_header(cookie_header: &str) -> BpiResult<Vec<CookiePair>> {
    let mut pairs = Vec::new();

    for segment in cookie_header
        .split(';')
        .map(str::trim)
        .filter(|segment| !segment.is_empty())
    {
        let Some((key, value)) = segment.split_once('=') else {
            return Err(BpiError::invalid_parameter(
                "cookie",
                "cookie segment must contain '='",
            ));
        };

        let key = key.trim();
        if key.is_empty() {
            return Err(BpiError::invalid_parameter(
                "cookie",
                "cookie key cannot be empty",
            ));
        }

        pairs.push((key.to_string(), value.trim().to_string()));
    }

    if pairs.is_empty() {
        return Err(BpiError::invalid_parameter(
            "cookie",
            "cookie string cannot be empty",
        ));
    }

    Ok(pairs)
}

pub fn format_cookie_pairs(pairs: &[CookiePair]) -> String {
    pairs
        .iter()
        .map(|(key, value)| format!("{key}={value}"))
        .collect::<Vec<_>>()
        .join("; ")
}

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

    #[test]
    fn parse_cookie_header_returns_named_pairs() -> Result<(), BpiError> {
        let pairs = parse_cookie_header("SESSDATA=session; bili_jct=csrf")?;

        assert_eq!(
            pairs,
            vec![
                ("SESSDATA".to_string(), "session".to_string()),
                ("bili_jct".to_string(), "csrf".to_string())
            ]
        );
        Ok(())
    }

    #[test]
    fn parse_cookie_header_rejects_invalid_segments() {
        let err = parse_cookie_header("SESSDATA").unwrap_err();

        assert!(matches!(
            err,
            BpiError::InvalidParameter {
                field: "cookie",
                ..
            }
        ));
    }

    #[test]
    fn parse_cookie_header_rejects_empty_input() {
        let err = parse_cookie_header(" ; ").unwrap_err();

        assert!(matches!(
            err,
            BpiError::InvalidParameter {
                field: "cookie",
                ..
            }
        ));
    }

    #[test]
    fn format_cookie_pairs_returns_deterministic_header() {
        let pairs = vec![
            ("SESSDATA".to_string(), "session".to_string()),
            ("bili_jct".to_string(), "csrf".to_string()),
        ];

        assert_eq!(
            format_cookie_pairs(&pairs),
            "SESSDATA=session; bili_jct=csrf"
        );
    }
}