vecboost 0.3.0-rc.1

High-performance embedding vector service written in Rust
// Copyright (c) 2025-2026 Kirky.X🌠
// SPDX-License-Identifier: Apache-2.0

use crate::error::VecboostError;
use crate::i18n;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
pub struct LoginRequest {
    pub username: String,
    pub password: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
pub struct RefreshTokenRequest {
    pub refresh_token: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
pub struct AuthResponse {
    pub token: String,
    pub token_type: String,
    pub expires_in: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    pub username: String,
    pub role: String,
    pub permissions: Vec<String>,
}

/// 验证用户名格式:
/// - 长度 3-32 字符
/// - 必须以字母开头
/// - 仅允许字母、数字、下划线和连字符
pub fn validate_username_format(username: &str) -> Result<(), VecboostError> {
    if username.len() < 3 || username.len() > 32 {
        return Err(VecboostError::ValidationError(i18n::tr(
            "auth-username-length",
        )));
    }

    if !username
        .chars()
        .next()
        .map(|c| c.is_ascii_alphabetic())
        .unwrap_or(false)
    {
        return Err(VecboostError::ValidationError(i18n::tr(
            "auth-username-start",
        )));
    }

    if !username
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
    {
        return Err(VecboostError::ValidationError(i18n::tr(
            "auth-username-charset",
        )));
    }

    Ok(())
}

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

    #[test]
    fn test_login_request_serialization() {
        let req = LoginRequest {
            username: "alice".to_string(),
            password: "secret".to_string(),
        };
        let json = serde_json::to_string(&req).unwrap();
        let deserialized: LoginRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.username, "alice");
        assert_eq!(deserialized.password, "secret");
    }

    #[test]
    fn test_auth_response_serialization() {
        let resp = AuthResponse {
            token: "tok123".to_string(),
            token_type: "Bearer".to_string(),
            expires_in: 3600,
        };
        let json = serde_json::to_string(&resp).unwrap();
        let deserialized: AuthResponse = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.token, "tok123");
        assert_eq!(deserialized.token_type, "Bearer");
        assert_eq!(deserialized.expires_in, 3600);
    }

    #[test]
    fn test_refresh_token_request_serialization() {
        let req = RefreshTokenRequest {
            refresh_token: "refresh_tok".to_string(),
        };
        let json = serde_json::to_string(&req).unwrap();
        let deserialized: RefreshTokenRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.refresh_token, "refresh_tok");
    }

    #[test]
    fn test_user_serialization() {
        let user = User {
            username: "bob".to_string(),
            role: "admin".to_string(),
            permissions: vec!["read".to_string(), "write".to_string()],
        };
        let json = serde_json::to_string(&user).unwrap();
        let deserialized: User = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.username, "bob");
        assert_eq!(deserialized.role, "admin");
        assert_eq!(deserialized.permissions.len(), 2);
    }

    #[test]
    fn test_validate_username_format_valid() {
        assert!(validate_username_format("alice").is_ok());
        assert!(validate_username_format("Bob123").is_ok());
        assert!(validate_username_format("a-b").is_ok());
        assert!(validate_username_format("user_name").is_ok());
    }

    #[test]
    fn test_validate_username_format_too_short() {
        assert!(validate_username_format("ab").is_err());
        assert!(validate_username_format("").is_err());
    }

    #[test]
    fn test_validate_username_format_too_long() {
        let long_name = "a".repeat(33);
        assert!(validate_username_format(&long_name).is_err());
    }

    #[test]
    fn test_validate_username_format_must_start_with_letter() {
        assert!(validate_username_format("1user").is_err());
        assert!(validate_username_format("_user").is_err());
        assert!(validate_username_format("-user").is_err());
    }

    #[test]
    fn test_validate_username_format_invalid_chars() {
        assert!(validate_username_format("user name").is_err());
        assert!(validate_username_format("user@name").is_err());
        assert!(validate_username_format("user.name").is_err());
    }
}