diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2024-11-22 23:18:18 +0100 |
|---|---|---|
| committer | Sebastian Holmin <sebastian.holmin@mullvad.net> | 2025-01-22 13:32:05 +0100 |
| commit | e913da1df1185c56673c99a1eb6e42d08d8361ed (patch) | |
| tree | f5a51146fd573fa45541fecc2cd0b4e98ff401f2 | |
| parent | 598727746cd679b6d1aa327f6a5ac0506693bda8 (diff) | |
| download | mullvadvpn-e913da1df1185c56673c99a1eb6e42d08d8361ed.tar.xz mullvadvpn-e913da1df1185c56673c99a1eb6e42d08d8361ed.zip | |
Implement FromStr on GeographicLocationConstraint
| -rw-r--r-- | mullvad-cli/src/cmds/relay_constraints.rs | 1 | ||||
| -rw-r--r-- | mullvad-types/src/relay_constraints.rs | 53 |
2 files changed, 53 insertions, 1 deletions
diff --git a/mullvad-cli/src/cmds/relay_constraints.rs b/mullvad-cli/src/cmds/relay_constraints.rs index 97555997fc..72b3bdb88e 100644 --- a/mullvad-cli/src/cmds/relay_constraints.rs +++ b/mullvad-cli/src/cmds/relay_constraints.rs @@ -27,7 +27,6 @@ impl From<LocationArgs> for Constraint<GeographicLocationConstraint> { (country, Some(city), Some(hostname)) => { GeographicLocationConstraint::Hostname(country, city, hostname) } - _ => unreachable!("invalid location arguments"), }) } diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs index 4be7e25a4b..e6eaa7ca55 100644 --- a/mullvad-types/src/relay_constraints.rs +++ b/mullvad-types/src/relay_constraints.rs @@ -174,6 +174,12 @@ pub enum GeographicLocationConstraint { Hostname(CountryCode, CityCode, Hostname), } +#[derive(thiserror::Error, Debug)] +#[error("Failed to parse {input} into a geographic location constraint")] +pub struct ParseGeoLocationError { + input: String, +} + impl GeographicLocationConstraint { /// Create a new [`GeographicLocationConstraint`] given a country. pub fn country(country: impl Into<String>) -> Self { @@ -227,6 +233,27 @@ impl Match<Relay> for GeographicLocationConstraint { } } +impl FromStr for GeographicLocationConstraint { + type Err = ParseGeoLocationError; + + // TODO: Implement for country and city as well? + fn from_str(input: &str) -> Result<Self, Self::Err> { + // A host name, such as "se-got-wg-101" maps to + // Country: se + // City: got + // hostname: se-got-wg-101 + let x = input.split("-").collect::<Vec<_>>(); + match x[..] { + [country] => Ok(GeographicLocationConstraint::country(country)), + [country, city] => Ok(GeographicLocationConstraint::city(country, city)), + [country, city, ..] => Ok(GeographicLocationConstraint::hostname(country, city, input)), + _ => Err(ParseGeoLocationError { + input: input.to_string(), + }), + } + } +} + /// Limits the set of servers to choose based on ownership. #[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize, Serialize)] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] @@ -677,3 +704,29 @@ impl RelayOverride { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_hostname() { + // Parse a country + assert_eq!( + "se".parse::<GeographicLocationConstraint>().unwrap(), + GeographicLocationConstraint::country("se") + ); + // Parse a city + assert_eq!( + "se-got".parse::<GeographicLocationConstraint>().unwrap(), + GeographicLocationConstraint::city("se", "got") + ); + // Parse a hostname + assert_eq!( + "se-got-wg-101" + .parse::<GeographicLocationConstraint>() + .unwrap(), + GeographicLocationConstraint::hostname("se", "got", "se-got-wg-101") + ); + } +} |
