easyofd_core/graph/tight/method/
move_cmd.rs1use crate::xml_element::{XmlElement, XmlElementError, XmlNode};
6
7#[derive(Debug, Clone, PartialEq)]
13pub struct Move {
14 pub point: (f64, f64),
16}
17
18impl Move {
19 #[must_use]
21 pub fn new(x: f64, y: f64) -> Self {
22 Self { point: (x, y) }
23 }
24
25 #[must_use]
27 pub fn to_abbreviated_string(&self) -> String {
28 format!("M {} {}", self.point.0, self.point.1)
29 }
30}
31
32impl std::fmt::Display for Move {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 f.write_str(&self.to_abbreviated_string())
35 }
36}
37
38impl XmlElement for Move {
39 fn element_name(&self) -> &'static str {
41 "Move"
42 }
43
44 fn attributes(&self) -> Vec<(String, String)> {
45 Vec::new()
46 }
47
48 fn write_xml(&self, out: &mut String) {
50 out.push_str("<Move>");
51 out.push_str(&crate::xml_element::xml_escape(
52 &self.to_abbreviated_string(),
53 ));
54 out.push_str("</Move>");
55 }
56
57 fn from_xml(node: &XmlNode) -> Result<Self, XmlElementError> {
58 let text = node
59 .text
60 .as_deref()
61 .ok_or_else(|| XmlElementError("Move 缺少文本内容".to_string()))?;
62 let parts: Vec<&str> = text.split_whitespace().collect();
63 if parts.len() < 3 || parts[0] != "M" {
64 return Err(XmlElementError(format!("Move 格式错误: {text}")));
65 }
66 let x: f64 = parts[1]
67 .parse()
68 .map_err(|e| XmlElementError(format!("解析 Move.x 失败: {e}")))?;
69 let y: f64 = parts[2]
70 .parse()
71 .map_err(|e| XmlElementError(format!("解析 Move.y 失败: {e}")))?;
72 Ok(Self::new(x, y))
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use crate::xml_parse::parse_xml_to_nodes;
80
81 #[test]
82 fn move_new() {
83 let m = Move::new(10.0, 20.0);
84 assert_eq!(m.point, (10.0, 20.0));
85 }
86
87 #[test]
88 fn move_to_string() {
89 let m = Move::new(0.0, 0.0);
90 assert_eq!(m.to_abbreviated_string(), "M 0 0");
91 }
92
93 #[test]
94 fn move_display() {
95 let m = Move::new(5.5, 3.3);
96 let s = format!("{m}");
97 assert!(s.contains("M 5.5 3.3"));
98 }
99
100 #[test]
101 fn move_clone_eq() {
102 let m = Move::new(1.0, 2.0);
103 let m2 = m.clone();
104 assert_eq!(m, m2);
105 }
106
107 #[test]
108 fn test_xml_element_name() {
109 let m = Move::new(1.0, 2.0);
110 assert_eq!(m.element_name(), "Move");
111 }
112
113 #[test]
114 fn test_xml_element_roundtrip() {
115 let m = Move::new(10.5, 20.5);
116 let xml = m.to_xml();
117 assert!(xml.contains("<Move>"));
118 assert!(xml.contains("M 10.5 20.5"));
119 let node = parse_xml_to_nodes(&xml).unwrap();
120 let m2 = Move::from_xml(&node).unwrap();
121 assert!((m.point.0 - m2.point.0).abs() < f64::EPSILON);
122 assert!((m.point.1 - m2.point.1).abs() < f64::EPSILON);
123 }
124}