use serde::{Deserialize, Serialize};
use std::collections::HashSet;
pub type MemberId = String;
pub type FactionId = String;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum CultureTag {
RiskTaking,
PsychologicalSafety,
Ruthless,
Bureaucratic,
Fanatic,
Overwork,
Martyrdom,
Custom(String),
}
impl CultureTag {
pub fn description(&self) -> &str {
match self {
CultureTag::RiskTaking => "Innovation over safety - failure is tolerated",
CultureTag::PsychologicalSafety => "Open communication - reporting is rewarded",
CultureTag::Ruthless => "Results over relationships - survival of the fittest",
CultureTag::Bureaucratic => "Process over speed - everything by the book",
CultureTag::Fanatic => "Ideology over life - death holds no fear",
CultureTag::Overwork => "Productivity over health - rest is weakness",
CultureTag::Martyrdom => "Sacrifice is honor - dying for the cause is glorious",
CultureTag::Custom(name) => name,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PersonalityTrait {
Cautious,
Bold,
Competitive,
Collaborative,
Zealous,
Pragmatic,
Workaholic,
Balanced,
Custom(String),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Member {
pub id: MemberId,
pub name: String,
pub personality_traits: HashSet<PersonalityTrait>,
pub stress: f32,
pub fervor: f32,
pub tenure: u32,
}
impl Member {
pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
Self {
id: id.into(),
name: name.into(),
personality_traits: HashSet::new(),
stress: 0.0,
fervor: 0.5, tenure: 0,
}
}
pub fn with_trait(mut self, trait_: PersonalityTrait) -> Self {
self.personality_traits.insert(trait_);
self
}
pub fn with_traits(mut self, traits: Vec<PersonalityTrait>) -> Self {
self.personality_traits.extend(traits);
self
}
pub fn with_stress(mut self, stress: f32) -> Self {
self.stress = stress.clamp(0.0, 1.0);
self
}
pub fn with_fervor(mut self, fervor: f32) -> Self {
self.fervor = fervor.clamp(0.0, 1.0);
self
}
pub fn with_tenure(mut self, tenure: u32) -> Self {
self.tenure = tenure;
self
}
pub fn has_trait(&self, trait_: &PersonalityTrait) -> bool {
self.personality_traits.contains(trait_)
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Alignment {
Aligned {
fervor_bonus: f32,
},
Misaligned {
stress_rate: f32,
reason: String,
},
Neutral,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum CultureEffect {
IncreasedRiskTaking { magnitude: f32 },
ImprovedReporting { magnitude: f32 },
IncreasedCompetition { magnitude: f32 },
IncreasedCaution { magnitude: f32 },
Fearless,
IncreasedProductivity { stress_cost: f32 },
SelfSacrifice { probability: f32 },
Custom {
key: String,
data: serde_json::Value,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CultureError {
FactionNotFound,
MemberNotFound,
InvalidCultureTag,
AlignmentCheckFailed,
}
impl std::fmt::Display for CultureError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CultureError::FactionNotFound => write!(f, "Faction not found"),
CultureError::MemberNotFound => write!(f, "Member not found"),
CultureError::InvalidCultureTag => write!(f, "Invalid culture tag"),
CultureError::AlignmentCheckFailed => write!(f, "Alignment check failed"),
}
}
}
impl std::error::Error for CultureError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_culture_tag_description() {
assert_eq!(
CultureTag::RiskTaking.description(),
"Innovation over safety - failure is tolerated"
);
assert_eq!(
CultureTag::PsychologicalSafety.description(),
"Open communication - reporting is rewarded"
);
}
#[test]
fn test_member_creation() {
let member = Member::new("m1", "Alice");
assert_eq!(member.id, "m1");
assert_eq!(member.name, "Alice");
assert_eq!(member.stress, 0.0);
assert_eq!(member.fervor, 0.5);
assert_eq!(member.tenure, 0);
assert!(member.personality_traits.is_empty());
}
#[test]
fn test_member_with_trait() {
let member = Member::new("m1", "Bob")
.with_trait(PersonalityTrait::Cautious)
.with_trait(PersonalityTrait::Collaborative);
assert_eq!(member.personality_traits.len(), 2);
assert!(member.has_trait(&PersonalityTrait::Cautious));
assert!(member.has_trait(&PersonalityTrait::Collaborative));
assert!(!member.has_trait(&PersonalityTrait::Bold));
}
#[test]
fn test_member_with_traits() {
let member = Member::new("m1", "Charlie")
.with_traits(vec![PersonalityTrait::Bold, PersonalityTrait::Competitive]);
assert_eq!(member.personality_traits.len(), 2);
assert!(member.has_trait(&PersonalityTrait::Bold));
assert!(member.has_trait(&PersonalityTrait::Competitive));
}
#[test]
fn test_member_stress_clamping() {
let member = Member::new("m1", "Test").with_stress(1.5);
assert_eq!(member.stress, 1.0);
let member = Member::new("m2", "Test").with_stress(-0.5);
assert_eq!(member.stress, 0.0);
}
#[test]
fn test_member_fervor_clamping() {
let member = Member::new("m1", "Test").with_fervor(2.0);
assert_eq!(member.fervor, 1.0);
}
#[test]
fn test_alignment_aligned() {
let alignment = Alignment::Aligned { fervor_bonus: 0.1 };
match alignment {
Alignment::Aligned { fervor_bonus } => assert_eq!(fervor_bonus, 0.1),
_ => panic!("Expected Aligned"),
}
}
#[test]
fn test_alignment_misaligned() {
let alignment = Alignment::Misaligned {
stress_rate: 0.05,
reason: "Cautious in RiskTaking culture".to_string(),
};
match alignment {
Alignment::Misaligned {
stress_rate,
reason,
} => {
assert_eq!(stress_rate, 0.05);
assert_eq!(reason, "Cautious in RiskTaking culture");
}
_ => panic!("Expected Misaligned"),
}
}
#[test]
fn test_culture_effect_serialization() {
let effect = CultureEffect::IncreasedRiskTaking { magnitude: 0.3 };
let json = serde_json::to_string(&effect).unwrap();
let deserialized: CultureEffect = serde_json::from_str(&json).unwrap();
assert_eq!(effect, deserialized);
}
#[test]
fn test_culture_error_display() {
let error = CultureError::FactionNotFound;
assert_eq!(error.to_string(), "Faction not found");
let error = CultureError::AlignmentCheckFailed;
assert_eq!(error.to_string(), "Alignment check failed");
}
#[test]
fn test_custom_culture_tag() {
let tag = CultureTag::Custom("Pirate Code".to_string());
assert_eq!(tag.description(), "Pirate Code");
}
#[test]
fn test_member_serialization() {
let member = Member::new("m1", "Test")
.with_trait(PersonalityTrait::Bold)
.with_stress(0.7)
.with_fervor(0.8);
let json = serde_json::to_string(&member).unwrap();
let deserialized: Member = serde_json::from_str(&json).unwrap();
assert_eq!(member, deserialized);
}
}