1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
//
// Copyright (c) 2021 RepliXio Ltd. All rights reserved.
// Use is subject to license terms.
//

use std::fmt;
use std::str;

use thiserror::Error;

use crate::v0;

pub trait CloudLocation {
    const VENDOR: &'static str;
    const VENDOR_PREFIX: &'static str;
    fn as_str(&self) -> &'static str;
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Location {
    Aws(v0::AwsRegion),
    Azure(v0::AzureRegion),
    // Gcp(v1::GcpRegion),
}

impl Location {
    pub fn is_aws(&self) -> bool {
        matches!(self, Location::Aws(_))
    }

    pub fn is_azure(&self) -> bool {
        matches!(self, Location::Azure(_))
    }
}

impl fmt::Display for Location {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Aws(region) => region.fmt(f),
            Self::Azure(region) => region.fmt(f),
        }
    }
}

impl From<v0::AwsRegion> for Location {
    fn from(region: v0::AwsRegion) -> Self {
        Self::Aws(region)
    }
}

impl From<v0::AzureRegion> for Location {
    fn from(region: v0::AzureRegion) -> Self {
        Self::Azure(region)
    }
}

impl str::FromStr for Location {
    type Err = String;

    fn from_str(text: &str) -> Result<Self, Self::Err> {
        let aws = text.parse::<v0::AwsRegion>();
        let azure = text.parse::<v0::AzureRegion>();
        // let gcp = text.parse::<v1::GcpRegion>();

        match (aws, azure) {
            (Ok(aws), Err(_)) => Ok(Self::Aws(aws)),
            (Err(_), Ok(azure)) => Ok(Self::Azure(azure)),
            (Ok(aws), Ok(azure)) => Err(format!(
                "Ambiguous region, use either {:#} or {:#}",
                aws, azure
            )),
            (Err(aws), Err(azure)) => {
                let error = format!("{} or {}", aws, azure);
                Err(error)
            }
        }
    }
}

#[derive(Debug, Error)]
#[error(r#"Invalid {vendor} region "{region}""#)]
pub struct InvalidLocation {
    vendor: String,
    region: String,
}

impl InvalidLocation {
    pub fn new(vendor: &str, region: &str) -> Self {
        let vendor = vendor.to_string();
        let region = region.to_string();
        Self { vendor, region }
    }
}

// impl<T> fmt::Display for T
// where
//     T: CloudLocation,
// {
//     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
//         let text = self.as_str();
//         if f.alternate() {
//             format!("{}{}", Self::VENDOR_PREFIX, text).fmt(f)
//         } else {
//             text.fmt(f)
//         }
//     }
// }