use crate::eval::yaml_tag_handler::YamlTagHandler;
#[derive(Debug, Clone)]
pub struct RosUnitTagHandler;
impl RosUnitTagHandler {
pub fn new() -> Self {
Self
}
fn get_conversion_factor(tag: &str) -> Option<f64> {
match tag {
"radians" => Some(1.0),
"degrees" => Some(core::f64::consts::PI / 180.0),
"meters" => Some(1.0),
"millimeters" => Some(0.001),
"foot" => Some(0.3048),
"inches" => Some(0.0254),
_ => None,
}
}
}
impl Default for RosUnitTagHandler {
fn default() -> Self {
Self::new()
}
}
impl YamlTagHandler for RosUnitTagHandler {
fn handle_tag(
&self,
tag: &str,
raw_value: &str,
) -> Option<String> {
let conversion_factor = Self::get_conversion_factor(tag)?;
let trimmed = raw_value.trim();
if trimmed.is_empty() {
return None;
}
if let Ok(num) = trimmed.parse::<f64>() {
let result = num * conversion_factor;
return Some(result.to_string());
}
if conversion_factor == 1.0 {
Some(trimmed.to_string())
} else {
Some(format!("({})*{}", trimmed, conversion_factor))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_degrees_numeric() {
let handler = RosUnitTagHandler::new();
let result = handler.handle_tag("degrees", "90.0");
assert!(result.is_some());
let value: f64 = result.unwrap().parse().expect("Should be numeric");
assert!((value - std::f64::consts::FRAC_PI_2).abs() < 1e-10);
}
#[test]
fn test_degrees_expression() {
let handler = RosUnitTagHandler::new();
let result = handler.handle_tag("degrees", "45*2");
assert_eq!(
result,
Some(format!("(45*2)*{}", std::f64::consts::PI / 180.0))
);
}
#[test]
fn test_radians_passthrough() {
let handler = RosUnitTagHandler::new();
let result = handler.handle_tag("radians", "1.57");
assert_eq!(result, Some("1.57".to_string()));
}
#[test]
fn test_millimeters_numeric() {
let handler = RosUnitTagHandler::new();
let result = handler.handle_tag("millimeters", "1000");
assert_eq!(result, Some("1".to_string()));
}
#[test]
fn test_millimeters_expression() {
let handler = RosUnitTagHandler::new();
let result = handler.handle_tag("millimeters", "500*2");
assert_eq!(result, Some("(500*2)*0.001".to_string()));
}
#[test]
fn test_unknown_tag() {
let handler = RosUnitTagHandler::new();
let result = handler.handle_tag("unknown", "42");
assert_eq!(result, None);
}
#[test]
fn test_foot_to_meters() {
let handler = RosUnitTagHandler::new();
let result = handler.handle_tag("foot", "1");
assert_eq!(result, Some("0.3048".to_string()));
}
#[test]
fn test_inches_to_meters() {
let handler = RosUnitTagHandler::new();
let result = handler.handle_tag("inches", "12");
assert!(result.is_some());
let value: f64 = result.unwrap().parse().expect("Should be numeric");
assert!((value - 0.3048).abs() < 1e-10);
}
#[test]
fn test_empty_value_returns_none() {
let handler = RosUnitTagHandler::new();
assert_eq!(handler.handle_tag("degrees", ""), None);
assert_eq!(handler.handle_tag("degrees", " "), None);
assert_eq!(handler.handle_tag("millimeters", " \t "), None);
assert_eq!(handler.handle_tag("radians", "\n"), None);
}
}