gette 0.0.2

A Rust library for getting "things"
Documentation
pub struct S3;

impl crate::Detector for S3 {
    fn detect(&self, path: &str) -> Result<Option<String>, crate::Error> {
        if path.contains("amazonaws.com/") {
            return self.detect_http(path);
        }

        Ok(None)
    }
}

impl S3 {
    fn detect_http(&self, path: &str) -> Result<Option<String>, crate::Error> {
        let parts: Vec<&str> = path.split('/').collect();
        if parts.len() < 2 {
            return Err(crate::Error::InvalidUrl(
                path.to_string(),
                "not a valid s3 url".to_string(),
            ));
        }

        let host: Vec<&str> = parts[0].split('.').collect();
        match host.len() {
            3 => self.region_path_style(host[0], parts[1..].to_vec()),
            4 => self.vhost_path_style(host[1], host[0], parts[1..].to_vec()),
            5 if host[1] == "s3" => {
                self.new_vhost_path_style(host[2], host[0], parts[1..].to_vec())
            }
            _ => Err(crate::Error::InvalidUrl(
                path.to_string(),
                "not a valid s3 url".to_string(),
            )),
        }
    }

    fn region_path_style(
        &self,
        region: &str,
        parts: Vec<&str>,
    ) -> Result<Option<String>, crate::Error> {
        let url_string = format!("https://{}.amazonaws.com/{}", region, parts.join("/"));
        let url_parsed = url::Url::parse(url_string.as_str())?;
        Ok(Some(format!("s3+{}", url_parsed)))
    }

    fn vhost_path_style(
        &self,
        region: &str,
        bucket: &str,
        parts: Vec<&str>,
    ) -> Result<Option<String>, crate::Error> {
        let url_string = format!(
            "https://{}.amazonaws.com/{}/{}",
            region,
            bucket,
            parts.join("/")
        );
        let url_parsed = url::Url::parse(url_string.as_str())?;
        Ok(Some(format!("s3+{}", url_parsed)))
    }

    fn new_vhost_path_style(
        &self,
        region: &str,
        bucket: &str,
        parts: Vec<&str>,
    ) -> Result<Option<String>, crate::Error> {
        let url_string = format!(
            "https://s3.{}.amazonaws.com/{}/{}",
            region,
            bucket,
            parts.join("/")
        );
        let url_parsed = url::Url::parse(url_string.as_str())?;
        Ok(Some(format!("s3+{}", url_parsed)))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::Detector;

    #[test]
    fn it_should_decode_all_valid_variants_of_s3_urls() {
        let d = S3;
        let tests = vec![
            (
                "test.us-east-2.amazonaws.com/test.txt",
                "s3+https://us-east-2.amazonaws.com/test/test.txt",
            ),
            (
                "test.s3.us-east-2.amazonaws.com/test.txt",
                "s3+https://s3.us-east-2.amazonaws.com/test/test.txt",
            ),
            (
                "us-east-2.amazonaws.com/test/test.txt",
                "s3+https://us-east-2.amazonaws.com/test/test.txt",
            ),
        ];

        for test in tests {
            let res = d.detect(test.0).unwrap();
            assert!(res.is_some());
            assert_eq!(res, Some(test.1.to_string()));
        }
    }

    #[test]
    fn it_should_fail_on_invalid_s3_urls() {
        let d = S3;
        let tests = vec![
            "wrong.test.us-east-2.amazonaws.com/test.txt",
            "amazonaws.com/test.txt",
        ];

        for test in tests {
            let res = d.detect(test);
            println!("test: ({}) => {:#?}", test, res);
            assert!(res.is_err());
        }
    }
}