envish/
lib.rs

1#[doc = include_str!("../README.md")]
2use regex::{Error, Regex};
3use std::{collections::HashMap, ffi::OsString};
4
5/// The result of an `envish::group_by` operation.
6///
7/// The outer HashMap describes named groups of environment variables.
8///
9/// The inner HashMaps describe each environment variable's key and value.
10type GroupByResult = HashMap<String, HashMap<String, OsString>>;
11
12pub struct Search {
13    expr: Regex,
14    vars: std::env::VarsOs,
15}
16
17impl Iterator for Search {
18    type Item = (OsString, OsString);
19
20    fn next(&mut self) -> Option<Self::Item> {
21        loop {
22            match self.vars.next() {
23                Some(pair) => {
24                    if self.expr.is_match(pair.0.to_str().unwrap()) {
25                        return Some(pair);
26                    }
27                }
28                None => {
29                    return None;
30                }
31            }
32        }
33    }
34}
35
36/// Gets a grouped collection of related environment variables.
37///
38/// The group expression must contain two named groups:
39///
40/// - `<group>` describes the substring that groups variables together.
41/// - `<key>` describes the variable's key in the result.
42///
43/// ```rust
44/// std::env::set_var("FOO_ALLY_AGE", "39");
45/// std::env::set_var("FOO_ALLY_JOB", "assassin");
46/// std::env::set_var("FOO_CHARLIE_AGE", "42");
47/// std::env::set_var("FOO_CHARLIE_JOB", "jester");
48///
49/// let group_expr = r"FOO_(?<group>.+)_(?<key>.+)";
50/// let mut groups = envish::group_by(group_expr).unwrap();
51///
52/// let ally = &groups["ALLY"];
53/// assert_eq!(ally["AGE"], "39");
54/// assert_eq!(ally["JOB"], "assassin");
55///
56/// let charlie = &groups["CHARLIE"];
57/// assert_eq!(charlie["AGE"], "42");
58/// assert_eq!(charlie["JOB"], "jester");
59/// ```
60pub fn group_by(expr: &str) -> Result<GroupByResult, Error> {
61    match Regex::new(expr) {
62        Ok(re) => {
63            let mut groups = HashMap::new();
64
65            for p in std::env::vars_os() {
66                match re.captures(p.0.to_str().unwrap()) {
67                    Some(captures) => {
68                        let group_id = &captures["group"];
69
70                        if !groups.contains_key(group_id) {
71                            groups.insert(group_id.to_owned(), HashMap::new());
72                        }
73
74                        groups
75                            .get_mut(group_id)
76                            .unwrap()
77                            .insert(captures["key"].to_string(), p.1);
78                    }
79                    None => continue,
80                }
81            }
82
83            Ok(groups)
84        }
85        Err(e) => Err(e),
86    }
87}
88
89/// Gets an iterator over environment variables with names that match a regular
90/// expression.
91///
92/// ```rust
93/// std::env::set_var("CAT_CLOWNS_WOO", "woo");
94/// std::env::set_var("IMP_JESTER_FOO", "foo");
95/// std::env::set_var("DOG_JUGGLE_WAR", "war");
96/// std::env::set_var("IMP_JESTER_BAR", "bar");
97///
98/// let expr = "(.*)_JESTER_(.*)";
99/// let mut matches = envish::search(expr).unwrap();
100///
101/// let m = matches.next().unwrap();
102/// assert_eq!(m.0, "IMP_JESTER_FOO");
103/// assert_eq!(m.1, "foo");
104///
105/// let m = matches.next().unwrap();
106/// assert_eq!(m.0, "IMP_JESTER_BAR");
107/// assert_eq!(m.1, "bar");
108///
109/// let m = matches.next();
110/// assert!(m.is_none());
111/// ```
112pub fn search(expr: &str) -> Result<Search, Error> {
113    Ok(Search {
114        expr: Regex::new(expr)?,
115        vars: std::env::vars_os(),
116    })
117}
118
119/// Gets an iterator over environment variables with names that have a given
120/// prefix.
121///
122/// ```rust
123/// std::env::set_var("CAT_CLOWNS_WOO", "woo");
124/// std::env::set_var("IMP_JESTER_FOO", "foo");
125/// std::env::set_var("DOG_JUGGLE_WAR", "war");
126/// std::env::set_var("IMP_JESTER_BAR", "bar");
127///
128/// let prefix = "IMP_JESTER_";
129/// let mut matches = envish::with_prefix(prefix).unwrap();
130///
131/// let m = matches.next().unwrap();
132/// assert_eq!(m.0, "IMP_JESTER_FOO");
133/// assert_eq!(m.1, "foo");
134///
135/// let m = matches.next().unwrap();
136/// assert_eq!(m.0, "IMP_JESTER_BAR");
137/// assert_eq!(m.1, "bar");
138///
139/// let m = matches.next();
140/// assert!(m.is_none());
141/// ```
142pub fn with_prefix(prefix: &str) -> Result<Search, Error> {
143    search(&format!("{}(.*)", prefix))
144}