use crate::builder::actions::base::{ActionBuilder, ManeuverAction};
use crate::builder::{BuilderError, BuilderResult};
use crate::types::{
actions::movement::{AssignRouteAction, FollowRouteAction, RoutingAction},
actions::wrappers::PrivateAction,
routing::{Route, RouteRef},
};
#[derive(Debug, Default)]
pub struct AssignRouteActionBuilder {
entity_ref: Option<String>,
route_ref: Option<RouteRef>,
}
impl AssignRouteActionBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn for_entity(mut self, entity_ref: &str) -> Self {
self.entity_ref = Some(entity_ref.to_string());
self
}
pub fn with_direct_route(mut self, route: Route) -> Self {
self.route_ref = Some(RouteRef::direct(route));
self
}
pub fn with_catalog_route(
mut self,
catalog_name: impl Into<String>,
entry_name: impl Into<String>,
) -> Self {
self.route_ref = Some(RouteRef::catalog(catalog_name, entry_name));
self
}
pub fn with_route_ref(mut self, route_ref: RouteRef) -> Self {
self.route_ref = Some(route_ref);
self
}
}
impl ActionBuilder for AssignRouteActionBuilder {
fn build_action(self) -> BuilderResult<PrivateAction> {
self.validate()?;
let action = AssignRouteAction {
route: self.route_ref.unwrap(),
};
Ok(PrivateAction::RoutingAction(RoutingAction {
assign_route_action: Some(action),
follow_trajectory_action: None,
follow_route_action: None,
}))
}
fn validate(&self) -> BuilderResult<()> {
if self.route_ref.is_none() {
return Err(BuilderError::validation_error(
"Route reference is required for assign route action",
));
}
Ok(())
}
}
impl ManeuverAction for AssignRouteActionBuilder {
fn entity_ref(&self) -> Option<&str> {
self.entity_ref.as_deref()
}
}
#[derive(Debug, Default)]
pub struct FollowRouteActionBuilder {
entity_ref: Option<String>,
route_ref: Option<RouteRef>,
}
impl FollowRouteActionBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn for_entity(mut self, entity_ref: &str) -> Self {
self.entity_ref = Some(entity_ref.to_string());
self
}
pub fn with_direct_route(mut self, route: Route) -> Self {
self.route_ref = Some(RouteRef::direct(route));
self
}
pub fn with_catalog_route(
mut self,
catalog_name: impl Into<String>,
entry_name: impl Into<String>,
) -> Self {
self.route_ref = Some(RouteRef::catalog(catalog_name, entry_name));
self
}
pub fn with_route_ref(mut self, route_ref: RouteRef) -> Self {
self.route_ref = Some(route_ref);
self
}
}
impl ActionBuilder for FollowRouteActionBuilder {
fn build_action(self) -> BuilderResult<PrivateAction> {
self.validate()?;
let action = FollowRouteAction {
route_ref: self.route_ref.unwrap(),
};
Ok(PrivateAction::RoutingAction(RoutingAction {
assign_route_action: None,
follow_trajectory_action: None,
follow_route_action: Some(action),
}))
}
fn validate(&self) -> BuilderResult<()> {
if self.route_ref.is_none() {
return Err(BuilderError::validation_error(
"Route reference is required for follow route action",
));
}
Ok(())
}
}
impl ManeuverAction for FollowRouteActionBuilder {
fn entity_ref(&self) -> Option<&str> {
self.entity_ref.as_deref()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::enums::RouteStrategy;
use crate::types::routing::Waypoint;
#[test]
fn test_assign_route_direct() {
let route = Route::new("test_route", false).add_waypoint(Waypoint::world_position(
0.0,
0.0,
0.0,
RouteStrategy::Shortest,
));
let builder = AssignRouteActionBuilder::new()
.for_entity("ego")
.with_direct_route(route)
.build_action()
.unwrap();
match builder {
PrivateAction::RoutingAction(ref action) => {
assert!(action.assign_route_action.is_some());
assert!(action.follow_route_action.is_none());
assert!(action.follow_trajectory_action.is_none());
let assign_action = action.assign_route_action.as_ref().unwrap();
assert!(matches!(assign_action.route, RouteRef::Direct(_)));
}
_ => panic!("Expected RoutingAction"),
}
}
#[test]
fn test_assign_route_catalog() {
let builder = AssignRouteActionBuilder::new()
.for_entity("ego")
.with_catalog_route("RouteCatalog", "MyRoute")
.build_action()
.unwrap();
match builder {
PrivateAction::RoutingAction(ref action) => {
let assign_action = action.assign_route_action.as_ref().unwrap();
assert!(matches!(assign_action.route, RouteRef::Catalog(_)));
}
_ => panic!("Expected RoutingAction"),
}
}
#[test]
fn test_assign_route_validation_requires_route() {
let result = AssignRouteActionBuilder::new()
.for_entity("ego")
.build_action();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Route reference is required"));
}
#[test]
fn test_follow_route_direct() {
let route = Route::new("test_route", false);
let builder = FollowRouteActionBuilder::new()
.for_entity("ego")
.with_direct_route(route)
.build_action()
.unwrap();
match builder {
PrivateAction::RoutingAction(ref action) => {
assert!(action.follow_route_action.is_some());
assert!(action.assign_route_action.is_none());
assert!(action.follow_trajectory_action.is_none());
let follow_action = action.follow_route_action.as_ref().unwrap();
assert!(matches!(follow_action.route_ref, RouteRef::Direct(_)));
}
_ => panic!("Expected RoutingAction"),
}
}
#[test]
fn test_follow_route_catalog() {
let builder = FollowRouteActionBuilder::new()
.for_entity("ego")
.with_catalog_route("RouteCatalog", "MyRoute")
.build_action()
.unwrap();
match builder {
PrivateAction::RoutingAction(ref action) => {
let follow_action = action.follow_route_action.as_ref().unwrap();
assert!(matches!(follow_action.route_ref, RouteRef::Catalog(_)));
}
_ => panic!("Expected RoutingAction"),
}
}
#[test]
fn test_follow_route_validation_requires_route() {
let result = FollowRouteActionBuilder::new()
.for_entity("ego")
.build_action();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Route reference is required"));
}
#[test]
fn test_maneuver_action_trait() {
let assign_builder = AssignRouteActionBuilder::new().for_entity("test_entity");
assert_eq!(assign_builder.entity_ref(), Some("test_entity"));
let follow_builder = FollowRouteActionBuilder::new().for_entity("another_entity");
assert_eq!(follow_builder.entity_ref(), Some("another_entity"));
}
}