1pub use netrc::{Authenticator, Netrc};
33use std::fs;
34use std::io;
35use std::io::ErrorKind;
36#[cfg(windows)]
37use std::iter::repeat;
38use std::path::{Path, PathBuf};
39use std::result;
40
41mod lex;
42mod netrc;
43
44pub type Result<T> = result::Result<T, Error>;
45
46#[derive(thiserror::Error, Debug)]
48pub enum Error {
49 #[error("I/O error: {0}")]
51 Io(#[from] std::io::Error),
52
53 #[error("{parser} in the file '{filename}'")]
55 Parsing {
56 parser: netrc::ParsingError,
57 filename: String,
58 },
59}
60
61impl Netrc {
62 pub fn new() -> Result<Self> {
67 Self::get_file()
68 .ok_or(Error::Io(io::Error::new(
69 ErrorKind::NotFound,
70 "no netrc file found",
71 )))
72 .and_then(|f| Netrc::from_file(f.as_path()))
73 }
74
75 pub fn from_file(file: &Path) -> Result<Self> {
77 String::from_utf8_lossy(&fs::read(file)?)
78 .parse()
79 .map_err(|e| Error::Parsing {
80 parser: e,
81 filename: file.display().to_string(),
82 })
83 }
84
85 pub fn get_file() -> Option<PathBuf> {
90 let env_var = std::env::var("NETRC")
91 .map(PathBuf::from)
92 .map(|f| shellexpand::path::tilde(&f).into_owned());
93
94 #[cfg(windows)]
95 let default = std::env::var("USERPROFILE")
96 .into_iter()
97 .flat_map(|home| repeat(home).zip([".netrc", "_netrc"]))
98 .map(|(home, file)| PathBuf::from(home).join(file));
99
100 #[cfg(not(windows))]
101 let default = std::env::var("HOME").map(|home| PathBuf::from(home).join(".netrc"));
102
103 env_var.into_iter().chain(default).find(|f| f.exists())
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 const CONTENT: &str = "\
112machine cocolog-nifty.com
113login jmarten0
114password cC2&yt7OT
115
116machine wired.com
117login mstanlack1
118password gH4={wx=>VixU
119
120machine joomla.org
121login mbutterley2
122password hY5>yKqU&$vq&0
123";
124
125 fn create_netrc_file() -> PathBuf {
126 let dest = std::env::temp_dir().join("mynetrc");
127 if !dest.exists() {
128 std::fs::write(&dest, CONTENT).unwrap();
129 }
130 dest
131 }
132
133 fn check_nrc(nrc: &Netrc) {
134 assert_eq!(nrc.hosts.len(), 3);
135 assert_eq!(
136 nrc.hosts["cocolog-nifty.com"],
137 Authenticator::new("jmarten0", "", "cC2&yt7OT")
138 );
139 assert_eq!(
140 nrc.hosts["wired.com"],
141 Authenticator::new("mstanlack1", "", "gH4={wx=>VixU")
142 );
143 assert_eq!(
144 nrc.hosts["joomla.org"],
145 Authenticator::new("mbutterley2", "", "hY5>yKqU&$vq&0")
146 );
147 }
148
149 #[test]
150 fn test_new_env() {
151 let fi = create_netrc_file();
152 std::env::set_var("NETRC", fi);
153 let nrc = Netrc::new().unwrap();
154 check_nrc(&nrc);
155 }
156
157 #[test]
158 fn test_new_default() {}
159
160 #[test]
161 fn test_from_file_failed() {
162 assert_eq!(
163 Netrc::from_file(Path::new("/netrc/file/not/exists/on/no/netrc"))
164 .unwrap_err()
165 .to_string(),
166 "I/O error: No such file or directory (os error 2)"
167 );
168 }
169
170 #[test]
171 fn test_from_file() {
172 let fi = create_netrc_file();
173 let nrc = Netrc::from_file(fi.as_path()).unwrap();
174 check_nrc(&nrc);
175 }
176}