varsun/
mswin.rs

1// -*- coding: utf-8 -*-
2// vi: set sts=4 ts=4 sw=4 et ft=rust:
3
4//! Provides MS-Windows style substition.
5//!
6//! MS-Winodws style substition is `%var%` format strings.
7//! You can see on COMMAND PROMPT (not PowerShell).
8
9/// Parse src and substitute found variables with result of `mapfn`.
10///
11/// # Examples
12///
13/// ```
14/// use varsun::mswin::substitute;
15///
16/// let src = "foo is %foo%.";
17/// let res = substitute(src, |name: &str| -> Option<String> {
18///     match name {
19///         "foo" => Some("FOO!!".to_string()),
20///         _     => None,      // If None returns, variable replaces with "" (empty string).
21///     }
22/// });
23/// ```
24pub fn substitute<F>(src: &str, mapfn: F) -> String where F: Fn(&str) -> Option<String> {
25    let mut dst = String::new();
26    let mut chs = src.chars();
27
28    // Temporary variables.
29    let mut varname = String::new();
30    let mut started = false;
31
32    // Check each characters.
33    while let Some(ch) = chs.next() {
34        if ch == '%' {
35            if started {
36                // Reach end of varname section.
37
38                // Call mapping-function.
39                if let Some(val) = mapfn(varname.as_str()) {
40                    // Push raw value.
41                    dst.push_str(val.as_str());
42
43                    // Leave varname section.
44                    started = false;
45                } else {
46                    // Push Back Variable.
47                    dst.push('%');
48                    dst.push_str(varname.as_str());
49                }
50
51                // Reset varname.
52                varname.clear();
53            } else {
54                // Enter varname section.
55                started = true;
56
57                // Continue to Next.
58                continue;
59            }
60        } else {
61            if started {
62                // Part of varname.
63                varname.push(ch);
64            } else {
65                // Part of text.
66                dst.push(ch);
67            }
68        }
69    }
70
71    // Push Back vaname if cursor placed in varname section yet.
72    if started {
73        dst.push('%');
74        dst.push_str(varname.as_str());
75
76        // Reset
77        varname.clear();
78    }
79
80    return dst;
81}
82
83/// Substitute environment variable by Windows format.
84pub fn substenvar(src: &str) -> String {
85    return self::substitute(src, super::envar);
86}
87
88#[cfg(test)]
89mod tests {
90    fn mapfn(name: &str) -> Option<String> {
91        match name {
92            "foo" => Some("foo!!".to_string()),
93            "bar" => Some("!bar!".to_string()),
94            "baz" => Some("%baz%".to_string()),
95            _     => Some("( ・ω・)?".to_string()),
96        }
97    }
98
99    #[test]
100    fn substitute_basic() {
101        assert_eq!("foo!!", super::substitute("%foo%", mapfn));
102        assert_eq!("!bar!", super::substitute("%bar%", mapfn));
103        assert_eq!("%baz%", super::substitute("%baz%", mapfn));
104        assert_eq!("foo is foo!!", super::substitute("foo is %foo%", mapfn));
105        assert_eq!("!bar! not ( ・ω・)?", super::substitute("%bar% not %foobar%", mapfn));
106        assert_eq!("foo!!!bar!%baz%", super::substitute("%foo%%bar%%baz%", mapfn));
107    }
108
109    //#[bench]
110    //fn substitute_bench(b: &mut Bencher) {
111    //    b.iter(|| super::substitute("%foo%%bar%%baz%", mapfn));
112    //}
113
114    #[test]
115    fn substenvar_basic() {
116        ::std::env::set_var("FOO", "foo, foo!");
117        ::std::env::set_var("BAR", "foobar");
118
119        assert_eq!("foo, foo!", super::substenvar("%FOO%"));
120        assert_eq!("foobar says 'foo, foo!'", super::substenvar("%BAR% says '%FOO%'"));
121        assert_eq!("%BAZ%", super::substenvar("%BAZ%"));
122    }
123
124}