use crate::error::CompileError;
use crate::expander::Command;
use crate::scheduler::{AgentCommand, TimelineStep};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CommandType {
Straight,
RotateRight,
RotateLeft,
Wait,
}
impl From<Command> for CommandType {
fn from(cmd: Command) -> Self {
match cmd {
Command::Straight => CommandType::Straight,
Command::Right => CommandType::RotateRight,
Command::Left => CommandType::RotateLeft,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToioCommand {
#[serde(rename = "type")]
pub command_type: CommandType,
#[serde(skip_serializing_if = "Option::is_none")]
pub steps: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub angle: Option<i32>,
}
impl ToioCommand {
pub fn straight() -> Self {
Self {
command_type: CommandType::Straight,
steps: Some(1),
angle: None,
}
}
pub fn rotate_right() -> Self {
Self {
command_type: CommandType::RotateRight,
steps: None,
angle: Some(90),
}
}
pub fn rotate_left() -> Self {
Self {
command_type: CommandType::RotateLeft,
steps: None,
angle: Some(-90),
}
}
pub fn wait() -> Self {
Self {
command_type: CommandType::Wait,
steps: None,
angle: None,
}
}
}
impl From<Command> for ToioCommand {
fn from(cmd: Command) -> Self {
match cmd {
Command::Straight => ToioCommand::straight(),
Command::Right => ToioCommand::rotate_right(),
Command::Left => ToioCommand::rotate_left(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompiledAgent {
pub id: u32,
pub commands: Vec<ToioCommand>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimelineEntry {
pub step: usize,
pub agent_commands: Vec<AgentTimelineCommand>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentTimelineCommand {
pub agent_id: u32,
pub command: ToioCommand,
}
impl From<&AgentCommand> for AgentTimelineCommand {
fn from(ac: &AgentCommand) -> Self {
Self {
agent_id: ac.agent_id,
command: ToioCommand::from(ac.command),
}
}
}
impl From<&TimelineStep> for TimelineEntry {
fn from(ts: &TimelineStep) -> Self {
Self {
step: ts.step,
agent_commands: ts.agent_commands.iter().map(|ac| ac.into()).collect(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompiledProgram {
pub agents: Vec<CompiledAgent>,
pub max_steps: usize,
pub timeline: Vec<TimelineEntry>,
}
impl CompiledProgram {
pub fn from_expanded(expanded: &[(u32, Vec<Command>)], timeline: Vec<TimelineStep>) -> Self {
let agents: Vec<CompiledAgent> = expanded
.iter()
.map(|(id, cmds)| CompiledAgent {
id: *id,
commands: cmds.iter().map(|c| ToioCommand::from(*c)).collect(),
})
.collect();
let max_steps = timeline.len();
let timeline: Vec<TimelineEntry> = timeline.iter().map(|ts| ts.into()).collect();
Self {
agents,
max_steps,
timeline,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "status")]
pub enum CompileResult {
#[serde(rename = "success")]
Success {
program: CompiledProgram,
},
#[serde(rename = "error")]
Error {
errors: Vec<CompileError>,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_type_serialization() {
let cmd = ToioCommand::straight();
let json = serde_json::to_string(&cmd).unwrap();
assert!(json.contains("straight"));
}
#[test]
fn test_compile_result_success() {
let program = CompiledProgram {
agents: vec![],
max_steps: 0,
timeline: vec![],
};
let result = CompileResult::Success { program };
let json = serde_json::to_string(&result).unwrap();
assert!(json.contains("success"));
}
#[test]
fn test_compile_result_error() {
let result = CompileResult::Error {
errors: vec![CompileError {
line: 1,
column: 5,
message: "Test error".to_string(),
}],
};
let json = serde_json::to_string(&result).unwrap();
assert!(json.contains("error"));
assert!(json.contains("Test error"));
}
}