use super::PositionBuilder;
use crate::builder::{BuilderError, BuilderResult};
use crate::types::basic::Double;
use crate::types::positions::{Position, WorldPosition};
#[derive(Debug, Clone, Default)]
pub struct WorldPositionBuilder {
x: Option<f64>,
y: Option<f64>,
z: Option<f64>,
h: Option<f64>,
p: Option<f64>,
r: Option<f64>,
}
impl WorldPositionBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn x(mut self, x: f64) -> Self {
self.x = Some(x);
self
}
pub fn y(mut self, y: f64) -> Self {
self.y = Some(y);
self
}
pub fn z(mut self, z: f64) -> Self {
self.z = Some(z);
self
}
pub fn heading(mut self, h: f64) -> Self {
self.h = Some(h);
self
}
pub fn pitch(mut self, p: f64) -> Self {
self.p = Some(p);
self
}
pub fn roll(mut self, r: f64) -> Self {
self.r = Some(r);
self
}
pub fn at_coordinates(mut self, x: f64, y: f64, z: f64) -> Self {
self.x = Some(x);
self.y = Some(y);
self.z = Some(z);
self
}
pub fn with_heading(mut self, h: f64) -> Self {
self.h = Some(h);
self
}
pub fn build(self) -> BuilderResult<Position> {
self.finish()
}
}
impl PositionBuilder for WorldPositionBuilder {
fn finish(self) -> BuilderResult<Position> {
self.validate()?;
let world_position = WorldPosition {
x: Double::literal(self.x.unwrap()),
y: Double::literal(self.y.unwrap()),
z: self.z.map(Double::literal),
h: self.h.map(Double::literal),
p: self.p.map(Double::literal),
r: self.r.map(Double::literal),
};
let mut position = Position::default();
position.world_position = Some(world_position);
Ok(position)
}
fn validate(&self) -> BuilderResult<()> {
if self.x.is_none() {
return Err(BuilderError::validation_error("X coordinate is required"));
}
if self.y.is_none() {
return Err(BuilderError::validation_error("Y coordinate is required"));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_minimal_world_position_xy_only() {
let pos = WorldPositionBuilder::new()
.x(10.0)
.y(20.0)
.finish()
.unwrap();
let wp = pos.world_position.unwrap();
assert_eq!(wp.x.as_literal(), Some(&10.0));
assert_eq!(wp.y.as_literal(), Some(&20.0));
assert!(wp.z.is_none());
assert!(wp.h.is_none());
}
#[test]
fn test_missing_x_fails() {
let result = WorldPositionBuilder::new().y(20.0).finish();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("X coordinate"));
}
#[test]
fn test_missing_y_fails() {
let result = WorldPositionBuilder::new().x(10.0).finish();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Y coordinate"));
}
#[test]
fn test_at_coordinates_with_heading() {
let pos = WorldPositionBuilder::new()
.at_coordinates(1.0, 2.0, 3.0)
.heading(1.57)
.finish()
.unwrap();
let wp = pos.world_position.unwrap();
assert_eq!(wp.z.as_ref().unwrap().as_literal(), Some(&3.0));
assert_eq!(wp.h.as_ref().unwrap().as_literal(), Some(&1.57));
}
#[test]
fn test_build_alias_works_same_as_finish() {
let pos = WorldPositionBuilder::new()
.x(5.0)
.y(6.0)
.build()
.unwrap();
assert!(pos.world_position.is_some());
}
}