reqwest-netrc 0.1.3

netrc support for Rust with support for reqwest
Documentation
use netrc::{Netrc, Result};
use reqwest_middleware::{RequestBuilder, RequestInitialiser};
use std::path::Path;

pub struct NetrcMiddleware {
    nrc: Netrc,
}

impl NetrcMiddleware {
    pub fn new() -> Result<Self> {
        Netrc::new().map(|nrc| NetrcMiddleware { nrc })
    }

    pub fn from_file(file: &Path) -> Result<Self> {
        Netrc::from_file(file).map(|nrc| NetrcMiddleware { nrc })
    }
}

impl RequestInitialiser for NetrcMiddleware {
    fn init(&self, req: RequestBuilder) -> RequestBuilder {
        match req.try_clone() {
            Some(nr) => req
                .try_clone()
                .unwrap()
                .build()
                .ok()
                .and_then(|r| {
                    r.url()
                        .host_str()
                        .and_then(|host| {
                            self.nrc
                                .hosts
                                .get(host)
                                .or_else(|| self.nrc.hosts.get("default"))
                        })
                        .map(|auth| {
                            nr.basic_auth(
                                &auth.login,
                                if auth.password.is_empty() {
                                    None
                                } else {
                                    Some(&auth.password)
                                },
                            )
                        })
                })
                .unwrap_or(req),
            None => req,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use reqwest::Client;
    use reqwest_middleware::ClientBuilder;
    use std::path::PathBuf;
    use wiremock::matchers::{basic_auth, method, path};
    use wiremock::{Mock, MockServer, ResponseTemplate};

    const NETRC: &str = r#"default login myuser password mypassword"#;

    fn create_netrc_file() -> PathBuf {
        let dest = std::env::temp_dir().join("netrc");
        if !dest.exists() {
            std::fs::write(&dest, NETRC).unwrap();
        }
        dest
    }

    #[tokio::test]
    async fn test_init() {
        let server = MockServer::start().await;

        Mock::given(method("GET"))
            .and(path("/hello"))
            .and(basic_auth("myuser", "mypassword"))
            .respond_with(ResponseTemplate::new(200))
            .mount(&server)
            .await;

        let status = ClientBuilder::new(Client::builder().build().unwrap())
            .build()
            .get(format!("{}/hello", &server.uri()))
            .send()
            .await
            .unwrap()
            .status();

        assert_eq!(status, 404);

        let file = create_netrc_file();

        let status = ClientBuilder::new(Client::builder().build().unwrap())
            .with_init(NetrcMiddleware::from_file(file.as_path()).unwrap())
            .build()
            .get(format!("{}/hello", &server.uri()))
            .send()
            .await
            .unwrap()
            .status();

        assert_eq!(status, 200);
    }
}