1
0
card/api-rs/src/config.rs
syui e7948bf4cf
Add complete ai.card Rust implementation
- Implement complete Rust API server with axum framework
- Add database abstraction supporting PostgreSQL and SQLite
- Implement comprehensive gacha system with probability calculations
- Add JWT authentication with atproto DID integration
- Create card master data system with rarities (Normal, Rare, SuperRare, Kira, Unique)
- Implement draw history tracking and collection management
- Add API endpoints for authentication, card drawing, and collection viewing
- Include database migrations for both PostgreSQL and SQLite
- Maintain full compatibility with Python API implementation
- Add comprehensive documentation and development guide

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-07 17:43:10 +09:00

127 lines
4.1 KiB
Rust

use config::{Config, ConfigError, Environment, File};
use serde::Deserialize;
use std::path::PathBuf;
#[derive(Debug, Clone, Deserialize)]
pub struct Settings {
// Application settings
pub app_name: String,
pub port: u16,
pub api_v1_prefix: String,
// Database settings
pub database_url: String,
pub database_url_supabase: Option<String>,
// Authentication
pub secret_key: String,
pub access_token_expire_minutes: u64,
// Gacha probabilities (percentages)
pub prob_normal: f64,
pub prob_rare: f64,
pub prob_super_rare: f64,
pub prob_kira: f64,
pub prob_unique: f64,
// atproto settings
pub atproto_pds_url: Option<String>,
pub atproto_handle: Option<String>,
// External data
pub card_master_url: String,
// File paths
pub config_dir: PathBuf,
}
impl Settings {
pub fn new() -> Result<Self, ConfigError> {
let config_dir = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".config")
.join("syui")
.join("ai")
.join("card");
// Ensure config directory exists
if !config_dir.exists() {
std::fs::create_dir_all(&config_dir)
.map_err(|e| ConfigError::Message(format!("Failed to create config directory: {}", e)))?;
}
let mut builder = Config::builder()
// Default values
.set_default("app_name", "ai.card")?
.set_default("port", 8000)?
.set_default("api_v1_prefix", "/api/v1")?
// Database defaults
.set_default("database_url", format!("sqlite://{}?mode=rwc", config_dir.join("aicard.db").display()))?
// Authentication defaults
.set_default("secret_key", "your-secret-key-change-in-production")?
.set_default("access_token_expire_minutes", 1440)? // 24 hours
// Gacha probability defaults (matching Python implementation)
.set_default("prob_normal", 99.789)?
.set_default("prob_rare", 0.1)?
.set_default("prob_super_rare", 0.01)?
.set_default("prob_kira", 0.1)?
.set_default("prob_unique", 0.0001)?
// External data source
.set_default("card_master_url", "https://git.syui.ai/ai/ai/raw/branch/main/ai.json")?;
// Load from config file if it exists
let config_file = config_dir.join("config.toml");
if config_file.exists() {
builder = builder.add_source(File::from(config_file));
}
// Override with environment variables (AI_CARD_ prefix)
builder = builder.add_source(Environment::with_prefix("AI_CARD").separator("_"));
let mut settings: Settings = builder.build()?.try_deserialize()?;
// Set the config directory path
settings.config_dir = config_dir;
Ok(settings)
}
/// Get the gacha configuration for the gacha service
pub fn gacha_config(&self) -> GachaConfig {
GachaConfig {
prob_normal: self.prob_normal,
prob_rare: self.prob_rare,
prob_super_rare: self.prob_super_rare,
prob_kira: self.prob_kira,
prob_unique: self.prob_unique,
}
}
}
#[derive(Debug, Clone)]
pub struct GachaConfig {
pub prob_normal: f64,
pub prob_rare: f64,
pub prob_super_rare: f64,
pub prob_kira: f64,
pub prob_unique: f64,
}
impl GachaConfig {
/// Calculate cumulative probabilities for rarity determination
pub fn cumulative_probabilities(&self, is_paid: bool) -> Vec<(f64, crate::models::CardRarity)> {
let multiplier = if is_paid { 2.0 } else { 1.0 };
vec![
(self.prob_unique * multiplier, crate::models::CardRarity::Unique),
(self.prob_kira * multiplier, crate::models::CardRarity::Kira),
(self.prob_super_rare * multiplier, crate::models::CardRarity::SuperRare),
(self.prob_rare * multiplier, crate::models::CardRarity::Rare),
(self.prob_normal, crate::models::CardRarity::Normal),
]
}
}