Skip to main content

maliput/api/rules/
mod.rs

1// BSD 3-Clause License
2//
3// Copyright (c) 2024, Woven by Toyota.
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are met:
8//
9// * Redistributions of source code must retain the above copyright notice, this
10//   list of conditions and the following disclaimer.
11//
12// * Redistributions in binary form must reproduce the above copyright notice,
13//   this list of conditions and the following disclaimer in the documentation
14//   and/or other materials provided with the distribution.
15//
16// * Neither the name of the copyright holder nor the names of its
17//   contributors may be used to endorse or promote products derived from
18//   this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31use std::collections::HashMap;
32
33use crate::{api::RoadPosition, common::MaliputError};
34use strum_macros::{Display, IntoStaticStr};
35
36/// Interface for accessing the [TrafficLight] in the [super::RoadNetwork]
37pub struct TrafficLightBook<'a> {
38    pub(super) traffic_light_book: &'a maliput_sys::api::rules::ffi::TrafficLightBook,
39}
40
41impl<'a> TrafficLightBook<'a> {
42    /// Gets all the [TrafficLight]s in the [TrafficLightBook]
43    ///
44    /// # Returns
45    /// A vector of [TrafficLight]s
46    pub fn traffic_lights(&self) -> Vec<TrafficLight<'_>> {
47        let traffic_lights_cpp = maliput_sys::api::rules::ffi::TrafficLightBook_TrafficLights(self.traffic_light_book);
48        traffic_lights_cpp
49            .into_iter()
50            .map(|tl| TrafficLight {
51                traffic_light: unsafe { tl.traffic_light.as_ref().expect("") },
52            })
53            .collect::<Vec<TrafficLight>>()
54    }
55
56    /// Gets a [TrafficLight] by its id.
57    ///
58    /// # Arguments
59    /// * `id` - The id of the [TrafficLight].
60    ///
61    /// # Returns
62    /// The [TrafficLight] with the given id.
63    /// If no [TrafficLight] is found with the given id, return None.
64    pub fn get_traffic_light(&self, id: &String) -> Option<TrafficLight<'_>> {
65        let traffic_light = maliput_sys::api::rules::ffi::TrafficLightBook_GetTrafficLight(self.traffic_light_book, id);
66        if traffic_light.is_null() {
67            return None;
68        }
69        Some(TrafficLight {
70            traffic_light: unsafe {
71                traffic_light
72                    .as_ref()
73                    .expect("Unable to get underlying traffic light pointer")
74            },
75        })
76    }
77
78    /// Gets all [TrafficLight]s whose `related_lanes()` includes the given lane ID.
79    ///
80    /// # Arguments
81    /// * `lane_id` - The lane ID to look up.
82    ///
83    /// # Returns
84    /// A vector of [TrafficLight]s associated with the given lane.
85    /// Returns an empty vector if no traffic lights are associated with the lane.
86    pub fn find_by_lane(&self, lane_id: &String) -> Vec<TrafficLight<'_>> {
87        let traffic_lights_cpp =
88            maliput_sys::api::rules::ffi::TrafficLightBook_FindByLane(self.traffic_light_book, lane_id);
89        traffic_lights_cpp
90            .into_iter()
91            .map(|tl| TrafficLight {
92                traffic_light: unsafe { tl.traffic_light.as_ref().expect("TrafficLight pointer is null") },
93            })
94            .collect::<Vec<TrafficLight>>()
95    }
96}
97
98/// Models a traffic light. A traffic light is a physical signaling device
99/// typically located at road intersections. It contains one or more groups of
100/// light bulbs with varying colors and shapes. The lighting patterns of the
101/// bulbs signify right-of-way rule information to the agents navigating the
102/// intersection (e.g., vehicles, bicyclists, pedestrians, etc.). Typically, an
103/// intersection will be managed by multiple traffic lights.
104///
105/// Note that traffic lights are physical manifestations of underlying
106/// right-of-way rules and thus naturally have lower signal-to-noise ratio
107/// relative to the underlying rules. Thus, oracular agents should directly use
108/// the underlying right-of-way rules instead of traffic lights when navigating
109/// intersections. TrafficLight exists for testing autonomous vehicles that do
110/// not have access to right-of-way rules.
111pub struct TrafficLight<'a> {
112    pub traffic_light: &'a maliput_sys::api::rules::ffi::TrafficLight,
113}
114
115impl<'a> TrafficLight<'a> {
116    /// Get the id of the [TrafficLight].
117    ///
118    /// # Returns
119    /// The id of the [TrafficLight].
120    pub fn id(&self) -> String {
121        maliput_sys::api::rules::ffi::TrafficLight_id(self.traffic_light)
122    }
123
124    /// Get the position of the [TrafficLight] in the road network.
125    ///
126    /// # Returns
127    /// An [super::InertialPosition] representing the position of the [TrafficLight] in the road network.
128    pub fn position_road_network(&self) -> super::InertialPosition {
129        let inertial_position = maliput_sys::api::rules::ffi::TrafficLight_position_road_network(self.traffic_light);
130        super::InertialPosition { ip: inertial_position }
131    }
132
133    /// Get the orientation of the [TrafficLight] in the road network.
134    ///
135    /// # Returns
136    /// An [super::Rotation] representing the orientation of the [TrafficLight] in the road network.
137    pub fn orientation_road_network(&self) -> super::Rotation {
138        let rotation = maliput_sys::api::rules::ffi::TrafficLight_orientation_road_network(self.traffic_light);
139        super::Rotation { r: rotation }
140    }
141
142    /// Get the bulb groups of the [TrafficLight].
143    ///
144    /// # Returns
145    /// A vector of [BulbGroup]s in the [TrafficLight].
146    /// If the [TrafficLight] has no bulb groups, return an empty vector.
147    pub fn bulb_groups(&self) -> Vec<BulbGroup<'_>> {
148        let bulb_groups_cpp = maliput_sys::api::rules::ffi::TrafficLight_bulb_groups(self.traffic_light);
149        bulb_groups_cpp
150            .into_iter()
151            .map(|bg| BulbGroup {
152                bulb_group: unsafe { bg.bulb_group.as_ref().expect("") },
153            })
154            .collect::<Vec<BulbGroup>>()
155    }
156
157    /// Get a [BulbGroup] by its id.
158    ///
159    /// # Arguments
160    /// * `id` - The id of the [BulbGroup].
161    ///
162    /// # Returns
163    /// The [BulbGroup] with the given id.
164    /// If no [BulbGroup] is found with the given id, return None.
165    pub fn get_bulb_group(&self, id: &String) -> Option<BulbGroup<'_>> {
166        let bulb_group = maliput_sys::api::rules::ffi::TrafficLight_GetBulbGroup(self.traffic_light, id);
167        if bulb_group.is_null() {
168            return None;
169        }
170        Some(BulbGroup {
171            bulb_group: unsafe {
172                bulb_group
173                    .as_ref()
174                    .expect("Unable to get underlying bulb group pointer")
175            },
176        })
177    }
178
179    /// Get the lane IDs that this traffic light is physically relevant to.
180    ///
181    /// # Returns
182    /// A vector of lane ID strings.
183    pub fn related_lanes(&self) -> Vec<String> {
184        maliput_sys::api::rules::ffi::TrafficLight_related_lanes(self.traffic_light)
185    }
186}
187
188#[derive(Debug, Copy, Clone, PartialEq, Eq)]
189/// Defines the possible bulb colors.
190pub enum BulbColor {
191    Red,
192    Yellow,
193    Green,
194}
195
196#[derive(Debug, Copy, Clone, PartialEq, Eq)]
197/// Defines the possible bulb types.
198pub enum BulbType {
199    Round,
200    /// Arrow with a custom orientation specified by [Bulb::arrow_orientation_rad].
201    Arrow,
202    /// Predefined arrow pointing left.
203    ArrowLeft,
204    /// Predefined arrow pointing right.
205    ArrowRight,
206    /// Predefined arrow pointing up (forward).
207    ArrowUp,
208    /// Predefined arrow pointing upper-left.
209    ArrowUpperLeft,
210    /// Predefined arrow pointing upper-right.
211    ArrowUpperRight,
212    /// U-turn to the left.
213    UTurnLeft,
214    /// U-turn to the right.
215    UTurnRight,
216    /// Pedestrian walk signal.
217    Walk,
218    /// Pedestrian don't walk signal.
219    DontWalk,
220}
221
222#[derive(Debug, Copy, Clone, PartialEq, Eq)]
223/// Defines the possible bulb states.
224pub enum BulbState {
225    Off,
226    On,
227    Blinking,
228}
229
230/// Models a bulb within a bulb group.
231pub struct Bulb<'a> {
232    pub bulb: &'a maliput_sys::api::rules::ffi::Bulb,
233}
234
235impl Bulb<'_> {
236    /// Returns this Bulb instance's unique identifier.
237    ///
238    /// # Returns
239    /// A [UniqueBulbId] representing the unique identifier of the [Bulb].
240    pub fn unique_id(&self) -> UniqueBulbId {
241        UniqueBulbId {
242            unique_bulb_id: maliput_sys::api::rules::ffi::Bulb_unique_id(self.bulb),
243        }
244    }
245
246    /// Get the id of the [Bulb].
247    ///
248    /// # Returns
249    /// The id of the [Bulb].
250    pub fn id(&self) -> String {
251        maliput_sys::api::rules::ffi::Bulb_id(self.bulb)
252    }
253
254    /// Get the color of the [Bulb].
255    ///
256    /// # Returns
257    /// The [BulbColor].
258    pub fn color(&self) -> BulbColor {
259        let color = self.bulb.color();
260        match *color {
261            maliput_sys::api::rules::ffi::BulbColor::kRed => BulbColor::Red,
262            maliput_sys::api::rules::ffi::BulbColor::kYellow => BulbColor::Yellow,
263            maliput_sys::api::rules::ffi::BulbColor::kGreen => BulbColor::Green,
264            _ => panic!("Invalid bulb color"),
265        }
266    }
267
268    /// Get the type of the [Bulb].
269    ///
270    /// # Returns
271    /// The [BulbType].
272    pub fn bulb_type(&self) -> BulbType {
273        let bulb_type = maliput_sys::api::rules::ffi::Bulb_type(self.bulb);
274        match *bulb_type {
275            maliput_sys::api::rules::ffi::BulbType::kRound => BulbType::Round,
276            maliput_sys::api::rules::ffi::BulbType::kArrow => BulbType::Arrow,
277            maliput_sys::api::rules::ffi::BulbType::kArrowLeft => BulbType::ArrowLeft,
278            maliput_sys::api::rules::ffi::BulbType::kArrowRight => BulbType::ArrowRight,
279            maliput_sys::api::rules::ffi::BulbType::kArrowUp => BulbType::ArrowUp,
280            maliput_sys::api::rules::ffi::BulbType::kArrowUpperLeft => BulbType::ArrowUpperLeft,
281            maliput_sys::api::rules::ffi::BulbType::kArrowUpperRight => BulbType::ArrowUpperRight,
282            maliput_sys::api::rules::ffi::BulbType::kUTurnLeft => BulbType::UTurnLeft,
283            maliput_sys::api::rules::ffi::BulbType::kUTurnRight => BulbType::UTurnRight,
284            maliput_sys::api::rules::ffi::BulbType::kWalk => BulbType::Walk,
285            maliput_sys::api::rules::ffi::BulbType::kDontWalk => BulbType::DontWalk,
286            _ => panic!("Invalid bulb type"),
287        }
288    }
289
290    /// Get the position of the [Bulb] in the bulb group.
291    ///
292    /// # Returns
293    /// An [super::InertialPosition] representing the position of the [Bulb] in the bulb group.
294    pub fn position_bulb_group(&self) -> super::InertialPosition {
295        let inertial_position = maliput_sys::api::rules::ffi::Bulb_position_bulb_group(self.bulb);
296        super::InertialPosition { ip: inertial_position }
297    }
298
299    /// Get the orientation of the [Bulb] in the bulb group.
300    ///
301    /// # Returns
302    /// An [super::Rotation] representing the orientation of the [Bulb] in the bulb group.
303    pub fn orientation_bulb_group(&self) -> super::Rotation {
304        let rotation = maliput_sys::api::rules::ffi::Bulb_orientation_bulb_group(self.bulb);
305        super::Rotation { r: rotation }
306    }
307
308    /// Returns the arrow's orientation. Only applicable if [Bulb::bulb_type] returns [BulbType::Arrow].
309    ///
310    /// # Returns
311    /// An `Option<f64>` representing the orientation of the arrow in radians.
312    pub fn arrow_orientation_rad(&self) -> Option<f64> {
313        let arrow_orientation = maliput_sys::api::rules::ffi::Bulb_arrow_orientation_rad(self.bulb);
314        if arrow_orientation.is_null() {
315            return None;
316        }
317        Some(arrow_orientation.value)
318    }
319
320    /// Gets the possible states of the [Bulb].
321    ///
322    /// # Returns
323    /// A vector of [BulbState]s representing the possible states of the [Bulb].
324    pub fn states(&self) -> Vec<BulbState> {
325        let states_cpp = maliput_sys::api::rules::ffi::Bulb_states(self.bulb);
326        states_cpp
327            .into_iter()
328            .map(Bulb::_from_cpp_state_to_rust_state)
329            .collect::<Vec<BulbState>>()
330    }
331
332    /// Gets the default state of the [Bulb].
333    ///
334    /// # Returns
335    /// A [BulbState] representing the default state of the [Bulb].
336    pub fn get_default_state(&self) -> BulbState {
337        let default_state = self.bulb.GetDefaultState();
338        Bulb::_from_cpp_state_to_rust_state(&default_state)
339    }
340
341    /// Check if the given state is possible valid for the [Bulb].
342    ///
343    /// # Arguments
344    /// * `state` - The [BulbState] to check.
345    ///
346    /// # Returns
347    /// A boolean indicating whether the given state is valid for the [Bulb].
348    pub fn is_valid_state(&self, state: &BulbState) -> bool {
349        self.bulb.IsValidState(&Bulb::_from_rust_state_to_cpp_state(state))
350    }
351
352    /// Returns the bounding box of the bulb.
353    ///
354    /// # Returns
355    /// A tuple containing the minimum and maximum points of the bounding box.
356    pub fn bounding_box(&self) -> (crate::math::Vector3, crate::math::Vector3) {
357        let min = maliput_sys::api::rules::ffi::Bulb_bounding_box_min(self.bulb);
358        let max = maliput_sys::api::rules::ffi::Bulb_bounding_box_max(self.bulb);
359        (crate::math::Vector3 { v: min }, crate::math::Vector3 { v: max })
360    }
361
362    /// Returns the parent [BulbGroup] of the bulb.
363    ///
364    /// # Returns
365    /// The parent [BulbGroup] of the bulb.
366    /// If the bulb is not part of any group, return None.
367    pub fn bulb_group(&self) -> BulbGroup<'_> {
368        BulbGroup {
369            bulb_group: unsafe {
370                maliput_sys::api::rules::ffi::Bulb_bulb_group(self.bulb)
371                    .as_ref()
372                    .expect("Unable to get underlying bulb group pointer. The Bulb might not be part of any BulbGroup.")
373            },
374        }
375    }
376
377    /// Convert from the C++ BulbState to the Rust BulbState
378    /// It is expected to be used only internally.
379    ///
380    /// # Arguments
381    /// * `cpp_bulb_state` - The C++ BulbState
382    ///
383    /// # Returns
384    /// The Rust BulbState
385    ///
386    /// # Panics
387    /// If the C++ BulbState is invalid.
388    fn _from_cpp_state_to_rust_state(cpp_bulb_state: &maliput_sys::api::rules::ffi::BulbState) -> BulbState {
389        match *cpp_bulb_state {
390            maliput_sys::api::rules::ffi::BulbState::kOff => BulbState::Off,
391            maliput_sys::api::rules::ffi::BulbState::kOn => BulbState::On,
392            maliput_sys::api::rules::ffi::BulbState::kBlinking => BulbState::Blinking,
393            _ => panic!("Invalid bulb state"),
394        }
395    }
396
397    /// Convert from the Rust BulbState to the C++ BulbState
398    /// It is expected to be used only internally.
399    ///
400    /// # Arguments
401    /// * `rust_bulb_state` - The Rust BulbState
402    ///
403    /// # Returns
404    /// The C++ BulbState
405    fn _from_rust_state_to_cpp_state(rust_bulb_state: &BulbState) -> maliput_sys::api::rules::ffi::BulbState {
406        match rust_bulb_state {
407            BulbState::Off => maliput_sys::api::rules::ffi::BulbState::kOff,
408            BulbState::On => maliput_sys::api::rules::ffi::BulbState::kOn,
409            BulbState::Blinking => maliput_sys::api::rules::ffi::BulbState::kBlinking,
410        }
411    }
412}
413
414/// Models a group of bulbs within a traffic light. All of the bulbs within a
415/// group should share the same approximate orientation. However, this is not
416/// programmatically enforced.
417/// About the bulb group pose:
418/// - The position of the bulb group is defined as the linear offset of this bulb group's frame
419///   relative to the frame of the traffic light that contains it. The origin of
420///   this bulb group's frame should approximate the bulb group's CoM.
421/// - The orientation of the bulb group is defined as the rotational offset of this bulb
422///   group's frame relative to the frame of the traffic light that contains it.
423///   The +Z axis should align with the bulb group's "up" direction, and the +X
424///   axis should point in the direction that the bulb group is facing.
425///   Following a right-handed coordinate frame, the +Y axis should point left
426///   when facing the +X direction.
427pub struct BulbGroup<'a> {
428    pub bulb_group: &'a maliput_sys::api::rules::ffi::BulbGroup,
429}
430
431impl BulbGroup<'_> {
432    /// Returns this BulbGroup instance's unique identifier.
433    ///
434    /// # Returns
435    /// A [UniqueBulbGroupId] representing the unique identifier of the [BulbGroup].
436    pub fn unique_id(&self) -> UniqueBulbGroupId {
437        UniqueBulbGroupId {
438            unique_bulb_group_id: maliput_sys::api::rules::ffi::BulbGroup_unique_id(self.bulb_group),
439        }
440    }
441
442    /// Gets the id of the [BulbGroup].
443    ///
444    /// # Returns
445    /// The id of the [BulbGroup].
446    pub fn id(&self) -> String {
447        maliput_sys::api::rules::ffi::BulbGroup_id(self.bulb_group)
448    }
449
450    /// Gets the position of the [BulbGroup] in the traffic light.
451    ///
452    /// # Returns
453    /// An [super::InertialPosition] representing the position of the [BulbGroup] in the traffic light.
454    pub fn position_traffic_light(&self) -> super::InertialPosition {
455        let inertial_position = maliput_sys::api::rules::ffi::BulbGroup_position_traffic_light(self.bulb_group);
456        super::InertialPosition { ip: inertial_position }
457    }
458
459    /// Gets the orientation of the [BulbGroup] in the traffic light.
460    ///
461    /// # Returns
462    /// An [super::Rotation] representing the orientation of the [BulbGroup] in the traffic light.
463    pub fn orientation_traffic_light(&self) -> super::Rotation {
464        let rotation = maliput_sys::api::rules::ffi::BulbGroup_orientation_traffic_light(self.bulb_group);
465        super::Rotation { r: rotation }
466    }
467
468    /// Returns the bulbs in the bulb group.
469    ///
470    /// # Returns
471    /// A vector of [Bulb]s in the bulb group.
472    pub fn bulbs(&self) -> Vec<Bulb<'_>> {
473        let bulbs_cpp = maliput_sys::api::rules::ffi::BulbGroup_bulbs(self.bulb_group);
474        bulbs_cpp
475            .into_iter()
476            .map(|b| Bulb {
477                bulb: unsafe { b.bulb.as_ref().expect("") },
478            })
479            .collect::<Vec<Bulb>>()
480    }
481
482    /// Gets a [Bulb] by its id
483    ///
484    /// # Arguments
485    /// * `id` - The id of the [Bulb].
486    ///
487    /// # Returns
488    /// The [Bulb] with the given id.
489    /// If no [Bulb] is found with the given id, return None.
490    pub fn get_bulb(&self, id: &String) -> Option<Bulb<'_>> {
491        let bulb = maliput_sys::api::rules::ffi::BulbGroup_GetBulb(self.bulb_group, id);
492        if bulb.is_null() {
493            return None;
494        }
495        Some(Bulb {
496            bulb: unsafe { bulb.as_ref().expect("Unable to get underlying bulb pointer") },
497        })
498    }
499
500    /// Returns the parent [TrafficLight] of the bulb group.
501    ///
502    /// # Returns
503    /// The parent [TrafficLight] of the bulb group.
504    pub fn traffic_light(&self) -> TrafficLight<'_> {
505        TrafficLight {
506            traffic_light: unsafe {
507                maliput_sys::api::rules::ffi::BulbGroup_traffic_light(self.bulb_group)
508                    .as_ref()
509                    .expect("Unable to get underlying traffic light pointer. The BulbGroup might not be registered to a TrafficLight.")
510            },
511        }
512    }
513}
514
515/// Uniquely identifies a bulb in the `Inertial` space. This consists of the
516/// concatenation of the bulb's ID, the ID of the bulb group that contains the
517/// bulb, and the the ID of the traffic light that contains the bulb group.
518///
519/// String representation of this ID is:
520/// "`traffic_light_id().string()`-`bulb_group_id.string()`-`bulb_id.string()`"
521pub struct UniqueBulbId {
522    pub(crate) unique_bulb_id: cxx::UniquePtr<maliput_sys::api::rules::ffi::UniqueBulbId>,
523}
524
525impl UniqueBulbId {
526    /// Get the traffic light id of the [UniqueBulbId].
527    ///
528    /// # Returns
529    /// The traffic light id of the [UniqueBulbId].
530    pub fn traffic_light_id(&self) -> String {
531        maliput_sys::api::rules::ffi::UniqueBulbId_traffic_light_id(&self.unique_bulb_id)
532    }
533
534    /// Get the bulb group id of the [UniqueBulbId].
535    ///
536    /// # Returns
537    /// The bulb group id of the [UniqueBulbId].
538    pub fn bulb_group_id(&self) -> String {
539        maliput_sys::api::rules::ffi::UniqueBulbId_bulb_group_id(&self.unique_bulb_id)
540    }
541
542    /// Get the bulb id of the [UniqueBulbId].
543    ///
544    /// # Returns
545    /// The bulb id of the [UniqueBulbId].
546    pub fn bulb_id(&self) -> String {
547        maliput_sys::api::rules::ffi::UniqueBulbId_bulb_id(&self.unique_bulb_id)
548    }
549
550    /// Get the string representation of the [UniqueBulbId].
551    ///
552    /// # Returns
553    /// The string representation of the [UniqueBulbId].
554    pub fn string(&self) -> String {
555        self.unique_bulb_id.string().to_string()
556    }
557}
558
559/// Uniquely identifies a bulb group in the `Inertial` space. This consists of
560/// the concatenation of the ID of the bulb group, and the ID of the traffic
561/// light that contains the bulb group.
562///
563/// String representation of this ID is:
564/// "`traffic_light_id().string()`-`bulb_group_id.string()`"
565pub struct UniqueBulbGroupId {
566    unique_bulb_group_id: cxx::UniquePtr<maliput_sys::api::rules::ffi::UniqueBulbGroupId>,
567}
568
569impl UniqueBulbGroupId {
570    /// Get the traffic light id of the [UniqueBulbGroupId].
571    ///
572    /// # Returns
573    /// The traffic light id of the [UniqueBulbGroupId].
574    pub fn traffic_light_id(&self) -> String {
575        maliput_sys::api::rules::ffi::UniqueBulbGroupId_traffic_light_id(&self.unique_bulb_group_id)
576    }
577
578    /// Get the bulb group id of the [UniqueBulbGroupId].
579    ///
580    /// # Returns
581    /// The bulb group id of the [UniqueBulbGroupId].
582    pub fn bulb_group_id(&self) -> String {
583        maliput_sys::api::rules::ffi::UniqueBulbGroupId_bulb_group_id(&self.unique_bulb_group_id)
584    }
585
586    /// Get the string representation of the [UniqueBulbGroupId].
587    ///
588    /// # Returns
589    /// The string representation of the [UniqueBulbGroupId].
590    pub fn string(&self) -> String {
591        self.unique_bulb_group_id.string().to_string()
592    }
593}
594
595/// Interface for querying types of rules. It includes both Discrete and Range value rules. It
596/// provides a registry of the various rule types.
597pub struct RuleRegistry<'a> {
598    pub(super) rule_registry: &'a maliput_sys::api::rules::ffi::RuleRegistry,
599}
600
601/// Represents the rule values the [RuleRegistry] can contain by their Discrete or Range type.
602pub enum RuleValuesByType {
603    DiscreteValues(Vec<DiscreteValue>),
604    Ranges(Vec<Range>),
605}
606
607impl<'a> RuleRegistry<'a> {
608    /// Returns all [DiscreteValue] rule type IDs.
609    ///
610    /// # Returns
611    /// A vector of [String]s representing rule type IDs that correspond to different
612    /// [DiscreteValue]s in the [RuleRegistry].
613    pub fn get_discrete_value_rule_types(&self) -> Vec<String> {
614        let discrete_value_types =
615            maliput_sys::api::rules::ffi::RuleRegistry_DiscreteValueRuleTypes(self.rule_registry);
616        let discrete_value_types = discrete_value_types
617            .as_ref()
618            .expect("Unable to get underlying discrete value rule types pointer.");
619        discrete_value_types.iter().map(|dvt| dvt.type_id.clone()).collect()
620    }
621
622    /// Returns all [DiscreteValue]s corresponding to the specified `rule_type_id`.
623    ///
624    /// This methods works in tandem with [RuleRegistry::get_discrete_value_rule_types].
625    ///
626    /// # Arguments
627    /// * `rule_type_id` - The id of the rule type.
628    ///
629    /// # Returns
630    /// A vector of [DiscreteValue]s or [None] if the `rule_type_id` doesn't match any type id in
631    /// the [RuleRegistry].
632    pub fn discrete_values_by_type(&self, rule_type_id: String) -> Option<Vec<DiscreteValue>> {
633        let discrete_value_types =
634            maliput_sys::api::rules::ffi::RuleRegistry_DiscreteValueRuleTypes(self.rule_registry);
635        let discrete_value_types = discrete_value_types
636            .as_ref()
637            .expect("Unable to get underlying discrete value rule types pointer.");
638        discrete_value_types
639            .iter()
640            .find(|dvt| dvt.type_id == rule_type_id)
641            .map(|dvt| discrete_values_from_cxx(&dvt.values))
642    }
643
644    /// Returns all [Range] rule type IDs.
645    ///
646    /// # Returns
647    /// A vector of [String]s representing rule type IDs that correspond to different [Range]s in
648    /// the [RuleRegistry].
649    pub fn get_range_rule_types(&self) -> Vec<String> {
650        let range_value_types = maliput_sys::api::rules::ffi::RuleRegistry_RangeValueRuleTypes(self.rule_registry);
651        let range_value_types = range_value_types
652            .as_ref()
653            .expect("Unable to get underlying range rule types pointer.");
654        range_value_types.iter().map(|rvt| rvt.type_id.clone()).collect()
655    }
656
657    /// Returns all [Range]s corresponding to the specified `rule_type_id`.
658    ///
659    /// This methods works in tandem with [RuleRegistry::get_range_rule_types].
660    ///
661    /// # Arguments
662    /// * `rule_type_id` - The id of the rule type.
663    ///
664    /// # Returns
665    /// A vector of [Range]s or [None] if the `rule_type_id` doesn't match any type id in the
666    /// [RuleRegistry].
667    pub fn range_values_by_type(&self, rule_type_id: String) -> Option<Vec<Range>> {
668        let range_value_types = maliput_sys::api::rules::ffi::RuleRegistry_RangeValueRuleTypes(self.rule_registry);
669        let range_value_types = range_value_types
670            .as_ref()
671            .expect("Unable to get underlying range rule types pointer.");
672        range_value_types
673            .iter()
674            .find(|rvt| rvt.type_id == rule_type_id)
675            .map(|rvt| range_values_from_cxx(&rvt.values))
676    }
677
678    /// Returns all possible states for a given `rule_type_id`.
679    ///
680    /// # Arguments
681    /// * `rule_type_id` - The id of the rule type.
682    ///
683    /// # Returns
684    /// An `Option` containing a [RuleValuesByType] enum with either a vector of [Range]s or a
685    /// vector of [DiscreteValue]s. Returns `None` if the `rule_type_id` is not found.
686    pub fn get_possible_states_of_rule_type(&self, rule_type_id: String) -> Option<RuleValuesByType> {
687        if let Some(ranges) = self.range_values_by_type(rule_type_id.clone()) {
688            Some(RuleValuesByType::Ranges(ranges))
689        } else {
690            self.discrete_values_by_type(rule_type_id)
691                .map(RuleValuesByType::DiscreteValues)
692        }
693    }
694}
695
696/// Abstraction for holding the output of [RoadRulebook::rules()] and [RoadRulebook::find_rules()]
697/// methods.
698/// This struct contains a map of [DiscreteValueRule]s and [RangeValueRule]s.
699/// The keys of the map are the ids of the rules.
700/// The values of the map are the rules.
701pub struct QueryResults {
702    pub discrete_value_rules: std::collections::HashMap<String, DiscreteValueRule>,
703    pub range_value_rules: std::collections::HashMap<String, RangeValueRule>,
704}
705
706/// Interface for querying "rules of the road". This interface
707/// provides access to static information about a road network (i.e.,
708/// information determined prior to the beginning of a simulation). Some
709/// rule types may refer to additional dynamic information which will be
710/// provided by other interfaces.
711pub struct RoadRulebook<'a> {
712    pub(super) road_rulebook: &'a maliput_sys::api::rules::ffi::RoadRulebook,
713}
714
715impl<'a> RoadRulebook<'a> {
716    /// Returns the DiscreteValueRule with the specified `id`.
717    ///
718    /// # Arguments
719    /// * `rule_id` - The id of the rule.
720    ///
721    /// # Returns
722    /// The DiscreteValueRule with the given id or None if the id is not in the Rulebook.
723    pub fn get_discrete_value_rule(&self, rule_id: &String) -> Option<DiscreteValueRule> {
724        let discrete_value_rule =
725            maliput_sys::api::rules::ffi::RoadRulebook_GetDiscreteValueRule(self.road_rulebook, rule_id);
726        if discrete_value_rule.is_null() {
727            return None;
728        }
729        Some(DiscreteValueRule { discrete_value_rule })
730    }
731    /// Returns the RangeValueRule with the specified `id`.
732    ///
733    /// # Arguments
734    /// * `rule_id` - The id of the rule.
735    ///
736    /// # Returns
737    /// The RangeValueRule with the given id or None if the id is not in the Rulebook.
738    pub fn get_range_value_rule(&self, rule_id: &String) -> Option<RangeValueRule> {
739        let range_value_rule =
740            maliput_sys::api::rules::ffi::RoadRulebook_GetRangeValueRule(self.road_rulebook, rule_id);
741        if range_value_rule.is_null() {
742            return None;
743        }
744        Some(RangeValueRule { range_value_rule })
745    }
746
747    /// Returns all the rules in the road rulebook.
748    ///
749    /// # Returns
750    /// A [QueryResults] containing all the rules in the road rulebook.
751    pub fn rules(&self) -> QueryResults {
752        let query_results_cpp = maliput_sys::api::rules::ffi::RoadRulebook_Rules(self.road_rulebook);
753        let discrete_value_rules_id =
754            maliput_sys::api::rules::ffi::QueryResults_discrete_value_rules(&query_results_cpp);
755        let range_value_rules_id = maliput_sys::api::rules::ffi::QueryResults_range_value_rules(&query_results_cpp);
756        let mut dvr_map = std::collections::HashMap::new();
757        for rule_id in discrete_value_rules_id {
758            // It is okay to unwrap here since we are iterating valid IDs obtained above.
759            let rule = self.get_discrete_value_rule(&rule_id).unwrap();
760            dvr_map.insert(rule.id(), rule);
761        }
762        let mut rvr_map = std::collections::HashMap::new();
763        for rule_id in range_value_rules_id {
764            // It is okay to unwrap here since we are iterating valid IDs obtained above.
765            let rule = self.get_range_value_rule(&rule_id).unwrap();
766            rvr_map.insert(rule.id(), rule);
767        }
768        QueryResults {
769            discrete_value_rules: dvr_map,
770            range_value_rules: rvr_map,
771        }
772    }
773
774    /// Finds rules that apply to the given lane s ranges.
775    ///
776    /// # Arguments
777    /// * `ranges` - A vector of [super::LaneSRange]s to find rules for.
778    /// * `tolerance` - A tolerance value to use when finding rules.
779    ///
780    /// # Returns
781    /// A [QueryResults] containing the rules that apply to the given lane s ranges.
782    /// If no rules are found, an empty [QueryResults] is returned.
783    ///
784    /// # Errors
785    /// Returns a [MaliputError] if the underlying C++ function fails.
786    pub fn find_rules(&self, ranges: &Vec<super::LaneSRange>, tolerance: f64) -> Result<QueryResults, MaliputError> {
787        let mut ranges_cpp = Vec::new();
788        for range in ranges {
789            ranges_cpp.push(maliput_sys::api::rules::ffi::ConstLaneSRangeRef {
790                lane_s_range: &range.lane_s_range,
791            });
792        }
793        let query_results_cpp =
794            maliput_sys::api::rules::ffi::RoadRulebook_FindRules(self.road_rulebook, &ranges_cpp, tolerance)?;
795
796        let discrete_value_rules_id =
797            maliput_sys::api::rules::ffi::QueryResults_discrete_value_rules(&query_results_cpp);
798        let range_value_rules_id = maliput_sys::api::rules::ffi::QueryResults_range_value_rules(&query_results_cpp);
799        let mut dvr_map = std::collections::HashMap::new();
800        for rule_id in discrete_value_rules_id {
801            if let Some(rule) = self.get_discrete_value_rule(&rule_id) {
802                dvr_map.insert(rule.id(), rule);
803            }
804        }
805        let mut rvr_map = std::collections::HashMap::new();
806        for rule_id in range_value_rules_id {
807            if let Some(rule) = self.get_range_value_rule(&rule_id) {
808                rvr_map.insert(rule.id(), rule);
809            }
810        }
811        Ok(QueryResults {
812            discrete_value_rules: dvr_map,
813            range_value_rules: rvr_map,
814        })
815    }
816}
817
818/// # Rule
819///
820/// A Rule may have multiple states that affect agent behavior while it is
821/// driving through the rule's zone. The possible states of a Rule must be
822/// semantically coherent. The current state of a Rule is given by a
823/// [RuleStateProvider]. States can be:
824///
825/// - range based ([RangeValueRule]).
826/// - discrete ([DiscreteValueRule]).
827///
828/// # DiscreteValueRule
829///
830/// [DiscreteValue]s are defined by a string value.
831/// Semantics of this rule are based on _all_ possible values that this
832/// [DiscreteValueRule::type_id] could have (as specified by RuleRegistry::FindRuleByType()),
833/// not only the subset of values that a specific instance of this rule can
834/// be in.
835pub struct DiscreteValueRule {
836    discrete_value_rule: cxx::UniquePtr<maliput_sys::api::rules::ffi::DiscreteValueRule>,
837}
838
839impl DiscreteValueRule {
840    /// Returns the Id of the rule as a string.
841    ///
842    /// # Returns
843    /// The id of the rule.
844    pub fn id(&self) -> String {
845        maliput_sys::api::rules::ffi::DiscreteValueRule_id(&self.discrete_value_rule)
846    }
847    /// Returns the type of the rule as a string.
848    /// Example: "right-of-way-rule-type-id", "direction-usage-rule-type-id"
849    ///
850    /// # Returns
851    /// The type id of the rule.
852    pub fn type_id(&self) -> String {
853        maliput_sys::api::rules::ffi::DiscreteValueRule_type_id(&self.discrete_value_rule)
854    }
855    /// Returns a [super::LaneSRoute] that represents the zone that the rule applies to.
856    ///
857    /// # Returns
858    /// A [super::LaneSRoute] representing the zone of the rule.
859    pub fn zone(&self) -> super::LaneSRoute {
860        let lane_s_route = maliput_sys::api::rules::ffi::DiscreteValueRule_zone(&self.discrete_value_rule);
861        super::LaneSRoute { lane_s_route }
862    }
863    /// Returns the states of the rule.
864    ///
865    /// # Returns
866    /// A vector of [DiscreteValue]s representing the states of the rule.
867    /// If the rule has no states, an empty vector is returned.
868    pub fn states(&self) -> Vec<DiscreteValue> {
869        discrete_values_from_cxx(self.discrete_value_rule.states())
870    }
871}
872
873impl std::fmt::Debug for DiscreteValueRule {
874    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
875        write!(
876            f,
877            "DiscreteValueRule {{ id: {}, type_id: {}, zone: {:?}, states: {:?} }}",
878            self.id(),
879            self.type_id(),
880            self.zone(),
881            self.states()
882        )
883    }
884}
885
886/// Holds a `Rule` ID and the current state of that `Rule`.
887/// It is usually used as a return type for [super::Intersection::discrete_value_rule_states].
888pub struct DiscreteValueRuleState {
889    /// Rule ID.
890    pub rule_id: String,
891    /// Current state of the rule.
892    pub state: DiscreteValue,
893}
894
895/// # Rule
896///
897/// A Rule may have multiple states that affect agent behavior while it is
898/// driving through the rule's zone. The possible states of a Rule must be
899/// semantically coherent. The current state of a Rule is given by a
900/// [RuleStateProvider]. States can be:
901///
902/// - range based ([RangeValueRule]).
903/// - discrete ([DiscreteValueRule]).
904///
905/// # RangeValueRule
906///
907/// [Range]s describe a numeric range based rule.
908/// Ranges are closed and continuous, defined by a minimum and maximum quantity.
909/// When only one extreme is formally defined, the other should take a
910/// semantically correct value. For example, if a speed limit only specifies a
911/// maximum value, the minimum value is typically zero.
912pub struct RangeValueRule {
913    range_value_rule: cxx::UniquePtr<maliput_sys::api::rules::ffi::RangeValueRule>,
914}
915
916impl RangeValueRule {
917    /// Returns the Id of the rule as a string.
918    ///
919    /// # Returns
920    /// The id of the rule.
921    pub fn id(&self) -> String {
922        maliput_sys::api::rules::ffi::RangeValueRule_id(&self.range_value_rule)
923    }
924    /// Returns the type of the rule as a string.
925    /// Example: "right-of-way-rule-type-id", "direction-usage-rule-type-id"
926    ///
927    /// # Returns
928    /// The type id of the rule.
929    pub fn type_id(&self) -> String {
930        maliput_sys::api::rules::ffi::RangeValueRule_type_id(&self.range_value_rule)
931    }
932    /// Returns a [super::LaneSRoute] that represents the zone that the rule applies to.
933    ///
934    /// # Returns
935    /// A [super::LaneSRoute] representing the zone of the rule.
936    pub fn zone(&self) -> super::LaneSRoute {
937        let lane_s_route = maliput_sys::api::rules::ffi::RangeValueRule_zone(&self.range_value_rule);
938        super::LaneSRoute { lane_s_route }
939    }
940    /// Returns the states of the rule.
941    ///
942    /// # Returns
943    /// A vector of [Range]s representing the states of the rule.
944    /// If the rule has no states, an empty vector is returned.
945    pub fn states(&self) -> Vec<Range> {
946        range_values_from_cxx(self.range_value_rule.states())
947    }
948}
949
950/// Defines a Rule Type.
951///
952/// # RuleType
953///
954/// [RuleType]s provide a way of obtaining a rule type's string defined in
955/// maliput's backend. Since new rule types can be created in a custom manner,
956/// [RuleType] only holds the most common types which are already defined in
957/// the backend.
958#[derive(Display, IntoStaticStr)]
959pub enum RuleType {
960    #[strum(serialize = "Direction-Usage Rule Type")]
961    DirectionUsage,
962    #[strum(serialize = "Right-Of-Way Rule Type")]
963    RightOfWay,
964    #[strum(serialize = "Vehicle-Stop-In-Zone-Behavior Rule Type")]
965    VehicleStopInZoneBehavior,
966    #[strum(serialize = "Speed-Limit Rule Type")]
967    SpeedLimit,
968}
969
970impl RuleType {
971    /// Gets the Rule ID for the [RuleType] and `lane_id`.
972    ///
973    /// # Arguments
974    /// - `lane_id` - The lane ID to get the rule ID from.
975    ///
976    /// # Returns
977    /// A rule ID formatted the way the backend defines it.
978    pub fn get_rule_id(&self, lane_id: &str) -> String {
979        // We rely on maliput_malidrive which define the rule id as:
980        // "<rule_type>/<lane_id>"
981        self.to_string() + "/" + lane_id
982    }
983}
984
985/// Defines a base state for a rule.
986///
987/// # RuleStateBase
988///
989/// - `severity` - The severity of the rule state.
990/// - `related_rules` - A map of related rules. The key is the group name and the value is a vector of rule ids.
991/// - `related_unique_ids` - A map of related unique ids. The key is the group name and the value is a vector of unique ids.
992///
993/// See [DiscreteValueRule] and [RangeValueRule] for more information.
994pub struct RuleStateBase {
995    /// Severity of the rule's state. A non-negative quantity that specifies the
996    /// level of enforcement. The smaller it is, the more strictly the rule is
997    /// enforced. Each rule type can define its own set of severity level
998    /// semantics.
999    severity: i32,
1000    related_rules: cxx::UniquePtr<cxx::CxxVector<maliput_sys::api::rules::ffi::RelatedRule>>,
1001    related_unique_ids: cxx::UniquePtr<cxx::CxxVector<maliput_sys::api::rules::ffi::RelatedUniqueId>>,
1002}
1003
1004/// A trait representing a possible state of a `Rule`.
1005///
1006/// A `Rule` can have multiple states that affect agent behavior. This trait
1007/// provides a common interface for accessing the properties shared by all
1008/// rule states, such as severity and related rules.
1009///
1010/// This trait is implemented by specific state types like [`DiscreteValue`]
1011/// and [`Range`].
1012///
1013/// # Implementors
1014///
1015/// When implementing this trait, you must provide an implementation for the
1016/// [`get_rule_state()`] method, which gives access to the underlying
1017/// [`RuleStateBase`] data. The other methods have default implementations.
1018pub trait RuleState {
1019    /// Gets the underlying [`RuleStateBase`] that contains common state properties.
1020    ///
1021    /// # Returns
1022    /// A reference to the [`RuleStateBase`] that contains the severity, related rules,
1023    /// and related unique ids for the rule state.
1024    fn get_rule_state(&self) -> &RuleStateBase;
1025
1026    /// Returns the severity of the rule state.
1027    ///
1028    /// # Returns
1029    /// An `i32` representing the severity of the rule state.
1030    /// The severity is a numeric value that indicates the importance or urgency of the rule. The lower the value, the more strictly the rule is enforced.
1031    fn severity(&self) -> i32 {
1032        self.get_rule_state().severity
1033    }
1034
1035    /// Returns a map of related rules ids. The key is the group name and the value is a vector of rule ids.
1036    ///
1037    /// # Returns
1038    /// A map of related rules where the key is the group name and the value is a vector of rule ids.
1039    fn related_rules(&self) -> std::collections::HashMap<&String, &Vec<String>> {
1040        self.get_rule_state()
1041            .related_rules
1042            .iter()
1043            .map(|rr| (&rr.group_name, &rr.rule_ids))
1044            .collect::<std::collections::HashMap<&String, &Vec<String>>>()
1045    }
1046    /// Returns a map of related unique ids. The key is the group name and the value is a vector of unique ids.
1047    ///
1048    /// # Returns
1049    /// A map of related unique ids where the key is the group name and the value is a vector of unique ids.
1050    fn related_unique_ids(&self) -> std::collections::HashMap<&String, &Vec<String>> {
1051        self.get_rule_state()
1052            .related_unique_ids
1053            .iter()
1054            .map(|rui| (&rui.group_name, &rui.unique_ids))
1055            .collect::<std::collections::HashMap<&String, &Vec<String>>>()
1056    }
1057}
1058
1059/// Defines a discrete value for a [DiscreteValueRule].
1060/// It extends the [RuleStateBase] with the value of the discrete value.
1061pub struct DiscreteValue {
1062    rule_state: RuleStateBase,
1063    value: String,
1064}
1065
1066impl RuleState for DiscreteValue {
1067    fn get_rule_state(&self) -> &RuleStateBase {
1068        &self.rule_state
1069    }
1070}
1071
1072impl DiscreteValue {
1073    /// Returns the value of the discrete value.
1074    pub fn value(&self) -> &String {
1075        &self.value
1076    }
1077}
1078
1079impl std::fmt::Debug for DiscreteValue {
1080    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1081        write!(
1082            f,
1083            "DiscreteValue {{ value: {}, severity: {}, related_rules: {:?}, related_unique_ids: {:?} }}",
1084            self.value(),
1085            self.severity(),
1086            self.related_rules(),
1087            self.related_unique_ids()
1088        )
1089    }
1090}
1091
1092/// Defines a range value for a [RangeValueRule].
1093/// It extends the [RuleStateBase] with the description, and min and max values of the range.
1094pub struct Range {
1095    rule_state: RuleStateBase,
1096    description: String,
1097    min: f64,
1098    max: f64,
1099}
1100
1101impl RuleState for Range {
1102    fn get_rule_state(&self) -> &RuleStateBase {
1103        &self.rule_state
1104    }
1105}
1106
1107impl Range {
1108    /// Returns the description of the range value.
1109    pub fn description(&self) -> &String {
1110        &self.description
1111    }
1112    /// Returns the minimum value of the range.
1113    pub fn min(&self) -> f64 {
1114        self.min
1115    }
1116    /// Returns the maximum value of the range.
1117    pub fn max(&self) -> f64 {
1118        self.max
1119    }
1120}
1121
1122/// Defines a phase in a traffic rule system.
1123///
1124/// A phase represents a specific state or configuration of traffic signals
1125/// and semantic rules within a traffic control system. Each phase has a unique
1126/// identifier and may include various traffic signal states and rule configurations
1127/// that dictate how traffic should behave during that phase.
1128pub struct Phase {
1129    phase: cxx::UniquePtr<maliput_sys::api::rules::ffi::Phase>,
1130}
1131
1132impl Phase {
1133    /// Gets the id of the [Phase].
1134    ///
1135    /// # Returns
1136    /// The id of the [Phase].
1137    pub fn id(&self) -> String {
1138        maliput_sys::api::rules::ffi::Phase_id(&self.phase)
1139    }
1140
1141    /// Gets the states of all discrete value rules for this phase.
1142    ///
1143    /// # Returns
1144    /// A `HashMap` where the key is the rule ID as a [String] and the value is the
1145    /// [DiscreteValue] state of that rule.
1146    pub fn discrete_value_rule_states(&self) -> HashMap<String, DiscreteValue> {
1147        let rule_states = maliput_sys::api::rules::ffi::Phase_discrete_value_rule_states(&self.phase);
1148        rule_states
1149            .iter()
1150            .map(|state| {
1151                (
1152                    state.rule_id.clone(),
1153                    discrete_value_from_discrete_value_cxx(&state.state),
1154                )
1155            })
1156            .collect()
1157    }
1158
1159    /// Obtains all [UniqueBulbId]s in the [Phase].
1160    ///
1161    /// # Returns
1162    /// A vector of [UniqueBulbId].
1163    pub fn unique_bulb_ids(&self) -> Vec<UniqueBulbId> {
1164        let unique_bulb_ids = maliput_sys::api::rules::ffi::Phase_unique_bulb_ids(&self.phase);
1165        unique_bulb_ids
1166            .iter()
1167            .map(|bulb_id| UniqueBulbId {
1168                unique_bulb_id: maliput_sys::api::rules::ffi::ptr_from_unique_bulb_id(bulb_id),
1169            })
1170            .collect()
1171    }
1172
1173    /// Returns the [BulbState] corresponding to a `bulb_id`.
1174    ///
1175    /// # Arguments
1176    /// * `unique_bulb_id` - The [UniqueBulbId] to get the [BulbState] from.
1177    ///
1178    /// # Returns
1179    /// The [BulbState] the `unique_bulb_id` is in, or [None] if the [UniqueBulbId] is not in this [Phase].
1180    pub fn bulb_state(&self, unique_bulb_id: &UniqueBulbId) -> Option<BulbState> {
1181        let bulb_state = maliput_sys::api::rules::ffi::Phase_bulb_state(&self.phase, &unique_bulb_id.unique_bulb_id);
1182        if bulb_state.is_null() {
1183            return None;
1184        }
1185        Some(match *bulb_state {
1186            maliput_sys::api::rules::ffi::BulbState::kOff => BulbState::Off,
1187            maliput_sys::api::rules::ffi::BulbState::kOn => BulbState::On,
1188            maliput_sys::api::rules::ffi::BulbState::kBlinking => BulbState::Blinking,
1189            _ => return None,
1190        })
1191    }
1192}
1193
1194/// Defines a phase that comes after another [Phase].
1195/// Used as a return type by:
1196///   - [PhaseRing::get_next_phases].
1197pub struct NextPhase {
1198    /// The next phase.
1199    pub next_phase: Phase,
1200    /// The default time before transitioning to the next phase. This is
1201    /// relative to when the current phase began. It is just a recommendation,
1202    /// the actual duration is determined by the PhaseProvider and may depend on
1203    /// events like a vehicle arriving at a left-turn lane or a pedestrian
1204    /// hitting a crosswalk button.
1205    pub duration_until: Option<f64>,
1206}
1207
1208/// Defines a ring of phases in a traffic rule system.
1209///
1210/// A phase ring represents a sequence of phases that a traffic control system
1211/// cycles through.
1212pub struct PhaseRing {
1213    phase_ring: cxx::UniquePtr<maliput_sys::api::rules::ffi::PhaseRing>,
1214}
1215
1216impl PhaseRing {
1217    /// Gets the id of the [PhaseRing].
1218    ///
1219    /// # Returns
1220    /// The id of the [PhaseRing].
1221    pub fn id(&self) -> String {
1222        maliput_sys::api::rules::ffi::PhaseRing_id(&self.phase_ring)
1223    }
1224
1225    /// Gets a [Phase] by its id
1226    ///
1227    /// # Arguments
1228    /// * `id` - The id of the [Phase].
1229    /// # Returns
1230    /// The [Phase] with the given id.
1231    /// If no [Phase] is found with the given id, return None.
1232    pub fn get_phase(&self, id: &String) -> Option<Phase> {
1233        let phase = maliput_sys::api::rules::ffi::PhaseRing_GetPhase(&self.phase_ring, id);
1234        if phase.is_null() {
1235            return None;
1236        }
1237        Some(Phase { phase })
1238    }
1239
1240    /// Returns the ids of all Phases in the PhaseRing.
1241    ///
1242    /// # Returns
1243    /// A vector of strings representing the ids of all Phases in the PhaseRing.
1244    pub fn phases(&self) -> Vec<String> {
1245        maliput_sys::api::rules::ffi::PhaseRing_phases_ids(&self.phase_ring)
1246    }
1247
1248    /// Returns the next phases for a given phase `id`.
1249    ///
1250    /// # Arguments
1251    /// * `id` - The id of the phase to get the next phases from.
1252    ///
1253    /// # Returns
1254    /// A `Result` containing a vector of [NextPhase]s.
1255    ///
1256    /// # Errors
1257    /// Returns a [MaliputError] if the provided `id` is not found in the [PhaseRing].
1258    pub fn get_next_phases(&self, id: &String) -> Result<Vec<NextPhase>, MaliputError> {
1259        let next_phases = maliput_sys::api::rules::ffi::PhaseRing_GetNextPhases(&self.phase_ring, id)?;
1260        Ok(next_phases
1261            .iter()
1262            .map(|np| NextPhase {
1263                next_phase: Phase {
1264                    phase: maliput_sys::api::rules::ffi::PhaseRing_GetPhase(&self.phase_ring, &np.phase_id),
1265                },
1266                duration_until: if np.duration_until.is_null() {
1267                    None
1268                } else {
1269                    Some(np.duration_until.value)
1270                },
1271            })
1272            .collect())
1273    }
1274}
1275
1276/// Defines a book of phase rings in a traffic rule system.
1277pub struct PhaseRingBook<'a> {
1278    pub(super) phase_ring_book: &'a maliput_sys::api::rules::ffi::PhaseRingBook,
1279}
1280
1281impl<'a> PhaseRingBook<'a> {
1282    /// Returns the ids of all PhaseRings in the PhaseRingBook.
1283    ///
1284    /// # Returns
1285    /// A vector of strings representing the ids of all PhaseRings in the PhaseRingBook.
1286    pub fn get_phase_rings_ids(&self) -> Vec<String> {
1287        maliput_sys::api::rules::ffi::PhaseRingBook_GetPhaseRingsId(self.phase_ring_book)
1288    }
1289
1290    /// Returns the PhaseRing with the specified `id`.
1291    ///
1292    /// # Arguments
1293    /// * `phase_ring_id` - The id of the phase ring.
1294    ///
1295    /// # Returns
1296    /// The PhaseRing with the given id or None if the id is not in the PhaseRingBook.
1297    pub fn get_phase_ring(&self, phase_ring_id: &String) -> Option<PhaseRing> {
1298        let phase_ring = maliput_sys::api::rules::ffi::PhaseRingBook_GetPhaseRing(self.phase_ring_book, phase_ring_id);
1299        if phase_ring.is_null() {
1300            return None;
1301        }
1302        Some(PhaseRing { phase_ring })
1303    }
1304
1305    /// Finds the [PhaseRing] that contains the rule with the specified `rule_id`.
1306    ///
1307    /// # Arguments
1308    /// * `rule_id` - The id of the rule.
1309    ///
1310    /// # Returns
1311    /// The [PhaseRing] that contains the rule with the given id or `None` if no [PhaseRing] is found.
1312    pub fn find_phase_ring(&self, rule_id: &String) -> Option<PhaseRing> {
1313        let phase_ring = maliput_sys::api::rules::ffi::PhaseRingBook_FindPhaseRing(self.phase_ring_book, rule_id);
1314        if phase_ring.is_null() {
1315            return None;
1316        }
1317        Some(PhaseRing { phase_ring })
1318    }
1319}
1320
1321/// Defines a next state of a generic type.
1322pub struct NextState<T> {
1323    /// The next state.
1324    pub next_state: T,
1325    /// The default time before transitioning to the next state. This is
1326    /// relative to when the current state began. It is just a recommendation,
1327    /// the actual duration is determined by the StateProvider and may depend on
1328    /// events like a vehicle arriving at a left-turn lane or a pedestrian
1329    /// hitting a crosswalk button.
1330    pub duration_until: Option<f64>,
1331}
1332
1333/// Holds the current and possible next state of a system.
1334/// It is usually returned by the different types of state providers.
1335pub struct StateProviderQuery<T> {
1336    /// The current state.
1337    pub state: T,
1338    /// The next state.
1339    pub next: Option<NextState<T>>,
1340}
1341
1342/// Alias for the [StateProviderQuery] returned by [PhaseProvider::get_phase].
1343pub type PhaseStateProviderQuery = StateProviderQuery<String>;
1344
1345/// Defines a phase provider.
1346///
1347/// A phase provider is able to get the current phase from a phase-based system.
1348pub struct PhaseProvider<'a> {
1349    pub(super) phase_provider: &'a maliput_sys::api::rules::ffi::PhaseProvider,
1350}
1351
1352impl<'a> PhaseProvider<'a> {
1353    /// Returns the [PhaseStateProviderQuery] for the specified `phase_ring_id`.
1354    ///
1355    /// The states are represented with Strings containing the IDs of each [Phase].
1356    ///
1357    /// # Arguments
1358    /// * `phase_ring_id` - The id of the phase ring.
1359    ///
1360    /// # Returns
1361    /// An `Option` containing the [PhaseStateProviderQuery] for the given `phase_ring_id`.
1362    /// Returns `None` if no phase provider is found for the given id.
1363    pub fn get_phase(&self, phase_ring_id: &String) -> Option<PhaseStateProviderQuery> {
1364        let phase_state = maliput_sys::api::rules::ffi::PhaseProvider_GetPhase(self.phase_provider, phase_ring_id);
1365        if phase_state.is_null() {
1366            return None;
1367        }
1368
1369        let next_state = maliput_sys::api::rules::ffi::PhaseStateProvider_next(&phase_state);
1370        let next_phase = if next_state.is_null() {
1371            None
1372        } else {
1373            Some(NextState {
1374                next_state: next_state.phase_id.clone(),
1375                duration_until: if next_state.duration_until.is_null() {
1376                    None
1377                } else {
1378                    Some(next_state.duration_until.value)
1379                },
1380            })
1381        };
1382
1383        Some(StateProviderQuery {
1384            state: maliput_sys::api::rules::ffi::PhaseStateProvider_state(&phase_state),
1385            next: next_phase,
1386        })
1387    }
1388}
1389
1390/// Provides the dynamic state of [DiscreteValueRule]s.
1391///
1392/// While a [RoadRulebook] provides the static definitions of rules, a
1393/// `DiscreteValueRuleStateProvider` provides the current state of those rules
1394/// at runtime. This allows for querying what state a rule is currently in,
1395/// which is essential for dynamic systems where rule states can change over
1396/// time (e.g., traffic light phases changing).
1397pub struct DiscreteValueRuleStateProvider<'a> {
1398    pub(super) state_provider: &'a maliput_sys::api::rules::ffi::DiscreteValueRuleStateProvider,
1399}
1400
1401impl<'a> DiscreteValueRuleStateProvider<'a> {
1402    /// Gets a state from the provider based on it's `rule_id`.
1403    ///
1404    /// # Arguments
1405    /// * `rule_id` - A Rule ID.
1406    ///
1407    /// # Returns
1408    /// An Option containing the [StateProviderQuery] with a [DiscreteValue] if the `rule_id` matches with any rule.
1409    /// Otherwise, None is returned.
1410    pub fn get_state_by_rule_id(&self, rule_id: &String) -> Option<StateProviderQuery<DiscreteValue>> {
1411        let query_state =
1412            maliput_sys::api::rules::ffi::DiscreteValueRuleStateProvider_GetStateById(self.state_provider, rule_id);
1413        Self::next_state_from_cxx_query(query_state)
1414    }
1415
1416    /// Gets a state from the provider if there is a `rule_type` in the received `road_position`.
1417    ///
1418    /// # Arguments
1419    /// * `road_position` - A position in the road geometry.
1420    /// * `rule_type` - A Rule Type.
1421    /// * `tolerance` - The tolerance in which to look for the Rule of type `rule_type` around the `road_position`.
1422    ///
1423    /// # Returns
1424    /// An Option containing the [StateProviderQuery] with a [DiscreteValue] if `rule_type` matches with any rule's type near `road_position`.
1425    /// Otherwise, None is returned.
1426    pub fn get_state_by_rule_type(
1427        &self,
1428        road_position: &RoadPosition,
1429        rule_type: RuleType,
1430        tolerance: f64,
1431    ) -> Option<StateProviderQuery<DiscreteValue>> {
1432        let query_state = maliput_sys::api::rules::ffi::DiscreteValueRuleStateProvider_GetStateByType(
1433            self.state_provider,
1434            &road_position.rp,
1435            &rule_type.to_string(),
1436            tolerance,
1437        );
1438        Self::next_state_from_cxx_query(query_state)
1439    }
1440
1441    // Internal helper to avoid code duplication.
1442    fn next_state_from_cxx_query(
1443        query_state: cxx::UniquePtr<maliput_sys::api::rules::ffi::DiscreteValueRuleStateProviderQuery>,
1444    ) -> Option<StateProviderQuery<DiscreteValue>> {
1445        if query_state.is_null() {
1446            return None;
1447        }
1448        let next_state = maliput_sys::api::rules::ffi::DiscreteValueRuleStateProviderQuery_next(&query_state);
1449        Some(StateProviderQuery {
1450            state: discrete_value_from_discrete_value_cxx(
1451                &maliput_sys::api::rules::ffi::DiscreteValueRuleStateProviderQuery_state(&query_state),
1452            ),
1453            next: if next_state.is_null() {
1454                None
1455            } else {
1456                Some(NextState {
1457                    next_state: discrete_value_from_discrete_value_cxx(&next_state.state),
1458                    duration_until: if next_state.duration_until.is_null() {
1459                        None
1460                    } else {
1461                        Some(next_state.duration_until.value)
1462                    },
1463                })
1464            },
1465        })
1466    }
1467}
1468
1469/// Provides the dynamic state of [RangeValueRule]s.
1470///
1471/// While a [RoadRulebook] provides the static definitions of rules, a
1472/// `RangeValueRuleStateProvider` provides the current state of those rules
1473/// at runtime. This allows for querying what state a rule is currently in,
1474/// which is essential for dynamic systems where rule states can change over
1475/// time (e.g., variable speed limits based on types of roads).
1476pub struct RangeValueRuleStateProvider<'a> {
1477    pub(super) state_provider: &'a maliput_sys::api::rules::ffi::RangeValueRuleStateProvider,
1478}
1479
1480impl<'a> RangeValueRuleStateProvider<'a> {
1481    /// Gets a state from the provider based on it's `rule_id`.
1482    ///
1483    /// # Arguments
1484    /// * `rule_id` - A Rule ID.
1485    ///
1486    /// # Returns
1487    /// An Option containing the [StateProviderQuery] with a [Range] if the `rule_id` matches with any rule.
1488    /// Otherwise, None is returned.
1489    pub fn get_state_by_rule_id(&self, rule_id: &String) -> Option<StateProviderQuery<Range>> {
1490        let query_state =
1491            maliput_sys::api::rules::ffi::RangeValueRuleStateProvider_GetStateById(self.state_provider, rule_id);
1492        Self::next_state_from_cxx_query(query_state)
1493    }
1494
1495    /// Gets a state from the provider if there is a `rule_type` in the received `road_position`.
1496    ///
1497    /// # Arguments
1498    /// * `road_position` - A position in the road geometry.
1499    /// * `rule_type` - A Rule Type.
1500    /// * `tolerance` - The tolerance in which to look for the Rule of type `rule_type` around the `road_position`.
1501    ///
1502    /// # Returns
1503    /// An Option containing the [StateProviderQuery] with a [Range] if `rule_type` matches with any rule's type near `road_position`.
1504    /// Otherwise, None is returned.
1505    pub fn get_state_by_rule_type(
1506        &self,
1507        road_position: &RoadPosition,
1508        rule_type: RuleType,
1509        tolerance: f64,
1510    ) -> Option<StateProviderQuery<Range>> {
1511        let query_state = maliput_sys::api::rules::ffi::RangeValueRuleStateProvider_GetStateByType(
1512            self.state_provider,
1513            &road_position.rp,
1514            &rule_type.to_string(),
1515            tolerance,
1516        );
1517        Self::next_state_from_cxx_query(query_state)
1518    }
1519
1520    // Internal helper to avoid code duplication.
1521    fn next_state_from_cxx_query(
1522        query_state: cxx::UniquePtr<maliput_sys::api::rules::ffi::RangeValueRuleStateProviderQuery>,
1523    ) -> Option<StateProviderQuery<Range>> {
1524        if query_state.is_null() {
1525            return None;
1526        }
1527        let next_state = maliput_sys::api::rules::ffi::RangeValueRuleStateProviderQuery_next(&query_state);
1528        Some(StateProviderQuery {
1529            state: range_value_from_range_value_cxx(
1530                &maliput_sys::api::rules::ffi::RangeValueRuleStateProviderQuery_state(&query_state),
1531            ),
1532            next: if next_state.is_null() {
1533                None
1534            } else {
1535                Some(NextState {
1536                    next_state: range_value_from_range_value_cxx(&next_state.state),
1537                    duration_until: if next_state.duration_until.is_null() {
1538                        None
1539                    } else {
1540                        Some(next_state.duration_until.value)
1541                    },
1542                })
1543            },
1544        })
1545    }
1546}
1547
1548// Auxiliary method to create a [Vec<Range>] from a [cxx::Vector<RangeValueRuleRange>].
1549fn range_values_from_cxx(
1550    range_values_cxx: &cxx::Vector<maliput_sys::api::rules::ffi::RangeValueRuleRange>,
1551) -> Vec<Range> {
1552    range_values_cxx
1553        .iter()
1554        .map(|range| Range {
1555            rule_state: RuleStateBase {
1556                severity: maliput_sys::api::rules::ffi::RangeValueRuleRange_severity(range),
1557                related_rules: maliput_sys::api::rules::ffi::RangeValueRuleRange_related_rules(range),
1558                related_unique_ids: maliput_sys::api::rules::ffi::RangeValueRuleRange_related_unique_ids(range),
1559            },
1560            description: maliput_sys::api::rules::ffi::RangeValueRuleRange_description(range),
1561            min: maliput_sys::api::rules::ffi::RangeValueRuleRange_min(range),
1562            max: maliput_sys::api::rules::ffi::RangeValueRuleRange_max(range),
1563        })
1564        .collect()
1565}
1566
1567// Auxiliary method to create a [Vec<DiscreteValue>] from a [cxx::Vector<DiscreteValueRuleDiscreteValue>].
1568fn discrete_values_from_cxx(
1569    discrete_values_cxx: &cxx::Vector<maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue>,
1570) -> Vec<DiscreteValue> {
1571    discrete_values_cxx
1572        .iter()
1573        .map(discrete_value_from_discrete_value_cxx)
1574        .collect()
1575}
1576
1577// Auxiliary method to create a [DiscreteValue] from a [maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue].
1578pub(crate) fn discrete_value_from_discrete_value_cxx(
1579    discrete_value: &maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue,
1580) -> DiscreteValue {
1581    DiscreteValue {
1582        rule_state: RuleStateBase {
1583            severity: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_severity(discrete_value),
1584            related_rules: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_related_rules(discrete_value),
1585            related_unique_ids: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_related_unique_ids(
1586                discrete_value,
1587            ),
1588        },
1589        value: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_value(discrete_value),
1590    }
1591}
1592
1593// Auxiliary method to create a [Range] from a [maliput_sys::api::rules::ffi::RangeValueRuleRange].
1594fn range_value_from_range_value_cxx(range: &maliput_sys::api::rules::ffi::RangeValueRuleRange) -> Range {
1595    Range {
1596        rule_state: RuleStateBase {
1597            severity: maliput_sys::api::rules::ffi::RangeValueRuleRange_severity(range),
1598            related_rules: maliput_sys::api::rules::ffi::RangeValueRuleRange_related_rules(range),
1599            related_unique_ids: maliput_sys::api::rules::ffi::RangeValueRuleRange_related_unique_ids(range),
1600        },
1601        description: maliput_sys::api::rules::ffi::RangeValueRuleRange_description(range),
1602        min: maliput_sys::api::rules::ffi::RangeValueRuleRange_min(range),
1603        max: maliput_sys::api::rules::ffi::RangeValueRuleRange_max(range),
1604    }
1605}
1606
1607#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1608/// Defines the possible traffic sign types.
1609pub enum TrafficSignType {
1610    Stop,
1611    Yield,
1612    SpeedLimit,
1613    NoEntry,
1614    OneWay,
1615    PedestrianCrossing,
1616    NoLeftTurn,
1617    NoRightTurn,
1618    NoUTurn,
1619    SchoolZone,
1620    Construction,
1621    RailroadCrossing,
1622    NoOvertaking,
1623    Unknown,
1624}
1625
1626fn traffic_sign_type_from_cpp(sign_type: &maliput_sys::api::rules::ffi::TrafficSignType) -> TrafficSignType {
1627    match *sign_type {
1628        maliput_sys::api::rules::ffi::TrafficSignType::kStop => TrafficSignType::Stop,
1629        maliput_sys::api::rules::ffi::TrafficSignType::kYield => TrafficSignType::Yield,
1630        maliput_sys::api::rules::ffi::TrafficSignType::kSpeedLimit => TrafficSignType::SpeedLimit,
1631        maliput_sys::api::rules::ffi::TrafficSignType::kNoEntry => TrafficSignType::NoEntry,
1632        maliput_sys::api::rules::ffi::TrafficSignType::kOneWay => TrafficSignType::OneWay,
1633        maliput_sys::api::rules::ffi::TrafficSignType::kPedestrianCrossing => TrafficSignType::PedestrianCrossing,
1634        maliput_sys::api::rules::ffi::TrafficSignType::kNoLeftTurn => TrafficSignType::NoLeftTurn,
1635        maliput_sys::api::rules::ffi::TrafficSignType::kNoRightTurn => TrafficSignType::NoRightTurn,
1636        maliput_sys::api::rules::ffi::TrafficSignType::kNoUTurn => TrafficSignType::NoUTurn,
1637        maliput_sys::api::rules::ffi::TrafficSignType::kSchoolZone => TrafficSignType::SchoolZone,
1638        maliput_sys::api::rules::ffi::TrafficSignType::kConstruction => TrafficSignType::Construction,
1639        maliput_sys::api::rules::ffi::TrafficSignType::kRailroadCrossing => TrafficSignType::RailroadCrossing,
1640        maliput_sys::api::rules::ffi::TrafficSignType::kNoOvertaking => TrafficSignType::NoOvertaking,
1641        _ => TrafficSignType::Unknown,
1642    }
1643}
1644
1645fn traffic_sign_type_to_cpp(sign_type: &TrafficSignType) -> maliput_sys::api::rules::ffi::TrafficSignType {
1646    match sign_type {
1647        TrafficSignType::Stop => maliput_sys::api::rules::ffi::TrafficSignType::kStop,
1648        TrafficSignType::Yield => maliput_sys::api::rules::ffi::TrafficSignType::kYield,
1649        TrafficSignType::SpeedLimit => maliput_sys::api::rules::ffi::TrafficSignType::kSpeedLimit,
1650        TrafficSignType::NoEntry => maliput_sys::api::rules::ffi::TrafficSignType::kNoEntry,
1651        TrafficSignType::OneWay => maliput_sys::api::rules::ffi::TrafficSignType::kOneWay,
1652        TrafficSignType::PedestrianCrossing => maliput_sys::api::rules::ffi::TrafficSignType::kPedestrianCrossing,
1653        TrafficSignType::NoLeftTurn => maliput_sys::api::rules::ffi::TrafficSignType::kNoLeftTurn,
1654        TrafficSignType::NoRightTurn => maliput_sys::api::rules::ffi::TrafficSignType::kNoRightTurn,
1655        TrafficSignType::NoUTurn => maliput_sys::api::rules::ffi::TrafficSignType::kNoUTurn,
1656        TrafficSignType::SchoolZone => maliput_sys::api::rules::ffi::TrafficSignType::kSchoolZone,
1657        TrafficSignType::Construction => maliput_sys::api::rules::ffi::TrafficSignType::kConstruction,
1658        TrafficSignType::RailroadCrossing => maliput_sys::api::rules::ffi::TrafficSignType::kRailroadCrossing,
1659        TrafficSignType::NoOvertaking => maliput_sys::api::rules::ffi::TrafficSignType::kNoOvertaking,
1660        TrafficSignType::Unknown => maliput_sys::api::rules::ffi::TrafficSignType::kUnknown,
1661    }
1662}
1663
1664/// Interface for accessing the [TrafficSign]s in the [super::RoadNetwork].
1665pub struct TrafficSignBook<'a> {
1666    pub(super) traffic_sign_book: &'a maliput_sys::api::rules::ffi::TrafficSignBook,
1667}
1668
1669impl<'a> TrafficSignBook<'a> {
1670    /// Gets all the [TrafficSign]s in the [TrafficSignBook].
1671    ///
1672    /// # Returns
1673    /// A vector of [TrafficSign]s.
1674    pub fn traffic_signs(&self) -> Vec<TrafficSign<'_>> {
1675        let traffic_signs_cpp = maliput_sys::api::rules::ffi::TrafficSignBook_TrafficSigns(self.traffic_sign_book);
1676        traffic_signs_cpp
1677            .into_iter()
1678            .map(|ts| TrafficSign {
1679                traffic_sign: unsafe { ts.traffic_sign.as_ref().expect("TrafficSign pointer is null") },
1680            })
1681            .collect::<Vec<TrafficSign>>()
1682    }
1683
1684    /// Gets a [TrafficSign] by its id.
1685    ///
1686    /// # Arguments
1687    /// * `id` - The id of the [TrafficSign].
1688    ///
1689    /// # Returns
1690    /// The [TrafficSign] with the given id, or `None` if not found.
1691    pub fn get_traffic_sign(&self, id: &String) -> Option<TrafficSign<'_>> {
1692        let ptr = maliput_sys::api::rules::ffi::TrafficSignBook_GetTrafficSign(self.traffic_sign_book, id);
1693        if ptr.is_null() {
1694            return None;
1695        }
1696        Some(TrafficSign {
1697            traffic_sign: unsafe { ptr.as_ref().expect("Unable to get underlying traffic sign pointer") },
1698        })
1699    }
1700
1701    /// Gets all [TrafficSign]s whose `related_lanes()` includes the given lane ID.
1702    ///
1703    /// # Arguments
1704    /// * `lane_id` - The lane ID to filter by.
1705    ///
1706    /// # Returns
1707    /// A vector of [TrafficSign]s associated with the given lane.
1708    pub fn find_by_lane(&self, lane_id: &String) -> Vec<TrafficSign<'_>> {
1709        let traffic_signs_cpp =
1710            maliput_sys::api::rules::ffi::TrafficSignBook_FindByLane(self.traffic_sign_book, lane_id);
1711        traffic_signs_cpp
1712            .into_iter()
1713            .map(|ts| TrafficSign {
1714                traffic_sign: unsafe { ts.traffic_sign.as_ref().expect("TrafficSign pointer is null") },
1715            })
1716            .collect::<Vec<TrafficSign>>()
1717    }
1718
1719    /// Gets all [TrafficSign]s of the given [TrafficSignType].
1720    ///
1721    /// # Arguments
1722    /// * `sign_type` - The [TrafficSignType] to filter by.
1723    ///
1724    /// # Returns
1725    /// A vector of [TrafficSign]s of the given type.
1726    pub fn find_by_type(&self, sign_type: &TrafficSignType) -> Vec<TrafficSign<'_>> {
1727        let sign_type_ffi = traffic_sign_type_to_cpp(sign_type);
1728        let traffic_signs_cpp =
1729            maliput_sys::api::rules::ffi::TrafficSignBook_FindByType(self.traffic_sign_book, sign_type_ffi);
1730        traffic_signs_cpp
1731            .into_iter()
1732            .map(|ts| TrafficSign {
1733                traffic_sign: unsafe { ts.traffic_sign.as_ref().expect("TrafficSign pointer is null") },
1734            })
1735            .collect::<Vec<TrafficSign>>()
1736    }
1737}
1738
1739/// Models a physical traffic sign — a static, passive signaling device placed
1740/// along or above the road to convey regulatory, warning, or informational
1741/// messages to road users.
1742///
1743/// Unlike [TrafficLight], a traffic sign has no dynamic state. It simply
1744/// exists at a position with an orientation and a type.
1745pub struct TrafficSign<'a> {
1746    pub traffic_sign: &'a maliput_sys::api::rules::ffi::TrafficSign,
1747}
1748
1749impl<'a> TrafficSign<'a> {
1750    /// Gets the unique identifier of the [TrafficSign].
1751    ///
1752    /// # Returns
1753    /// The id of the [TrafficSign].
1754    pub fn id(&self) -> String {
1755        maliput_sys::api::rules::ffi::TrafficSign_id(self.traffic_sign)
1756    }
1757
1758    /// Gets the [TrafficSignType] of the [TrafficSign].
1759    ///
1760    /// # Returns
1761    /// The [TrafficSignType].
1762    pub fn sign_type(&self) -> TrafficSignType {
1763        let sign_type = maliput_sys::api::rules::ffi::TrafficSign_type(self.traffic_sign);
1764        traffic_sign_type_from_cpp(sign_type)
1765    }
1766
1767    /// Gets the position of the [TrafficSign] in the road network's Inertial frame.
1768    ///
1769    /// # Returns
1770    /// An [super::InertialPosition] representing the position of the [TrafficSign].
1771    pub fn position_road_network(&self) -> super::InertialPosition {
1772        let inertial_position = maliput_sys::api::rules::ffi::TrafficSign_position_road_network(self.traffic_sign);
1773        super::InertialPosition { ip: inertial_position }
1774    }
1775
1776    /// Gets the orientation of the [TrafficSign] in the road network's Inertial frame.
1777    ///
1778    /// # Returns
1779    /// An [super::Rotation] representing the orientation of the [TrafficSign].
1780    pub fn orientation_road_network(&self) -> super::Rotation {
1781        let rotation = maliput_sys::api::rules::ffi::TrafficSign_orientation_road_network(self.traffic_sign);
1782        super::Rotation { r: rotation }
1783    }
1784
1785    /// Gets the optional text message displayed on the [TrafficSign].
1786    ///
1787    /// # Returns
1788    /// `Some(String)` if a message is set, `None` otherwise.
1789    pub fn message(&self) -> Option<String> {
1790        let wrapper = maliput_sys::api::rules::ffi::TrafficSign_message(self.traffic_sign);
1791        if wrapper.is_null() {
1792            return None;
1793        }
1794        Some(wrapper.value.clone())
1795    }
1796
1797    /// Gets the lane IDs that this sign is physically relevant to.
1798    ///
1799    /// # Returns
1800    /// A vector of lane ID strings.
1801    pub fn related_lanes(&self) -> Vec<String> {
1802        maliput_sys::api::rules::ffi::TrafficSign_related_lanes(self.traffic_sign)
1803    }
1804
1805    /// Gets the bounding box of the [TrafficSign].
1806    ///
1807    /// # Returns
1808    /// A [crate::math::BoundingBox] describing the sign's oriented bounding volume.
1809    /// The box position is the centroid, `box_size` gives full extents, and `orientation`
1810    /// is expressed as roll-pitch-yaw angles.
1811    pub fn bounding_box(&self) -> crate::math::BoundingBox {
1812        let b = maliput_sys::api::rules::ffi::TrafficSign_bounding_box(self.traffic_sign);
1813        crate::math::BoundingBox { b }
1814    }
1815}