diff --git a/maliput-sys/src/api/rules/aliases.h b/maliput-sys/src/api/rules/aliases.h index 46e2f2f..fb628c8 100644 --- a/maliput-sys/src/api/rules/aliases.h +++ b/maliput-sys/src/api/rules/aliases.h @@ -29,6 +29,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include +#include #include #include @@ -39,6 +40,7 @@ namespace rules { // Workaround for supporting nested types: https://github.com/dtolnay/cxx/issues/1198 using DiscreteValueRuleDiscreteValue = DiscreteValueRule::DiscreteValue; +using RangeValueRuleRange = RangeValueRule::Range; } // namespace rules } // namespace api diff --git a/maliput-sys/src/api/rules/mod.rs b/maliput-sys/src/api/rules/mod.rs index 0c093fe..7e6e67a 100644 --- a/maliput-sys/src/api/rules/mod.rs +++ b/maliput-sys/src/api/rules/mod.rs @@ -69,7 +69,7 @@ pub mod ffi { /// This is needed because maps can't be binded directly. struct RelatedUniqueId { pub group_name: String, - pub unique_id: Vec, + pub unique_ids: Vec, } #[repr(i32)] @@ -163,6 +163,7 @@ pub mod ffi { // RoadRulebook bindings definitions. type RoadRulebook; fn RoadRulebook_GetDiscreteValueRule(book: &RoadRulebook, rule_id: &String) -> UniquePtr; + fn RoadRulebook_GetRangeValueRule(book: &RoadRulebook, rule_id: &String) -> UniquePtr; // DiscreteValueRule::DiscreteValue bindings definitions. type DiscreteValueRuleDiscreteValue; @@ -180,5 +181,20 @@ pub mod ffi { fn states(self: &DiscreteValueRule) -> &CxxVector; fn DiscreteValueRule_id(rule: &DiscreteValueRule) -> String; fn DiscreteValueRule_type_id(rule: &DiscreteValueRule) -> String; + + // RangeValueRule::Range bindings definitions. + type RangeValueRuleRange; + fn RangeValueRuleRange_description(range: &RangeValueRuleRange) -> String; + fn RangeValueRuleRange_min(range: &RangeValueRuleRange) -> f64; + fn RangeValueRuleRange_max(range: &RangeValueRuleRange) -> f64; + fn RangeValueRuleRange_severity(range: &RangeValueRuleRange) -> i32; + fn RangeValueRuleRange_related_rules(range: &RangeValueRuleRange) -> UniquePtr>; + fn RangeValueRuleRange_related_unique_ids(range: &RangeValueRuleRange) + -> UniquePtr>; + // RangeValueRule::Range bindings definitions. + type RangeValueRule; + fn RangeValueRule_id(rule: &RangeValueRule) -> String; + fn RangeValueRule_type_id(rule: &RangeValueRule) -> String; + fn states(self: &RangeValueRule) -> &CxxVector; } } diff --git a/maliput-sys/src/api/rules/rules.h b/maliput-sys/src/api/rules/rules.h index 91e8d3e..bc1914c 100644 --- a/maliput-sys/src/api/rules/rules.h +++ b/maliput-sys/src/api/rules/rules.h @@ -44,6 +44,33 @@ namespace maliput { namespace api { namespace rules { +namespace { + +std::unique_ptr> related_rules_from_state(const Rule::State& rule_state) { + std::vector related_rules; + for (const auto& related_rule : rule_state.related_rules) { + rust::Vec rule_ids; + for (const auto& rule_id : related_rule.second) { + rule_ids.push_back({rule_id.string()}); + } + related_rules.push_back({related_rule.first, rule_ids}); + } + return std::make_unique>(std::move(related_rules)); +} + +std::unique_ptr> related_unique_ids_from_state(const Rule::State& rule_state) { + std::vector related_unique_ids; + for (const auto& related_unique_id : rule_state.related_unique_ids) { + rust::Vec unique_ids; + for (const auto& rule_id : related_unique_id.second) { + unique_ids.push_back({rule_id.string()}); + } + related_unique_ids.push_back({related_unique_id.first, unique_ids}); + } + return std::make_unique>(std::move(related_unique_ids)); +} + +} // namespace std::unique_ptr> TrafficLightBook_TrafficLights(const TrafficLightBook& traffic_light_book) { const auto traffic_lights_cpp = traffic_light_book.TrafficLights(); @@ -199,27 +226,11 @@ std::unique_ptr RoadRulebook_GetDiscreteValueRule(const RoadR } std::unique_ptr> DiscreteValueRuleDiscreteValue_related_rules(const DiscreteValueRuleDiscreteValue& discrete_value) { - std::vector related_rules; - for (const auto& related_rule : discrete_value.related_rules) { - rust::Vec rule_ids; - for (const auto& rule_id : related_rule.second) { - rule_ids.push_back({rule_id.string()}); - } - related_rules.push_back({related_rule.first, rule_ids}); - } - return std::make_unique>(std::move(related_rules)); + return related_rules_from_state(discrete_value); } std::unique_ptr> DiscreteValueRuleDiscreteValue_related_unique_ids(const DiscreteValueRuleDiscreteValue& discrete_value) { - std::vector related_unique_ids; - for (const auto& related_unique_id : discrete_value.related_unique_ids) { - rust::Vec unique_ids; - for (const auto& rule_id : related_unique_id.second) { - unique_ids.push_back({rule_id.string()}); - } - related_unique_ids.push_back({related_unique_id.first, unique_ids}); - } - return std::make_unique>(std::move(related_unique_ids)); + return related_unique_ids_from_state(discrete_value); } rust::String DiscreteValueRule_id(const DiscreteValueRule& rule) { @@ -230,6 +241,42 @@ rust::String DiscreteValueRule_type_id(const DiscreteValueRule& rule) { return rule.type_id().string(); } +rust::String RangeValueRuleRange_description(const RangeValueRuleRange& range) { + return rust::String(range.description); +} + +rust::f64 RangeValueRuleRange_min(const RangeValueRuleRange& range) { + return range.min; +} + +rust::f64 RangeValueRuleRange_max(const RangeValueRuleRange& range) { + return range.max; +} + +rust::i32 RangeValueRuleRange_severity(const RangeValueRuleRange& range) { + return range.severity; +} + +std::unique_ptr> RangeValueRuleRange_related_rules(const RangeValueRuleRange& range) { + return related_rules_from_state(range); +} + +std::unique_ptr> RangeValueRuleRange_related_unique_ids(const RangeValueRuleRange& range) { + return related_unique_ids_from_state(range); +} + +rust::String RangeValueRule_id(const RangeValueRule& rule) { + return rule.id().string(); +} + +rust::String RangeValueRule_type_id(const RangeValueRule& rule) { + return rule.type_id().string(); +} + +std::unique_ptr RoadRulebook_GetRangeValueRule(const RoadRulebook& road_rulebook, const rust::String& id) { + return std::make_unique(road_rulebook.GetRangeValueRule(Rule::Id{std::string(id)})); +} + } // namespace rules } // namespace api } // namespace maliput diff --git a/maliput/src/api/rules/mod.rs b/maliput/src/api/rules/mod.rs index f278a1a..b21b32c 100644 --- a/maliput/src/api/rules/mod.rs +++ b/maliput/src/api/rules/mod.rs @@ -492,6 +492,10 @@ pub struct RoadRulebook<'a> { impl<'a> RoadRulebook<'a> { /// Returns the DiscreteValueRule with the specified `id`. + /// ## Arguments + /// * `rule_id` - The id of the rule. + /// ## Return + /// The DiscreteValueRule with the given id. pub fn get_discrete_value_rule(&self, rule_id: &String) -> DiscreteValueRule { DiscreteValueRule { discrete_value_rule: maliput_sys::api::rules::ffi::RoadRulebook_GetDiscreteValueRule( @@ -500,6 +504,16 @@ impl<'a> RoadRulebook<'a> { ), } } + /// Returns the RangeValueRule with the specified `id`. + /// ## Arguments + /// * `rule_id` - The id of the rule. + /// ## Return + /// The RangeValueRule with the given id. + pub fn get_range_value_rule(&self, rule_id: &String) -> RangeValueRule { + RangeValueRule { + range_value_rule: maliput_sys::api::rules::ffi::RoadRulebook_GetRangeValueRule(self.road_rulebook, rule_id), + } + } } /// ## Rule @@ -533,4 +547,176 @@ impl DiscreteValueRule { pub fn type_id(&self) -> String { maliput_sys::api::rules::ffi::DiscreteValueRule_type_id(&self.discrete_value_rule) } + /// Returns a [LaneSRoute] that represents the zone that the rule applies to. + pub fn zone(&self) { + unimplemented!("Not yet implemented") + } + /// Returns the states of the rule. + pub fn states(&self) -> Vec { + let states_cpp = &self.discrete_value_rule.states(); + states_cpp + .into_iter() + .map(|dv| DiscreteValue { + rule_state: RuleStateBase { + severity: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_severity(dv), + related_rules: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_related_rules(dv), + related_unique_ids: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_related_unique_ids( + dv, + ), + }, + value: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_value(dv), + }) + .collect::>() + } +} + +/// ## Rule +/// +/// A Rule may have multiple states that affect agent behavior while it is +/// driving through the rule's zone. The possible states of a Rule must be +/// semantically coherent. The current state of a Rule is given by a +/// [RuleStateProvider]. States can be: +/// +/// - range based ([RangeValueRule]). +/// - discrete ([DiscreteValueRule]). +/// +/// ## RangeValueRule +/// +/// [Range]s describe a numeric range based rule. +/// Ranges are closed and continuous, defined by a minimum and maximum quantity. +/// When only one extreme is formally defined, the other should take a +/// semantically correct value. For example, if a speed limit only specifies a +/// maximum value, the minimum value is typically zero. +pub struct RangeValueRule { + range_value_rule: cxx::UniquePtr, +} + +impl RangeValueRule { + /// Returns the Id of the rule as a string. + pub fn id(&self) -> String { + maliput_sys::api::rules::ffi::RangeValueRule_id(&self.range_value_rule) + } + /// Returns the type of the rule as a string. + /// Example: "right-of-way-rule-type-id", "direction-usage-rule-type-id" + pub fn type_id(&self) -> String { + maliput_sys::api::rules::ffi::RangeValueRule_type_id(&self.range_value_rule) + } + /// Returns a [LaneSRoute] that represents the zone that the rule applies to. + pub fn zone(&self) { + unimplemented!("Not yet implemented") + } + /// Returns the states of the rule. + pub fn states(&self) -> Vec { + let states_cpp = &self.range_value_rule.states(); + states_cpp + .into_iter() + .map(|r| Range { + rule_state: RuleStateBase { + severity: maliput_sys::api::rules::ffi::RangeValueRuleRange_severity(r), + related_rules: maliput_sys::api::rules::ffi::RangeValueRuleRange_related_rules(r), + related_unique_ids: maliput_sys::api::rules::ffi::RangeValueRuleRange_related_unique_ids(r), + }, + description: maliput_sys::api::rules::ffi::RangeValueRuleRange_description(r), + min: maliput_sys::api::rules::ffi::RangeValueRuleRange_min(r), + max: maliput_sys::api::rules::ffi::RangeValueRuleRange_max(r), + }) + .collect::>() + } +} + +/// Defines a base state for a rule. +/// +/// ## RuleStateBase +/// +/// - `severity` - The severity of the rule state. +/// - `related_rules` - A map of related rules. The key is the group name and the value is a vector of rule ids. +/// - `related_unique_ids` - A map of related unique ids. The key is the group name and the value is a vector of unique ids. +/// +/// See [DiscreteValueRule] and [RangeValueRule] for more information. +pub struct RuleStateBase { + severity: i32, + related_rules: cxx::UniquePtr>, + related_unique_ids: cxx::UniquePtr>, +} + +/// Defines the interface for a rule state. +/// ## To implement by the trait user. +/// - `get_rule_state` - Returns the base state of the rule. +/// To be implemented by the concrete rule state. +pub trait RuleState { + /// Returns the base state of the rule. + /// To be implemented by the concrete rule state. + fn get_rule_state(&self) -> &RuleStateBase; + + /// Returns the severity of the rule state. + fn severity(&self) -> i32 { + self.get_rule_state().severity + } + + /// Returns a map of related unique ids. The key is the group name and the value is a vector of unique ids. + fn related_rules(&self) -> std::collections::HashMap<&String, &Vec> { + self.get_rule_state() + .related_rules + .iter() + .map(|rr| (&rr.group_name, &rr.rule_ids)) + .collect::>>() + } + /// Returns a map of related unique ids. The key is the group name and the value is a vector of unique ids. + fn related_unique_ids(&self) -> std::collections::HashMap<&String, &Vec> { + self.get_rule_state() + .related_unique_ids + .iter() + .map(|rui| (&rui.group_name, &rui.unique_ids)) + .collect::>>() + } +} + +/// Defines a discrete value for a [DiscreteValueRule]. +/// It extends the [RuleStateBase] with the value of the discrete value. +pub struct DiscreteValue { + rule_state: RuleStateBase, + value: String, +} + +impl RuleState for DiscreteValue { + fn get_rule_state(&self) -> &RuleStateBase { + &self.rule_state + } +} + +impl DiscreteValue { + /// Returns the value of the discrete value. + pub fn value(&self) -> &String { + &self.value + } +} + +/// Defines a range value for a [RangeValueRule]. +/// It extends the [RuleStateBase] with the description, and min and max values of the range. +pub struct Range { + rule_state: RuleStateBase, + description: String, + min: f64, + max: f64, +} + +impl RuleState for Range { + fn get_rule_state(&self) -> &RuleStateBase { + &self.rule_state + } +} + +impl Range { + /// Returns the description of the range value. + pub fn description(&self) -> &String { + &self.description + } + /// Returns the minimum value of the range. + pub fn min(&self) -> f64 { + self.min + } + /// Returns the maximum value of the range. + pub fn max(&self) -> f64 { + self.max + } } diff --git a/maliput/tests/discrete_value_rule_test.rs b/maliput/tests/discrete_value_rule_test.rs new file mode 100644 index 0000000..661e820 --- /dev/null +++ b/maliput/tests/discrete_value_rule_test.rs @@ -0,0 +1,123 @@ +// BSD 3-Clause License +// +// Copyright (c) 2024, Woven by Toyota. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod common; + +// LoopRoadPedestrianCrosswalk map is being used to test the discrete value rule API and its components. +// YAML information about the RoadRulebook can be found at: +// https://github.com/maliput/maliput_malidrive/blob/352601969b1363cc13fe2008c198a3d95843bf5b/resources/LoopRoadPedestrianCrosswalk.yaml#L64 + +#[test] +fn discrete_value_rule_test_api() { + use maliput::api::rules::RuleState; + + let road_network = common::create_loop_road_pedestrian_crosswalk_road_network_with_books(); + + let book = road_network.rulebook(); + let expected_rule_id = String::from("Right-Of-Way Rule Type/WestToEastSouth"); + let expected_type_id = String::from("Right-Of-Way Rule Type"); + let rule = book.get_discrete_value_rule(&expected_rule_id); + assert_eq!(rule.id(), expected_rule_id); + assert_eq!(rule.type_id(), expected_type_id); + + let states = rule.states(); + assert_eq!(states.len(), 2); // Go and Stop + + // Find DiscreteValue with value "Go" + let expected_value = "Go"; + let go_discrete_value = states.iter().find(|state| state.value() == expected_value).unwrap(); + assert_eq!(go_discrete_value.value(), expected_value); + + let severity = go_discrete_value.severity(); + assert_eq!(severity, 0); + + let related_rules = go_discrete_value.related_rules(); + assert_eq!(related_rules.len(), 1); + let related_rule_group = String::from("Vehicle-Stop-In-Zone-Behavior Rule Type"); + let related_rules_for_vehicle_stop_behavior_group = related_rules + .get(&related_rule_group) + .expect("Related rule group not found"); + assert_eq!(related_rules_for_vehicle_stop_behavior_group.len(), 1); + assert_eq!( + related_rules_for_vehicle_stop_behavior_group + .first() + .expect("Related rule not found"), + "Vehicle-Stop-In-Zone-Behavior Rule Type/WestToEastSouth" + ); + + let related_unique_ids = go_discrete_value.related_unique_ids(); + assert_eq!(related_unique_ids.len(), 1); + let related_unique_id_group = String::from("Bulb Group"); + let related_unique_ids_for_bulb_group = related_unique_ids + .get(&related_unique_id_group) + .expect("Related unique id group not found"); + assert_eq!(related_unique_ids_for_bulb_group.len(), 1); + assert_eq!( + related_unique_ids_for_bulb_group + .first() + .expect("Related unique id not found"), + "WestFacingSouth-WestFacingBulbsSouth" + ); + + // Find DiscreteValue with value "Stop" + let expected_value = "Stop"; + let stop_discrete_value = states.iter().find(|state| state.value() == expected_value).unwrap(); + assert_eq!(stop_discrete_value.value(), expected_value); + + let severity = go_discrete_value.severity(); + assert_eq!(severity, 0); + + let related_rules = stop_discrete_value.related_rules(); + assert_eq!(related_rules.len(), 1); + let related_rule_group = String::from("Vehicle-Stop-In-Zone-Behavior Rule Type"); + let related_rules_for_vehicle_stop_behavior_group = related_rules + .get(&related_rule_group) + .expect("Related rule group not found"); + assert_eq!(related_rules_for_vehicle_stop_behavior_group.len(), 1); + assert_eq!( + related_rules_for_vehicle_stop_behavior_group + .first() + .expect("Related rule not found"), + "Vehicle-Stop-In-Zone-Behavior Rule Type/WestToEastSouth" + ); + + let related_unique_ids = stop_discrete_value.related_unique_ids(); + assert_eq!(related_unique_ids.len(), 1); + let related_unique_id_group = String::from("Bulb Group"); + let related_unique_ids_for_bulb_group = related_unique_ids + .get(&related_unique_id_group) + .expect("Related unique id group not found"); + assert_eq!(related_unique_ids_for_bulb_group.len(), 1); + assert_eq!( + related_unique_ids_for_bulb_group + .first() + .expect("Related unique id not found"), + "WestFacingSouth-WestFacingBulbsSouth" + ); +} diff --git a/maliput/tests/range_value_rule_test.rs b/maliput/tests/range_value_rule_test.rs new file mode 100644 index 0000000..110345c --- /dev/null +++ b/maliput/tests/range_value_rule_test.rs @@ -0,0 +1,63 @@ +// BSD 3-Clause License +// +// Copyright (c) 2024, Woven by Toyota. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod common; + +// LoopRoadPedestrianCrosswalk map is being used to test the discrete value rule API and its components. +// YAML information about the RoadRulebook can be found at: +// https://github.com/maliput/maliput_malidrive/blob/352601969b1363cc13fe2008c198a3d95843bf5b/resources/LoopRoadPedestrianCrosswalk.yaml#L64 + +#[test] +fn range_value_rule_test_api() { + use maliput::api::rules::RuleState; + + let road_network = common::create_loop_road_pedestrian_crosswalk_road_network_with_books(); + + let book = road_network.rulebook(); + let expected_rule_id = String::from("Speed-Limit Rule Type/1_0_1_1"); + let expected_type_id = String::from("Speed-Limit Rule Type"); + let rule = book.get_range_value_rule(&expected_rule_id); + assert_eq!(rule.id(), expected_rule_id); + assert_eq!(rule.type_id(), expected_type_id); + + let states = rule.states(); + assert_eq!(states.len(), 1); // Only one speed limit state + let speed_limit_state = states.first().expect("State not found"); + assert_eq!(speed_limit_state.description(), "m/s"); + assert_eq!(speed_limit_state.min(), 0.); + assert_eq!(speed_limit_state.max(), 11.11111111111111); + + let severity = speed_limit_state.severity(); + assert_eq!(severity, 0); + + let related_rules = speed_limit_state.related_rules(); + assert_eq!(related_rules.len(), 0); + let related_unique_ids = speed_limit_state.related_unique_ids(); + assert_eq!(related_unique_ids.len(), 0); +} diff --git a/maliput/tests/road_rulebook_test.rs b/maliput/tests/road_rulebook_test.rs index 618dac3..f6e5f90 100644 --- a/maliput/tests/road_rulebook_test.rs +++ b/maliput/tests/road_rulebook_test.rs @@ -38,9 +38,17 @@ fn road_rulebook_test_api() { let road_network = common::create_loop_road_pedestrian_crosswalk_road_network_with_books(); let book = road_network.rulebook(); + // Test get_discrete_value_rule method. let expected_rule_id = String::from("Vehicle-Stop-In-Zone-Behavior Rule Type/WestToEastSouth"); let expected_type_id = String::from("Vehicle-Stop-In-Zone-Behavior Rule Type"); let rule = book.get_discrete_value_rule(&expected_rule_id); assert_eq!(rule.id(), expected_rule_id); assert_eq!(rule.type_id(), expected_type_id); + + // Test get_range_value_rule method. + let expected_rule_id = String::from("Speed-Limit Rule Type/1_0_1_1"); + let expected_type_id = String::from("Speed-Limit Rule Type"); + let rule = book.get_range_value_rule(&expected_rule_id); + assert_eq!(rule.id(), expected_rule_id); + assert_eq!(rule.type_id(), expected_type_id); }