1use std::ffi::{OsStr, OsString};
6use std::path::PathBuf;
7
8#[derive(Debug, Clone, Default, PartialEq, Eq)]
10pub struct Host {
11 vars: Vec<(OsString, OsString)>,
12}
13
14impl Host {
15 #[must_use]
17 pub fn new(vars: impl IntoIterator<Item = (OsString, OsString)>) -> Host {
18 Host {
19 vars: vars.into_iter().collect(),
20 }
21 }
22
23 fn var(&self, key: &str) -> Option<&OsStr> {
25 self.vars.iter().find(|(k, _)| k == key).map(|(_, v)| v.as_os_str())
26 }
27
28 #[must_use]
31 pub fn home(&self) -> PathBuf {
32 if let Some(home) = self.var("ISOLOOM_HOME") {
33 return PathBuf::from(home);
34 }
35 let user_home = self
36 .var("HOME")
37 .or_else(|| self.var("USERPROFILE"))
38 .map_or_else(|| PathBuf::from("."), PathBuf::from);
39 user_home.join(".isoloom")
40 }
41
42 #[must_use]
44 pub fn text_vars(&self) -> Vec<(String, String)> {
45 let mut out: Vec<(String, String)> = self
46 .vars
47 .iter()
48 .filter_map(|(k, v)| Some((k.to_str()?.to_owned(), v.to_str()?.to_owned())))
49 .collect();
50 out.sort();
51 out
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 fn host(vars: &[(&str, &str)]) -> Host {
60 Host::new(vars.iter().map(|(k, v)| (OsString::from(k), OsString::from(v))))
61 }
62
63 #[test]
64 fn isoloom_home_wins_then_the_users_home() {
65 assert_eq!(host(&[("HOME", "/home/a"), ("ISOLOOM_HOME", "/srv/iso")]).home(), PathBuf::from("/srv/iso"));
66 assert_eq!(host(&[("HOME", "/home/a")]).home(), PathBuf::from("/home/a/.isoloom"));
67 assert_eq!(host(&[("USERPROFILE", "C:\\Users\\a")]).home(), PathBuf::from("C:\\Users\\a").join(".isoloom"));
68 assert_eq!(Host::default().home(), PathBuf::from("./.isoloom"));
69 }
70
71 #[test]
72 fn text_variables_come_in_name_order() {
73 assert_eq!(
74 host(&[("B", "2"), ("A", "1")]).text_vars(),
75 [("A".to_owned(), "1".to_owned()), ("B".to_owned(), "2".to_owned())]
76 );
77 }
78}