muse2/
region.rs

1//! Regions represent different geographical areas in which agents, processes, etc. are active.
2use crate::id::{define_id_getter, define_id_type, IDCollection};
3use anyhow::{ensure, Result};
4use indexmap::IndexMap;
5use serde::Deserialize;
6use std::collections::HashSet;
7
8define_id_type! {RegionID}
9
10/// A map of [`Region`]s, keyed by region ID
11pub type RegionMap = IndexMap<RegionID, Region>;
12
13/// Represents a region with an ID and a longer description.
14#[derive(Debug, Deserialize, PartialEq)]
15pub struct Region {
16    /// A unique identifier for a region (e.g. "GBR").
17    pub id: RegionID,
18    /// A text description of the region (e.g. "United Kingdom").
19    pub description: String,
20}
21define_id_getter! {Region, RegionID}
22
23/// Parse a string of regions separated by semicolons into a vector of RegionID.
24///
25/// The string can be either "all" (case-insensitive), a single region, or a semicolon-separated
26/// list of regions (e.g. "GBR;FRA;USA" or "GBR; FRA; USA")
27pub fn parse_region_str(s: &str, region_ids: &HashSet<RegionID>) -> Result<HashSet<RegionID>> {
28    let s = s.trim();
29    ensure!(!s.is_empty(), "No regions provided");
30
31    if s.eq_ignore_ascii_case("all") {
32        return Ok(region_ids.clone());
33    }
34
35    s.split(";")
36        .map(|y| region_ids.get_id_by_str(y.trim()))
37        .collect()
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_parse_region_str() {
46        let region_ids: HashSet<RegionID> = ["GBR".into(), "USA".into()].into_iter().collect();
47
48        // List of regions
49        let parsed = parse_region_str("GBR;USA", &region_ids).unwrap();
50        assert_eq!(parsed.len(), 2);
51        assert!(parsed.contains(&RegionID::from("GBR")));
52        assert!(parsed.contains(&RegionID::from("USA")));
53
54        // All regions
55        let parsed = parse_region_str("all", &region_ids).unwrap();
56        assert_eq!(parsed.len(), 2);
57        assert!(parsed.contains(&RegionID::from("GBR")));
58        assert!(parsed.contains(&RegionID::from("USA")));
59
60        // Single region
61        let parsed = parse_region_str("GBR", &region_ids).unwrap();
62        assert_eq!(parsed.len(), 1);
63        assert!(parsed.contains(&RegionID::from("GBR")));
64
65        // Empty string
66        let result = parse_region_str("", &region_ids);
67        assert!(result.is_err());
68
69        // Invalid region
70        let result = parse_region_str("GBR;INVALID", &region_ids);
71        assert!(result.is_err());
72    }
73}