titleformat_rs/
environment.rs

1use std::collections::HashMap;
2
3use crate::types::Error;
4use crate::types::Error::*;
5use std::cell::RefCell;
6
7use crate::functions;
8use crate::functions::num::to_int;
9
10#[derive(Debug, PartialEq, Clone)]
11pub struct Value {
12    pub val: String,
13    pub cond: bool,
14}
15
16pub fn value_string(s: &str, c: bool) -> Value {
17    Value {
18        val: String::from(s),
19        cond: c,
20    }
21}
22
23#[derive(Clone)]
24pub enum FuncValue {
25    NativeFn(fn(Vec<String>) -> String),
26    NativeFnError(fn(Vec<String>) -> Result<String, Error>),
27    NativeCondFnError(fn(Vec<Value>) -> Result<Value, Error>),
28    NativeEnvFnError(fn(&Environment, Vec<Value>) -> Result<Value, Error>),
29}
30
31/* everything is a string... */
32/* like a register file in a cpu, but with strings! */
33#[derive(Clone)]
34pub struct Environment {
35    vars: RefCell<HashMap<String, String>>,
36    metadata: HashMap<String, Vec<String>>,
37    funcs: HashMap<String, FuncValue>,
38}
39
40impl Environment {
41    fn add_default_functions(&mut self) {
42        self.funcs.insert(
43            String::from("add"),
44            FuncValue::NativeFnError(functions::num::add::add),
45        );
46        self.funcs.insert(
47            String::from("sub"),
48            FuncValue::NativeFnError(functions::num::sub::sub),
49        );
50        self.funcs.insert(
51            String::from("mul"),
52            FuncValue::NativeFnError(functions::num::mul::mul),
53        );
54        self.funcs.insert(
55            String::from("div"),
56            FuncValue::NativeFnError(functions::num::div::div),
57        );
58        self.funcs.insert(
59            String::from("min"),
60            FuncValue::NativeFnError(functions::num::min::min),
61        );
62        self.funcs.insert(
63            String::from("max"),
64            FuncValue::NativeFnError(functions::num::max::max),
65        );
66
67        self.funcs.insert(
68            String::from("eq"),
69            FuncValue::NativeCondFnError(functions::num::control::eq),
70        );
71        self.funcs.insert(
72            String::from("ne"),
73            FuncValue::NativeCondFnError(functions::num::control::ne),
74        );
75        self.funcs.insert(
76            String::from("gt"),
77            FuncValue::NativeCondFnError(functions::num::control::gt),
78        );
79        self.funcs.insert(
80            String::from("gte"),
81            FuncValue::NativeCondFnError(functions::num::control::gte),
82        );
83        self.funcs.insert(
84            String::from("lt"),
85            FuncValue::NativeCondFnError(functions::num::control::lt),
86        );
87        self.funcs.insert(
88            String::from("lte"),
89            FuncValue::NativeCondFnError(functions::num::control::lte),
90        );
91
92        self.funcs.insert(
93            String::from("if"),
94            FuncValue::NativeCondFnError(functions::control::if_::if_),
95        );
96        self.funcs.insert(
97            String::from("if2"),
98            FuncValue::NativeCondFnError(functions::control::if2::if2),
99        );
100        self.funcs.insert(
101            String::from("if3"),
102            FuncValue::NativeCondFnError(functions::control::if3::if3),
103        );
104        self.funcs.insert(
105            String::from("ifequal"),
106            FuncValue::NativeCondFnError(functions::control::ifequal::ifequal),
107        );
108        self.funcs.insert(
109            String::from("ifgreater"),
110            FuncValue::NativeCondFnError(functions::control::ifgreater::ifgreater),
111        );
112        self.funcs.insert(
113            String::from("iflonger"),
114            FuncValue::NativeCondFnError(functions::control::iflonger::iflonger),
115        );
116        self.funcs.insert(
117            String::from("select"),
118            FuncValue::NativeCondFnError(functions::control::select::select),
119        );
120        self.funcs.insert(
121            String::from("and"),
122            FuncValue::NativeCondFnError(functions::control::and::and),
123        );
124        self.funcs.insert(
125            String::from("or"),
126            FuncValue::NativeCondFnError(functions::control::or::or),
127        );
128        self.funcs.insert(
129            String::from("xor"),
130            FuncValue::NativeCondFnError(functions::control::xor::xor),
131        );
132        self.funcs.insert(
133            String::from("not"),
134            FuncValue::NativeCondFnError(functions::control::not::not),
135        );
136
137        self.funcs.insert(
138            String::from("crlf"),
139            FuncValue::NativeCondFnError(functions::str::constants::crlf),
140        );
141        self.funcs.insert(
142            String::from("tab"),
143            FuncValue::NativeCondFnError(functions::str::constants::tab),
144        );
145        self.funcs.insert(
146            String::from("noop"),
147            FuncValue::NativeCondFnError(functions::str::constants::noop),
148        );
149
150        self.funcs.insert(
151            String::from("upper"),
152            FuncValue::NativeCondFnError(functions::str::case::upper),
153        );
154        self.funcs.insert(
155            String::from("lower"),
156            FuncValue::NativeCondFnError(functions::str::case::lower),
157        );
158        self.funcs.insert(
159            String::from("firstalphachar"),
160            FuncValue::NativeCondFnError(functions::str::case::firstalphachar),
161        );
162
163        self.funcs.insert(
164            String::from("len"),
165            FuncValue::NativeCondFnError(functions::str::size::len),
166        );
167        self.funcs.insert(
168            String::from("longer"),
169            FuncValue::NativeCondFnError(functions::str::size::longer),
170        );
171
172        self.funcs.insert(
173            String::from("stripprefix"),
174            FuncValue::NativeCondFnError(functions::str::modify::stripprefix),
175        );
176        self.funcs.insert(
177            String::from("swapprefix"),
178            FuncValue::NativeCondFnError(functions::str::modify::swapprefix),
179        );
180        self.funcs.insert(
181            String::from("cut"),
182            FuncValue::NativeCondFnError(functions::str::modify::cut),
183        );
184        self.funcs.insert(
185            String::from("left"),
186            FuncValue::NativeCondFnError(functions::str::modify::left),
187        );
188        self.funcs.insert(
189            String::from("num"),
190            FuncValue::NativeCondFnError(functions::str::format::num),
191        );
192
193        self.funcs.insert(
194            String::from("meta"),
195            FuncValue::NativeEnvFnError(Environment::meta_value),
196        );
197        self.funcs.insert(
198            String::from("meta_sep"),
199            FuncValue::NativeEnvFnError(Environment::meta_sep_value),
200        );
201        self.funcs.insert(
202            String::from("meta_num"),
203            FuncValue::NativeEnvFnError(Environment::meta_num_value),
204        );
205        self.funcs.insert(
206            String::from("meta_test"),
207            FuncValue::NativeEnvFnError(Environment::meta_test_value),
208        );
209        self.funcs.insert(
210            String::from("get"),
211            FuncValue::NativeEnvFnError(Environment::get_value),
212        );
213        self.funcs.insert(
214            String::from("put"),
215            FuncValue::NativeEnvFnError(Environment::put_value),
216        );
217        self.funcs.insert(
218            String::from("puts"),
219            FuncValue::NativeEnvFnError(Environment::puts_value),
220        );
221
222        self.funcs.insert(
223            String::from("year"),
224            FuncValue::NativeCondFnError(functions::str::datetime::year),
225        );
226    }
227
228    /// Constructs a new `Environment`
229    ///
230    /// # Examples
231    ///
232    /// Constructing an `Environment` without any metadata
233    ///
234    /// ```
235    /// # use titleformat_rs::environment::Environment;
236    /// # use std::collections::HashMap;
237    /// let env = Environment::new(HashMap::new());
238    /// ```
239    ///
240    /// Constructing an `Environment` with various metadata
241    ///
242    /// ```
243    /// # use titleformat_rs::environment::Environment;
244    /// # use std::collections::HashMap;
245    /// let mut metadata = HashMap::new();
246    /// metadata.insert(String::from("key"), vec![String::from("value1"), String::from("value2")]);
247    /// let env = Environment::new(metadata);
248    /// ```
249    pub fn new(metadata: HashMap<String, Vec<String>>) -> Self {
250        let mut env = Environment {
251            vars: RefCell::new(HashMap::new()),
252            metadata: metadata.clone(),
253            funcs: HashMap::new(),
254        };
255        Self::add_default_functions(&mut env);
256        env
257    }
258
259    fn put_value(&self, args: Vec<Value>) -> Result<Value, Error> {
260        match args.len() {
261            2 => Ok(value_string(&self.put(&args[0].val, &args[1].val), true)),
262            _ => Err(InvalidNativeFunctionArgs(String::from("put"), args.len())),
263        }
264    }
265
266    /* sets %key% to val and returns val */
267    fn put(&self, key: &str, val: &str) -> String {
268        let s = String::from(val);
269        self.vars.borrow_mut().insert(String::from(key), s.clone());
270        s
271    }
272
273    fn puts_value(&self, args: Vec<Value>) -> Result<Value, Error> {
274        match args.len() {
275            2 => Ok(value_string(
276                {
277                    self.puts(&args[0].val, &args[1].val);
278                    ""
279                },
280                true,
281            )),
282            _ => Err(InvalidNativeFunctionArgs(String::from("puts"), args.len())),
283        }
284    }
285
286    /* sets key to val inside vars */
287    fn puts(&self, key: &str, val: &str) {
288        self.vars
289            .borrow_mut()
290            .insert(String::from(key), String::from(val));
291    }
292
293    fn get_value(&self, args: Vec<Value>) -> Result<Value, Error> {
294        match args.len() {
295            1 => Ok(self.get(&args[0].val)),
296            _ => Err(InvalidNativeFunctionArgs(String::from("get"), args.len())),
297        }
298    }
299
300    /* sets key from vars */
301    fn get(&self, key: &str) -> Value {
302        match self.vars.borrow().get(key) {
303            Some(v) => Value {
304                val: v.clone(),
305                cond: true,
306            },
307            None => Value {
308                val: String::from("?"),
309                cond: false,
310            },
311        }
312    }
313
314    pub fn get_variable(&self, key: &str) -> Value {
315        self.meta_i(key, 0)
316    }
317
318    /* gets the ith key from the metadata */
319    fn meta_i(&self, key: &str, i: usize) -> Value {
320        match self.metadata.get(key) {
321            Some(v) => {
322                if i >= v.len() {
323                    value_string("?", false)
324                } else {
325                    value_string(&v[i], true)
326                }
327            }
328            None => value_string("?", false),
329        }
330    }
331
332    /* gets the key from the metadata separated by ", " */
333    fn meta(&self, key: &str) -> Value {
334        self.meta_sep(key, ", ")
335    }
336
337    /* gets the key from the metadata separated by sep */
338    fn meta_sep(&self, key: &str, sep: &str) -> Value {
339        self.meta_sep_with_last(key, sep, sep)
340    }
341
342    /* gets the key from the metadata separated by ", " and last separator with last_sep */
343    fn meta_sep_with_last(&self, key: &str, sep: &str, last_sep: &str) -> Value {
344        match self.metadata.get(key) {
345            Some(v) => {
346                let mut s = String::from("");
347                for (i, val) in v.iter().enumerate() {
348                    if i > 0 && i + 1 >= v.len() {
349                        s.push_str(last_sep);
350                    } else if i > 0 {
351                        s.push_str(sep);
352                    }
353                    s.push_str(val);
354                }
355                value_string(&s, true)
356            }
357            None => value_string("?", false),
358        }
359    }
360
361    fn meta_num(&self, key: &str) -> usize {
362        match self.metadata.get(key) {
363            Some(v) => v.len(),
364            None => 0,
365        }
366    }
367
368    /*
369     * $meta(name)
370     * Returns value of tag called name. If multiple values of that tag exist,
371     * they are concatenated with ", " as separator.
372     * Example: $meta(artist) → "He, She, It"
373     *
374     * $meta(name,n)
375     * Returns value of n-th (0,1,2 and so on) tag called name.
376     * Example: $meta(artist,1) → "She"
377     */
378    fn meta_value(&self, args: Vec<Value>) -> Result<Value, Error> {
379        match args.len() {
380            1 => Ok(self.meta(&args[0].val)),
381            2 => Ok(self.meta_i(&args[0].val, to_int(&args[1].val) as usize)),
382            _ => Err(InvalidNativeFunctionArgs(String::from("meta"), args.len())),
383        }
384    }
385
386    /*
387     * $meta_sep(name,sep)
388     * Returns value of tag called name. If multiple values of that tag exist, they are concatenated with sep as separator.
389     * Example: $meta_sep(artist,' + ') → "He + She + It"
390     *
391     * $meta_sep(name,sep,lastsep)
392     * Returns value of tag called name. If multiple values of that tag exist,
393     * they are concatenated with sep as separator between all but the last two
394     * values which are concatenated with lastsep.
395     * Example: $meta_sep(artist,', ',', and ') → "He, She, and It"
396     */
397    fn meta_sep_value(&self, args: Vec<Value>) -> Result<Value, Error> {
398        match args.len() {
399            2 => Ok(self.meta_sep(&args[0].val, &args[1].val)),
400            3 => Ok(self.meta_sep_with_last(&args[0].val, &args[1].val, &args[2].val)),
401            _ => Err(InvalidNativeFunctionArgs(
402                String::from("meta_sep"),
403                args.len(),
404            )),
405        }
406    }
407
408    /*
409     * $meta_test(...)
410     * Returns 1, if all given tags exist, undefined otherwise.
411     * Example: $meta_test(artist,title) → true
412     */
413    fn meta_test_value(&self, args: Vec<Value>) -> Result<Value, Error> {
414        match args.len() {
415            0 => Err(InvalidNativeFunctionArgs(
416                String::from("meta_num"),
417                args.len(),
418            )),
419            _ => Ok(value_string(
420                "",
421                args.iter().all(|i| self.meta_num(&i.val) > 0),
422            )),
423        }
424    }
425
426    /*
427     * $meta_num(name)
428     * Returns the number of values for the tag called name.
429     * Example: $meta_num(artist) → 3
430     */
431    fn meta_num_value(&self, args: Vec<Value>) -> Result<Value, Error> {
432        match args.len() {
433            1 => Ok(value_string(&self.meta_num(&args[0].val).to_string(), true)),
434            _ => Err(InvalidNativeFunctionArgs(
435                String::from("meta_num"),
436                args.len(),
437            )),
438        }
439    }
440
441    pub fn call(&self, name: &str, args: Vec<Value>) -> Result<Value, Error> {
442        let f = { self.funcs.get(name) };
443        match f {
444            Some(func_val) => match func_val {
445                FuncValue::NativeFn(func) => {
446                    /* return true only if all inputs are true */
447                    let c = args.iter().all(|val| val.cond);
448                    Ok(Value {
449                        val: func(args.iter().map(|a| a.val.clone()).collect()),
450                        cond: c,
451                    })
452                }
453                FuncValue::NativeFnError(func) => {
454                    /* return true only if all inputs are true */
455                    let c = args.iter().all(|val| val.cond);
456                    Ok(Value {
457                        val: func(args.iter().map(|a| a.val.clone()).collect())?,
458                        cond: c,
459                    })
460                }
461                FuncValue::NativeCondFnError(func) => Ok(func(args)?),
462                FuncValue::NativeEnvFnError(func) => Ok(func(self, args)?),
463            },
464            None => Err(Error::UndefinedFunction(String::from(name))),
465        }
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    use super::*;
472
473    #[test]
474    fn test_put_get() {
475        let env = Environment::new(HashMap::new());
476        assert_eq!(env.put("a", "val"), String::from("val"));
477        assert_eq!(env.get("a"), value_string("val", true));
478    }
479
480    #[test]
481    fn test_put_get_value() {
482        let env = Environment::new(HashMap::new());
483
484        assert_eq!(
485            env.put_value(vec![value_string("a", true), value_string("val", true)])
486                .unwrap(),
487            value_string("val", true)
488        );
489        assert_eq!(
490            env.get_value(vec![value_string("a", true)]).unwrap(),
491            value_string("val", true)
492        );
493
494        assert_eq!(
495            env.puts_value(vec![value_string("b", true), value_string("bar", true)])
496                .unwrap(),
497            value_string("", true)
498        );
499        assert_eq!(
500            env.get_value(vec![value_string("b", true)]).unwrap(),
501            value_string("bar", true)
502        );
503    }
504
505    #[test]
506    fn test_puts_get() {
507        let env = Environment::new(HashMap::new());
508        env.puts("a", "val");
509        assert_eq!(env.get("a"), value_string("val", true));
510    }
511
512    #[test]
513    fn test_get_unknown() {
514        let env = Environment::new(HashMap::new());
515        assert_eq!(env.get("invalid"), value_string("?", false));
516    }
517
518    #[test]
519    fn test_call() {
520        let env = Environment::new(HashMap::new());
521        assert_eq!(
522            env.call(
523                "add",
524                vec![value_string("2", true), value_string("2", true)]
525            )
526            .unwrap(),
527            value_string("4", true)
528        );
529    }
530
531    #[test]
532    fn test_call_unknown() {
533        let env = Environment::new(HashMap::new());
534        assert_eq!(
535            env.call("unknown", vec![]).err().unwrap(),
536            Error::UndefinedFunction(String::from("unknown"))
537        );
538    }
539
540    #[test]
541    fn wrong_n_arguments() {
542        let env = Environment::new(HashMap::new());
543        assert_eq!(
544            env.meta_value(vec![]).err().unwrap(),
545            InvalidNativeFunctionArgs(String::from("meta"), 0)
546        );
547        assert_eq!(
548            env.meta_sep_value(vec![]).err().unwrap(),
549            InvalidNativeFunctionArgs(String::from("meta_sep"), 0)
550        );
551        assert_eq!(
552            env.meta_num_value(vec![]).err().unwrap(),
553            InvalidNativeFunctionArgs(String::from("meta_num"), 0)
554        );
555        assert_eq!(
556            env.put_value(vec![]).err().unwrap(),
557            InvalidNativeFunctionArgs(String::from("put"), 0)
558        );
559        assert_eq!(
560            env.puts_value(vec![]).err().unwrap(),
561            InvalidNativeFunctionArgs(String::from("puts"), 0)
562        );
563        assert_eq!(
564            env.get_value(vec![]).err().unwrap(),
565            InvalidNativeFunctionArgs(String::from("get"), 0)
566        );
567    }
568
569    #[test]
570    fn test_meta() {
571        let mut m = HashMap::new();
572        m.insert(
573            String::from("a"),
574            vec![
575                String::from("0"),
576                String::from("1"),
577                String::from("2"),
578                String::from("3"),
579            ],
580        );
581        let env = Environment::new(m);
582        assert_eq!(
583            env.meta_value(vec![value_string("a", true)]).unwrap(),
584            value_string("0, 1, 2, 3", true)
585        );
586        assert_eq!(
587            env.meta_value(vec![value_string("a", true), value_string("1", true)])
588                .unwrap(),
589            value_string("1", true)
590        );
591        assert_eq!(
592            env.meta_value(vec![value_string("a", true), value_string("1000", true)])
593                .unwrap(),
594            value_string("?", false)
595        );
596    }
597
598    #[test]
599    fn test_meta_sep() {
600        let mut m = HashMap::new();
601        m.insert(
602            String::from("a"),
603            vec![
604                String::from("0"),
605                String::from("1"),
606                String::from("2"),
607                String::from("3"),
608            ],
609        );
610        let env = Environment::new(m);
611        assert_eq!(
612            env.meta_sep_value(vec![value_string("a", true), value_string("|", true)])
613                .unwrap(),
614            value_string("0|1|2|3", true)
615        );
616        assert_eq!(
617            env.meta_sep_value(vec![
618                value_string("a", true),
619                value_string("|", true),
620                value_string("^", true)
621            ])
622            .unwrap(),
623            value_string("0|1|2^3", true)
624        );
625    }
626
627    #[test]
628    fn test_meta_num() {
629        let mut m = HashMap::new();
630        m.insert(
631            String::from("a"),
632            vec![
633                String::from("0"),
634                String::from("1"),
635                String::from("2"),
636                String::from("3"),
637            ],
638        );
639        let env = Environment::new(m);
640        assert_eq!(
641            env.meta_num_value(vec![value_string("a", true)]).unwrap(),
642            value_string("4", true)
643        );
644        assert_eq!(
645            env.meta_num_value(vec![value_string("unknown", true)])
646                .unwrap(),
647            value_string("0", true)
648        );
649    }
650
651    #[test]
652    fn test_meta_test() {
653        let mut m = HashMap::new();
654        m.insert(
655            String::from("a"),
656            vec![
657                String::from("0"),
658                String::from("1"),
659                String::from("2"),
660                String::from("3"),
661            ],
662        );
663        m.insert(String::from("b"), vec![String::from("4")]);
664        let env = Environment::new(m);
665        assert_eq!(
666            env.meta_test_value(vec![value_string("a", true), value_string("b", true)])
667                .unwrap(),
668            value_string("", true)
669        );
670        assert_eq!(
671            env.meta_test_value(vec![value_string("unknown", true),])
672                .unwrap(),
673            value_string("", false)
674        );
675    }
676}