use crate::backend::MobBackendKind;
use crate::runtime_mode::MobRuntimeMode;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolConfig {
#[serde(default)]
pub builtins: bool,
#[serde(default)]
pub shell: bool,
#[serde(default)]
pub comms: bool,
#[serde(default)]
pub memory: bool,
#[serde(default)]
pub mob: bool,
#[serde(default)]
pub mob_tasks: bool,
#[serde(default)]
pub mcp: Vec<String>,
#[serde(default)]
pub rust_bundles: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Profile {
pub model: String,
#[serde(default)]
pub skills: Vec<String>,
#[serde(default)]
pub tools: ToolConfig,
#[serde(default)]
pub peer_description: String,
#[serde(default)]
pub external_addressable: bool,
#[serde(default)]
pub backend: Option<MobBackendKind>,
#[serde(default)]
pub runtime_mode: MobRuntimeMode,
#[serde(default)]
pub max_inline_peer_notifications: Option<i32>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tool_config_serde_roundtrip() {
let config = ToolConfig {
builtins: true,
shell: false,
comms: true,
memory: false,
mob: true,
mob_tasks: true,
mcp: vec!["server-a".to_string(), "server-b".to_string()],
rust_bundles: vec!["custom-tools".to_string()],
};
let json = serde_json::to_string(&config).unwrap();
let parsed: ToolConfig = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, config);
}
#[test]
fn test_tool_config_toml_roundtrip() {
let config = ToolConfig {
builtins: true,
shell: true,
comms: false,
memory: false,
mob: false,
mob_tasks: false,
mcp: vec!["mcp-server".to_string()],
rust_bundles: Vec::new(),
};
let toml_str = toml::to_string(&config).unwrap();
let parsed: ToolConfig = toml::from_str(&toml_str).unwrap();
assert_eq!(parsed, config);
}
#[test]
fn test_profile_serde_roundtrip() {
let profile = Profile {
model: "claude-opus-4-6".to_string(),
skills: vec!["orchestrator-skill".to_string()],
tools: ToolConfig {
builtins: true,
shell: false,
comms: true,
memory: false,
mob: true,
mob_tasks: true,
mcp: vec![],
rust_bundles: vec![],
},
peer_description: "Orchestrates worker agents".to_string(),
external_addressable: true,
backend: None,
runtime_mode: MobRuntimeMode::AutonomousHost,
max_inline_peer_notifications: None,
};
let json = serde_json::to_string(&profile).unwrap();
let parsed: Profile = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, profile);
}
#[test]
fn test_profile_toml_roundtrip() {
let profile = Profile {
model: "gpt-5.2".to_string(),
skills: vec!["worker-skill".to_string()],
tools: ToolConfig {
builtins: false,
shell: true,
comms: true,
memory: false,
mob: false,
mob_tasks: true,
mcp: vec!["code-server".to_string()],
rust_bundles: vec!["custom".to_string()],
},
peer_description: "Writes code".to_string(),
external_addressable: false,
backend: Some(MobBackendKind::External),
runtime_mode: MobRuntimeMode::TurnDriven,
max_inline_peer_notifications: Some(20),
};
let toml_str = toml::to_string(&profile).unwrap();
let parsed: Profile = toml::from_str(&toml_str).unwrap();
assert_eq!(parsed, profile);
}
#[test]
fn test_tool_config_defaults() {
let config = ToolConfig::default();
assert!(!config.builtins);
assert!(!config.shell);
assert!(!config.comms);
assert!(!config.memory);
assert!(!config.mob);
assert!(!config.mob_tasks);
assert!(config.mcp.is_empty());
assert!(config.rust_bundles.is_empty());
}
#[test]
fn test_profile_default_fields_from_toml() {
let toml_str = r#"
model = "claude-sonnet-4-5"
"#;
let profile: Profile = toml::from_str(toml_str).unwrap();
assert_eq!(profile.model, "claude-sonnet-4-5");
assert!(profile.skills.is_empty());
assert_eq!(profile.tools, ToolConfig::default());
assert_eq!(profile.peer_description, "");
assert!(!profile.external_addressable);
assert_eq!(profile.backend, None);
assert_eq!(profile.runtime_mode, MobRuntimeMode::AutonomousHost);
assert_eq!(profile.max_inline_peer_notifications, None);
}
#[test]
fn test_profile_toml_parses_zero_inline_threshold() {
let toml_str = r#"
model = "claude-sonnet-4-5"
max_inline_peer_notifications = 0
"#;
let profile: Profile = toml::from_str(toml_str).unwrap();
assert_eq!(profile.max_inline_peer_notifications, Some(0));
}
#[test]
fn test_profile_toml_parses_always_inline_threshold() {
let toml_str = r#"
model = "claude-sonnet-4-5"
max_inline_peer_notifications = -1
"#;
let profile: Profile = toml::from_str(toml_str).unwrap();
assert_eq!(profile.max_inline_peer_notifications, Some(-1));
}
}