1use crate::core::additional_data::AdditionalData;
2use crate::object::lane_validity::LaneValidity;
3use crate::object::orientation::Orientation;
4use crate::road::country_code::CountryCode;
5use crate::road::unit::Unit;
6use crate::signal::dependency::Dependency;
7use crate::signal::position::inertial::PositionInertial;
8use crate::signal::position::road::PositionRoad;
9use crate::signal::position::Position;
10use crate::signal::reference::Reference;
11use std::borrow::Cow;
12use uom::si::angle::radian;
13use uom::si::f64::{Angle, Length};
14use uom::si::length::meter;
15
16pub mod control;
17pub mod controller;
18pub mod dependency;
19pub mod position;
20pub mod reference;
21pub mod signal_reference;
22pub mod signals;
23
24#[derive(Debug, Clone, PartialEq)]
27pub struct Signal {
28 pub validity: Vec<LaneValidity>,
29 pub dependency: Vec<Dependency>,
30 pub reference: Vec<Reference>,
31 pub choice: Option<Position>,
32 pub country: Option<CountryCode>,
34 pub country_revision: Option<String>,
36 pub dynamic: bool,
38 pub height: Option<Length>,
40 pub h_offset: Option<Length>,
43 pub id: String,
45 pub name: Option<String>,
47 pub orientation: Orientation,
51 pub pitch: Option<Angle>,
53 pub roll: Option<Angle>,
56 pub s: Length,
58 pub subtype: String,
60 pub t: Length,
62 pub text: Option<String>,
65 pub r#type: String,
67 pub unit: Option<Unit>,
69 pub value: Option<f64>,
71 pub width: Option<Length>,
73 pub z_offset: Length,
76 pub additional_data: AdditionalData,
77}
78
79impl Signal {
80 pub fn visit_attributes(
81 &self,
82 visitor: impl for<'b> FnOnce(
83 Cow<'b, [xml::attribute::Attribute<'b>]>,
84 ) -> xml::writer::Result<()>,
85 ) -> xml::writer::Result<()> {
86 visit_attributes_flatten!(
87 visitor,
88 "country" => self.country.as_ref().map(CountryCode::as_str),
89 "countryRevision" => self.country_revision.as_deref(),
90 "dynamic" => Some(if self.dynamic { "yes" } else { "no" }),
91 "height" => self.height.map(|v| v.value.to_scientific_string()).as_deref(),
92 "hOffset" => self.h_offset.map(|v| v.value.to_scientific_string()).as_deref(),
93 "id" => Some(self.id.as_str()),
94 "name" => self.name.as_deref(),
95 "orientation" => Some(self.orientation.as_str()),
96 "pitch" => self.pitch.map(|v| v.value.to_scientific_string()).as_deref(),
97 "roll" => self.roll.map(|v| v.value.to_scientific_string()).as_deref(),
98 "s" => Some(self.s.value.to_scientific_string()).as_deref(),
99 "subtype" => Some(self.subtype.as_str()),
100 "t" => Some(self.t.value.to_scientific_string()).as_deref(),
101 "text" => self.text.as_deref(),
102 "type" => Some(self.r#type.as_str()),
103 "unit" => self.unit.as_ref().map(Unit::as_str),
104 "value" => self.value.map(|v| v.to_scientific_string()).as_deref(),
105 "width" => self.width.map(|v| v.value.to_scientific_string()).as_deref(),
106 "zOffset" => Some(self.z_offset.value.to_scientific_string()).as_deref(),
107 )
108 }
109
110 pub fn visit_children(
111 &self,
112 mut visitor: impl FnMut(xml::writer::XmlEvent) -> xml::writer::Result<()>,
113 ) -> xml::writer::Result<()> {
114 for validity in &self.validity {
115 visit_children!(visitor, "validity" => validity);
116 }
117
118 for dependency in &self.dependency {
119 visit_children!(visitor, "dependency" => dependency);
120 }
121
122 for reference in &self.reference {
123 visit_children!(visitor, "reference" => reference);
124 }
125
126 match &self.choice {
127 Some(Position::Inertial(v)) => visit_children!(visitor, "positionInertial" => v),
128 Some(Position::Road(v)) => visit_children!(visitor, "positionRoad" => v),
129 None => {}
130 }
131
132 self.additional_data.append_children(visitor)
133 }
134}
135
136impl<'a, I> TryFrom<crate::parser::ReadContext<'a, I>> for Signal
137where
138 I: Iterator<Item = xml::reader::Result<xml::reader::XmlEvent>>,
139{
140 type Error = Box<crate::parser::Error>;
141
142 fn try_from(mut read: crate::parser::ReadContext<'a, I>) -> Result<Self, Self::Error> {
143 let mut validity = Vec::new();
144 let mut dependency = Vec::new();
145 let mut reference = Vec::new();
146 let mut choice = None;
147 let mut additional_data = AdditionalData::default();
148
149 match_child_eq_ignore_ascii_case!(
150 read,
151 "validity" => LaneValidity => |v| validity.push(v),
152 "dependency" => Dependency => |v| dependency.push(v),
153 "reference" => Reference => |v| reference.push(v),
154 "positionInertial" => PositionInertial => |v| choice = Some(Position::Inertial(v)),
155 "positionRoad" => PositionRoad => |v| choice = Some(Position::Road(v)),
156 _ => |_name, context| additional_data.fill(context),
157 );
158
159 Ok(Self {
160 validity,
161 dependency,
162 reference,
163 choice,
164 country: read.attribute_opt("country")?,
165 country_revision: read.attribute_opt("countryRevision")?,
166 dynamic: read
167 .attribute::<String>("dynamic")
168 .map(|v| v.eq_ignore_ascii_case("yes"))?,
169 height: read.attribute_opt("height")?.map(Length::new::<meter>),
170 h_offset: read.attribute_opt("hOffset")?.map(Length::new::<meter>),
171 id: read.attribute("id")?,
172 name: read.attribute_opt("name")?,
173 orientation: read.attribute("orientation")?,
174 pitch: read.attribute_opt("pitch")?.map(Angle::new::<radian>),
175 roll: read.attribute_opt("roll")?.map(Angle::new::<radian>),
176 s: read.attribute("s").map(Length::new::<meter>)?,
177 subtype: read.attribute("subtype")?,
178 t: read.attribute("t").map(Length::new::<meter>)?,
179 text: read.attribute_opt("text")?,
180 r#type: read.attribute("type")?,
181 unit: read.attribute_opt("unit")?,
182 value: read.attribute_opt("value")?,
183 width: read.attribute_opt("width")?.map(Length::new::<meter>),
184 z_offset: read.attribute("zOffset").map(Length::new::<meter>)?,
185 additional_data,
186 })
187 }
188}
189
190#[cfg(feature = "fuzzing")]
191impl arbitrary::Arbitrary<'_> for Signal {
192 fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
193 use crate::fuzzing::NotNan;
194 Ok(Self {
195 validity: u.arbitrary()?,
196 dependency: u.arbitrary()?,
197 reference: u.arbitrary()?,
198 choice: u.arbitrary()?,
199 country: u.arbitrary()?,
200 country_revision: u.arbitrary()?,
201 dynamic: u.arbitrary()?,
202 height: u
203 .arbitrary::<Option<()>>()?
204 .map(|_| u.not_nan_f64().map(Length::new::<meter>))
205 .transpose()?,
206 h_offset: u
207 .arbitrary::<Option<()>>()?
208 .map(|_| u.not_nan_f64().map(Length::new::<meter>))
209 .transpose()?,
210 id: u.arbitrary()?,
211 name: u.arbitrary()?,
212 orientation: u.arbitrary()?,
213 pitch: u
214 .arbitrary::<Option<()>>()?
215 .map(|_| u.not_nan_f64().map(Angle::new::<radian>))
216 .transpose()?,
217 roll: u
218 .arbitrary::<Option<()>>()?
219 .map(|_| u.not_nan_f64().map(Angle::new::<radian>))
220 .transpose()?,
221 s: Length::new::<meter>(u.not_nan_f64()?),
222 subtype: u.arbitrary()?,
223 t: Length::new::<meter>(u.not_nan_f64()?),
224 text: u.arbitrary()?,
225 r#type: u.arbitrary()?,
226 unit: u.arbitrary()?,
227 value: u
228 .arbitrary::<Option<()>>()?
229 .map(|_| u.not_nan_f64())
230 .transpose()?,
231 width: u
232 .arbitrary::<Option<()>>()?
233 .map(|_| u.not_nan_f64().map(Length::new::<meter>))
234 .transpose()?,
235 z_offset: Length::new::<meter>(u.not_nan_f64()?),
236 additional_data: u.arbitrary()?,
237 })
238 }
239}