use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum MosCode {
Alpha18A,
Alpha180A,
Zulu18Z,
Fox18F,
Bravo18B,
Echo18E,
Delta18D,
Charlie18C,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum OdaSlot {
VentureCommander,
OperationsDeputy,
ProcessArchitect,
DeepResearcher,
GrowthEngineer1,
GrowthEngineer2,
Communications1,
Communications2,
HrPeopleOps1,
HrPeopleOps2,
PlatformEngineer1,
PlatformEngineer2,
}
impl OdaSlot {
pub const ALL: [OdaSlot; 12] = [
Self::VentureCommander,
Self::OperationsDeputy,
Self::ProcessArchitect,
Self::DeepResearcher,
Self::GrowthEngineer1,
Self::GrowthEngineer2,
Self::Communications1,
Self::Communications2,
Self::HrPeopleOps1,
Self::HrPeopleOps2,
Self::PlatformEngineer1,
Self::PlatformEngineer2,
];
pub const FOUNDERS: [OdaSlot; 2] = [Self::HrPeopleOps1, Self::DeepResearcher];
#[must_use]
pub const fn mos_code(&self) -> MosCode {
match self {
Self::VentureCommander => MosCode::Alpha18A,
Self::OperationsDeputy => MosCode::Alpha180A,
Self::ProcessArchitect => MosCode::Zulu18Z,
Self::DeepResearcher => MosCode::Fox18F,
Self::GrowthEngineer1 | Self::GrowthEngineer2 => MosCode::Bravo18B,
Self::Communications1 | Self::Communications2 => MosCode::Echo18E,
Self::HrPeopleOps1 | Self::HrPeopleOps2 => MosCode::Delta18D,
Self::PlatformEngineer1 | Self::PlatformEngineer2 => MosCode::Charlie18C,
}
}
#[must_use]
pub const fn is_founding(&self) -> bool {
matches!(self, Self::HrPeopleOps1 | Self::DeepResearcher)
}
#[must_use]
pub const fn display_name(&self) -> &'static str {
match self {
Self::VentureCommander => "Venture Commander",
Self::OperationsDeputy => "Operations Deputy",
Self::ProcessArchitect => "Process Architect",
Self::DeepResearcher => "Deep Researcher",
Self::GrowthEngineer1 => "Growth Engineer 1",
Self::GrowthEngineer2 => "Growth Engineer 2",
Self::Communications1 => "Communications 1",
Self::Communications2 => "Communications 2",
Self::HrPeopleOps1 => "HR/People Ops 1",
Self::HrPeopleOps2 => "HR/People Ops 2",
Self::PlatformEngineer1 => "Platform Engineer 1",
Self::PlatformEngineer2 => "Platform Engineer 2",
}
}
#[must_use]
pub const fn slug(&self) -> &'static str {
match self {
Self::VentureCommander => "venturecommander",
Self::OperationsDeputy => "operationsdeputy",
Self::ProcessArchitect => "processarchitect",
Self::DeepResearcher => "deepresearcher",
Self::GrowthEngineer1 => "growthengineer1",
Self::GrowthEngineer2 => "growthengineer2",
Self::Communications1 => "communications1",
Self::Communications2 => "communications2",
Self::HrPeopleOps1 => "hrpeopleops1",
Self::HrPeopleOps2 => "hrpeopleops2",
Self::PlatformEngineer1 => "platformengineer1",
Self::PlatformEngineer2 => "platformengineer2",
}
}
#[must_use]
pub const fn authority_depth(&self) -> u32 {
match self {
Self::VentureCommander => 0,
Self::OperationsDeputy => 1,
Self::ProcessArchitect => 2,
Self::DeepResearcher => 2,
Self::GrowthEngineer1
| Self::GrowthEngineer2
| Self::Communications1
| Self::Communications2
| Self::HrPeopleOps1
| Self::HrPeopleOps2
| Self::PlatformEngineer1
| Self::PlatformEngineer2 => 3,
}
}
}
impl std::fmt::Display for OdaSlot {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.slug())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_slots_count() {
assert_eq!(OdaSlot::ALL.len(), 12);
}
#[test]
fn founders() {
assert_eq!(OdaSlot::FOUNDERS.len(), 2);
for f in &OdaSlot::FOUNDERS {
assert!(f.is_founding());
}
assert!(!OdaSlot::VentureCommander.is_founding());
assert!(!OdaSlot::PlatformEngineer1.is_founding());
}
#[test]
fn mos_codes() {
assert_eq!(OdaSlot::VentureCommander.mos_code(), MosCode::Alpha18A);
assert_eq!(OdaSlot::DeepResearcher.mos_code(), MosCode::Fox18F);
assert_eq!(OdaSlot::HrPeopleOps1.mos_code(), MosCode::Delta18D);
assert_eq!(OdaSlot::HrPeopleOps2.mos_code(), MosCode::Delta18D);
assert_eq!(OdaSlot::GrowthEngineer1.mos_code(), MosCode::Bravo18B);
assert_eq!(OdaSlot::GrowthEngineer2.mos_code(), MosCode::Bravo18B);
}
#[test]
fn authority_depth_hierarchy() {
assert_eq!(OdaSlot::VentureCommander.authority_depth(), 0);
assert_eq!(OdaSlot::OperationsDeputy.authority_depth(), 1);
assert_eq!(OdaSlot::ProcessArchitect.authority_depth(), 2);
assert_eq!(OdaSlot::PlatformEngineer1.authority_depth(), 3);
}
#[test]
fn display_names() {
for slot in &OdaSlot::ALL {
assert!(!slot.display_name().is_empty());
}
}
#[test]
fn stable_slot_slugs() {
assert_eq!(OdaSlot::VentureCommander.slug(), "venturecommander");
assert_eq!(OdaSlot::HrPeopleOps1.slug(), "hrpeopleops1");
assert_eq!(OdaSlot::PlatformEngineer2.to_string(), "platformengineer2");
}
#[test]
fn slot_serde_roundtrip() {
for slot in &OdaSlot::ALL {
let j = serde_json::to_string(slot).unwrap();
let rt: OdaSlot = serde_json::from_str(&j).unwrap();
assert_eq!(&rt, slot);
}
}
#[test]
fn mos_serde_roundtrip() {
let codes = [
MosCode::Alpha18A,
MosCode::Alpha180A,
MosCode::Zulu18Z,
MosCode::Fox18F,
MosCode::Bravo18B,
MosCode::Echo18E,
MosCode::Delta18D,
MosCode::Charlie18C,
];
for code in &codes {
let j = serde_json::to_string(code).unwrap();
let rt: MosCode = serde_json::from_str(&j).unwrap();
assert_eq!(&rt, code);
}
}
}