use std::io::Read;
use anyhow::Result;
use url::Host;
use crate::parser_combinator::{parse_config, NetrcConfig};
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct RawEntry {
pub(crate) login: Option<String>,
pub(crate) password: Option<String>,
pub(crate) account: Option<String>,
}
impl RawEntry {
pub fn login(&self) -> Option<&String> {
self.login.as_ref()
}
pub fn password(&self) -> Option<&String> {
self.password.as_ref()
}
pub fn account(&self) -> Option<&String> {
self.account.as_ref()
}
}
#[derive(Debug)]
pub struct RawNetrcParser<R: Read> {
buffer: R,
config: Option<NetrcConfig>,
}
impl<R: Read> RawNetrcParser<R> {
pub fn new(buffer: R) -> Self {
Self {
buffer,
config: None,
}
}
pub fn entry_for_host(&mut self, host: &Host) -> Result<Option<RawEntry>> {
let mut buf_content = String::new();
self.buffer.read_to_string(&mut buf_content)?;
let config = match &self.config {
Some(config) => config.clone(),
None => {
let config = parse_config(&buf_content);
self.config = Some(config.clone());
config
}
};
Ok(config
.entries
.get(host)
.or(config.default.as_ref())
.cloned())
}
}
#[cfg(test)]
mod tests {
use std::io::BufReader;
use super::*;
const COM: &str = "example.com";
const ORG: &str = "example.org";
const UNI: &str = "xn--9ca.com";
const IP1: &str = "1.1.1.1";
const IP2: &str = "2.2.2.2.";
#[test]
fn parse_simple_config() {
const SIMPLE: &str = "
machine example.com
login user
password pass
account acc
";
found(SIMPLE, COM, "user", "pass", "acc");
notfound(SIMPLE, ORG);
notfound(SIMPLE, UNI);
notfound(SIMPLE, IP1);
}
#[test]
fn parse_empty_config() {
const SIMPLE: &str = "
machine example.com
";
found(SIMPLE, COM, None, None, None);
notfound(SIMPLE, ORG);
notfound(SIMPLE, UNI);
notfound(SIMPLE, IP1);
}
#[track_caller]
fn found(
netrc: &str,
host: &str,
login: impl Into<Option<&'static str>>,
password: impl Into<Option<&'static str>>,
account: impl Into<Option<&'static str>>,
) {
let entry = RawNetrcParser::new(BufReader::new(netrc.as_bytes()))
.entry_for_host(&Host::parse(host).unwrap());
let entry = entry.unwrap().expect("Didn't find entry");
assert_eq!(entry.login.as_deref(), login.into());
assert_eq!(entry.password.as_deref(), password.into());
assert_eq!(entry.account.as_deref(), account.into());
}
#[track_caller]
fn notfound(netrc: &str, host: &str) {
let entry =
RawNetrcParser::new(netrc.as_bytes()).entry_for_host(&Host::parse(host).unwrap());
assert!(entry.unwrap().is_none(), "Found entry");
}
}