netrc_util/
raw_netrc_parser.rs1use std::io::Read;
2
3use anyhow::Result;
4use url::Host;
5
6use crate::parser_combinator::{parse_config, NetrcConfig};
7
8#[derive(Debug, Default, Clone, PartialEq, Eq)]
10pub struct RawEntry {
11 pub(crate) login: Option<String>,
12 pub(crate) password: Option<String>,
13 pub(crate) account: Option<String>,
14}
15
16impl RawEntry {
18 pub fn login(&self) -> Option<&String> {
20 self.login.as_ref()
21 }
22
23 pub fn password(&self) -> Option<&String> {
25 self.password.as_ref()
26 }
27
28 pub fn account(&self) -> Option<&String> {
30 self.account.as_ref()
31 }
32}
33
34#[derive(Debug)]
37pub struct RawNetrcParser<R: Read> {
38 buffer: R,
39 config: Option<NetrcConfig>,
40}
41
42impl<R: Read> RawNetrcParser<R> {
43 pub fn new(buffer: R) -> Self {
45 Self {
46 buffer,
47 config: None,
48 }
49 }
50
51 pub fn entry_for_host(&mut self, host: &Host) -> Result<Option<RawEntry>> {
60 let mut buf_content = String::new();
61 self.buffer.read_to_string(&mut buf_content)?;
62
63 let config = match &self.config {
64 Some(config) => config.clone(),
65 None => {
66 let config = parse_config(&buf_content);
67 self.config = Some(config.clone());
68
69 config
70 }
71 };
72
73 Ok(config
74 .entries
75 .get(host)
76 .or(config.default.as_ref())
77 .cloned())
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use std::io::BufReader;
84
85 use super::*;
86
87 const COM: &str = "example.com";
88 const ORG: &str = "example.org";
89 const UNI: &str = "xn--9ca.com";
90 const IP1: &str = "1.1.1.1";
91 const IP2: &str = "2.2.2.2.";
92
93 #[test]
94 fn parse_simple_config() {
95 const SIMPLE: &str = "
96 machine example.com
97 login user
98 password pass
99 account acc
100 ";
101 found(SIMPLE, COM, "user", "pass", "acc");
102 notfound(SIMPLE, ORG);
103 notfound(SIMPLE, UNI);
104 notfound(SIMPLE, IP1);
105 }
106
107 #[test]
108 fn parse_empty_config() {
109 const SIMPLE: &str = "
110 machine example.com
111 ";
112 found(SIMPLE, COM, None, None, None);
113 notfound(SIMPLE, ORG);
114 notfound(SIMPLE, UNI);
115 notfound(SIMPLE, IP1);
116 }
117
118 #[track_caller]
119 fn found(
120 netrc: &str,
121 host: &str,
122 login: impl Into<Option<&'static str>>,
123 password: impl Into<Option<&'static str>>,
124 account: impl Into<Option<&'static str>>,
125 ) {
126 let entry = RawNetrcParser::new(BufReader::new(netrc.as_bytes()))
127 .entry_for_host(&Host::parse(host).unwrap());
128 let entry = entry.unwrap().expect("Didn't find entry");
129
130 assert_eq!(entry.login.as_deref(), login.into());
131 assert_eq!(entry.password.as_deref(), password.into());
132 assert_eq!(entry.account.as_deref(), account.into());
133 }
134
135 #[track_caller]
136 fn notfound(netrc: &str, host: &str) {
137 let entry =
138 RawNetrcParser::new(netrc.as_bytes()).entry_for_host(&Host::parse(host).unwrap());
139
140 assert!(entry.unwrap().is_none(), "Found entry");
141 }
142}