memflow/plugins/
args.rs

1/*!
2Connector argument handler.
3*/
4
5use std::fmt;
6use std::prelude::v1::*;
7
8use crate::error::{Error, ErrorKind, ErrorOrigin, Result};
9
10use cglue::{repr_cstring::ReprCString, vec::CVec};
11
12use core::convert::TryFrom;
13use hashbrown::HashMap;
14
15/// Argument wrapper for connectors
16///
17/// # Examples
18///
19/// Construct from a string:
20/// ```
21/// use memflow::plugins::Args;
22/// use std::convert::TryFrom;
23///
24/// let argstr = "opt1=test1,opt2=test2,opt3=test3";
25/// let args: Args = argstr.parse().unwrap();
26/// ```
27///
28/// Construct as builder:
29/// ```
30/// use memflow::plugins::Args;
31///
32/// let args = Args::new()
33///     .insert("arg1", "test1")
34///     .insert("arg2", "test2");
35/// ```
36#[repr(C)]
37#[derive(Debug, Clone)]
38#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
39pub struct Args {
40    // Just how many args do you have usually?
41    // Hashmap performance improvements may not be worth the complexity
42    // C/C++ users would have in constructing arguments structure.
43    args: CVec<ArgEntry>,
44}
45
46#[derive(Debug, Clone)]
47#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
48pub struct ArgEntry {
49    key: ReprCString,
50    value: ReprCString,
51}
52
53impl<T: Into<ReprCString>> From<(T, T)> for ArgEntry {
54    fn from((key, value): (T, T)) -> Self {
55        Self {
56            key: key.into(),
57            value: value.into(),
58        }
59    }
60}
61
62impl fmt::Display for Args {
63    /// Generates a string of key-value pairs containing the underlying data of the Args.
64    ///
65    /// This function will produce a string that can be properly parsed by the `parse` function again.
66    ///
67    /// # Remarks
68    ///
69    /// The sorting order of the underlying `HashMap` is random.
70    /// This function only guarantees that the 'default' value (if it is set) will be the first element.
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        let mut result = Vec::new();
73
74        if let Some(default) = self.get_default() {
75            result.push(default.to_string());
76        }
77
78        result.extend(
79            self.args
80                .iter()
81                .filter(|e| &*e.key != "default")
82                .map(|ArgEntry { key, value }| {
83                    if value.contains(',') || value.contains('=') {
84                        format!("{}=\"{}\"", key, value)
85                    } else {
86                        format!("{}={}", key, value)
87                    }
88                })
89                .collect::<Vec<_>>(),
90        );
91
92        write!(f, "{}", result.join(","))
93    }
94}
95
96impl std::str::FromStr for Args {
97    type Err = crate::error::Error;
98
99    /// Tries to create a `Args` structure from an argument string.
100    ///
101    /// The argument string is a string of comma seperated key-value pairs.
102    ///
103    /// An argument string can just contain keys and values:
104    /// `opt1=val1,opt2=val2,opt3=val3`
105    ///
106    /// The argument string can also contain a default value as the first entry
107    /// which will be placed as a default argument:
108    /// `default_value,opt1=val1,opt2=val2`
109    ///
110    /// This function can be used to initialize a connector from user input.
111    fn from_str(s: &str) -> Result<Self> {
112        let split = split_str_args(s, ',').collect::<Vec<_>>();
113
114        let mut map = HashMap::new();
115        for (i, kv) in split.iter().enumerate() {
116            let kvsplit = split_str_args(kv, '=').collect::<Vec<_>>();
117            if kvsplit.len() == 2 {
118                map.insert(kvsplit[0].to_string(), kvsplit[1].to_string());
119            } else if i == 0 && !kv.is_empty() {
120                map.insert("default".to_string(), kv.to_string());
121            }
122        }
123
124        Ok(Self {
125            args: map.into_iter().map(<_>::into).collect::<Vec<_>>().into(),
126        })
127    }
128}
129
130impl Default for Args {
131    /// Creates an empty `Args` struct.
132    fn default() -> Self {
133        Self {
134            args: Default::default(),
135        }
136    }
137}
138
139impl Args {
140    /// Creates an empty `Args` struct.
141    pub fn new() -> Self {
142        Self::default()
143    }
144
145    /// Creates a `Args` struct with a default (unnamed) value.
146    pub fn with_default(value: &str) -> Self {
147        Self::new().insert("default", value)
148    }
149
150    /// Consumes self, inserts the given key-value pair and returns the self again.
151    ///
152    /// This function can be used as a builder pattern when programatically
153    /// configuring connectors.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use memflow::plugins::Args;
159    ///
160    /// let args = Args::new()
161    ///     .insert("arg1", "test1")
162    ///     .insert("arg2", "test2");
163    /// ```
164    pub fn insert(mut self, key: &str, value: &str) -> Self {
165        if let Some(a) = self.args.iter_mut().find(|a| &*a.key == key) {
166            a.value = value.into();
167        } else {
168            self.args.push((key, value).into());
169        }
170        self
171    }
172
173    /// Tries to retrieve an entry from the options map.
174    /// If the entry was not found this function returns a `None` value.
175    pub fn get(&self, key: &str) -> Option<&str> {
176        self.args
177            .iter()
178            .filter(|a| &*a.key == key)
179            .map(|a| &*a.value)
180            .next()
181    }
182
183    /// Tries to retrieve the default entry from the options map.
184    /// If the entry was not found this function returns a `None` value.
185    ///
186    /// This function is a convenience wrapper for `args.get("default")`.
187    pub fn get_default(&self) -> Option<&str> {
188        self.get("default")
189    }
190}
191
192impl TryFrom<&str> for Args {
193    type Error = Error;
194
195    fn try_from(args: &str) -> Result<Self> {
196        args.parse()
197    }
198}
199
200impl TryFrom<String> for Args {
201    type Error = Error;
202
203    fn try_from(args: String) -> Result<Self> {
204        args.parse()
205    }
206}
207
208impl From<Args> for String {
209    fn from(args: Args) -> Self {
210        args.to_string()
211    }
212}
213
214/// Validator for connector arguments
215///
216/// # Examples
217///
218/// Builder:
219/// ```
220/// use memflow::plugins::{ArgsValidator, ArgDescriptor};
221///
222/// let validator = ArgsValidator::new()
223///     .arg(ArgDescriptor::new("default"))
224///     .arg(ArgDescriptor::new("arg1"));
225/// ```
226#[derive(Debug)]
227pub struct ArgsValidator {
228    args: Vec<ArgDescriptor>,
229}
230
231impl Default for ArgsValidator {
232    fn default() -> Self {
233        Self::new()
234    }
235}
236
237impl ArgsValidator {
238    /// Creates an empty `ArgsValidator` struct.
239    pub fn new() -> Self {
240        Self { args: Vec::new() }
241    }
242
243    /// Adds an `ArgDescriptor` to the validator and returns itself.
244    pub fn arg(mut self, arg: ArgDescriptor) -> Self {
245        self.args.push(arg);
246        self
247    }
248
249    pub fn validate(&self, args: &Args) -> Result<()> {
250        // check if all given args exist
251        for arg in args.args.iter() {
252            if !self.args.iter().any(|a| a.name == *arg.key) {
253                return Err(Error(ErrorOrigin::ArgsValidator, ErrorKind::ArgNotExists)
254                    .log_error(format!("argument {} does not exist", &*arg.key)));
255            }
256        }
257
258        for arg in self.args.iter() {
259            // check if required args are set
260            if arg.required && args.get(&arg.name).is_none() {
261                return Err(
262                    Error(ErrorOrigin::ArgsValidator, ErrorKind::RequiredArgNotFound).log_error(
263                        format!("argument {} is required but could not be found", arg.name),
264                    ),
265                );
266            }
267
268            // check if validate matches
269            if let Some(validator) = &arg.validator {
270                if let Some(value) = args.get(&arg.name) {
271                    if let Err(err) = validator(value) {
272                        return Err(Error(ErrorOrigin::ArgsValidator, ErrorKind::ArgValidation)
273                            .log_error(format!("argument {} is invalid: {}", arg.name, err)));
274                    }
275                }
276            }
277        }
278
279        Ok(())
280    }
281}
282
283impl fmt::Display for ArgsValidator {
284    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285        for (idx, arg) in self.args.iter().enumerate() {
286            if idx < self.args.len() - 1 {
287                writeln!(f, "{}", arg).ok();
288            } else {
289                write!(f, "{}", arg).ok();
290            }
291        }
292        Ok(())
293    }
294}
295
296pub type ArgValidator = Box<dyn Fn(&str) -> ::std::result::Result<(), &'static str>>;
297
298/// Describes a single validator argument.
299///
300/// # Examples
301///
302/// Builder:
303/// ```
304/// use memflow::plugins::ArgDescriptor;
305///
306/// let desc = ArgDescriptor::new("cache_size")
307///     .description("cache_size argument description")
308///     .required(true);
309/// ```
310pub struct ArgDescriptor {
311    pub name: String,
312    pub description: Option<String>,
313    pub required: bool,
314    pub validator: Option<ArgValidator>,
315}
316
317impl ArgDescriptor {
318    /// Creates a new `ArgDescriptor` with the given argument name.
319    pub fn new(name: &str) -> Self {
320        Self {
321            name: name.to_owned(),
322            description: None,
323            required: false,
324            validator: None,
325        }
326    }
327
328    /// Set the description for this argument.
329    ///
330    /// By default the description is `None`.
331    pub fn description(mut self, description: &str) -> Self {
332        self.description = Some(description.to_owned());
333        self
334    }
335
336    /// Set the required state for this argument.
337    ///
338    /// By default arguments are optional.
339    pub fn required(mut self, required: bool) -> Self {
340        self.required = required;
341        self
342    }
343
344    /// Sets the validator function for this argument.
345    ///
346    /// By default no validator is set.
347    ///
348    /// # Examples
349    ///
350    /// ```
351    /// use memflow::plugins::ArgDescriptor;
352    ///
353    /// let desc = ArgDescriptor::new("cache_size").validator(Box::new(|arg| {
354    ///     match arg == "valid_option" {
355    ///         true => Ok(()),
356    ///         false => Err("argument must be 'valid_option'"),
357    ///     }
358    /// }));
359    /// ```
360    pub fn validator(mut self, validator: ArgValidator) -> Self {
361        self.validator = Some(validator);
362        self
363    }
364}
365
366impl fmt::Display for ArgDescriptor {
367    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
368        write!(
369            f,
370            "{}: {}{}",
371            self.name,
372            self.description
373                .as_ref()
374                .unwrap_or(&"no description available".to_owned()),
375            if self.required { " (required)" } else { "" },
376        )
377    }
378}
379
380impl fmt::Debug for ArgDescriptor {
381    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
382        write!(
383            f,
384            "{}: {}{}",
385            self.name,
386            self.description
387                .as_ref()
388                .unwrap_or(&"no description available".to_owned()),
389            if self.required { " (required)" } else { "" },
390        )
391    }
392}
393
394/// Split a string into a list of separate parts based on ':' delimiter
395///
396/// This is a more advanced version of splitting that allows to do some basic escaping with
397/// quotation marks.
398///
399/// # Examples
400///
401/// ```
402/// use memflow::plugins::args::split_str_args;
403///
404/// let v: Vec<_> = split_str_args("a:b:c", ':').collect();
405/// assert_eq!(v, ["a", "b", "c"]);
406///
407/// let v: Vec<_> = split_str_args("a::c", ':').collect();
408/// assert_eq!(v, ["a", "", "c"]);
409///
410/// let v: Vec<_> = split_str_args("a:\"hello\":c", ':').collect();
411/// assert_eq!(v, ["a", "hello", "c"]);
412///
413/// let v: Vec<_> = split_str_args("a:\"hel:lo\":c", ':').collect();
414/// assert_eq!(v, ["a", "hel:lo", "c"]);
415///
416/// let v: Vec<_> = split_str_args("a:\"hel:lo:c", ':').collect();
417/// assert_eq!(v, ["a", "\"hel:lo:c"]);
418///
419/// let v: Vec<_> = split_str_args("a:'hel\":lo\"':c", ':').collect();
420/// assert_eq!(v, ["a", "hel\":lo\"", "c"]);
421///
422/// let v: Vec<_> = split_str_args("a:hel\":lo\":c", ':').collect();
423/// assert_eq!(v, ["a", "hel\":lo\"", "c"]);
424/// ```
425pub fn split_str_args(inp: &str, split_char: char) -> impl Iterator<Item = &str> {
426    let mut prev_char = '\0';
427    let mut quotation_char = None;
428
429    const VALID_QUOTES: &str = "\"'`";
430    assert!(!VALID_QUOTES.contains(split_char));
431
432    inp.split(move |c| {
433        let mut ret = false;
434
435        // found an unescaped quote
436        if VALID_QUOTES.contains(c) && prev_char != '\\' {
437            // scan string up until we find the same quotation char again
438            match quotation_char {
439                Some(qc) if qc == c => {
440                    quotation_char = None;
441                }
442                None => quotation_char = Some(c),
443                _ => (),
444            }
445        }
446
447        if quotation_char.is_none() && c == split_char {
448            ret = true;
449        }
450
451        prev_char = c;
452        ret
453    })
454    .map(|s| {
455        if let Some(c) = s.chars().next().and_then(|a| {
456            if s.ends_with(a) && VALID_QUOTES.contains(a) {
457                Some(a)
458            } else {
459                None
460            }
461        }) {
462            s.split_once(c)
463                .and_then(|(_, a)| a.rsplit_once(c))
464                .map(|(a, _)| a)
465                .unwrap_or("")
466        } else {
467            s
468        }
469    })
470}
471
472pub fn parse_vatcache(args: &Args) -> Result<Option<(usize, u64)>> {
473    match args.get("vatcache").unwrap_or("default") {
474        "default" => Ok(Some((0, 0))),
475        "none" => Ok(None),
476        size => Ok(Some(parse_vatcache_args(size)?)),
477    }
478}
479
480fn parse_vatcache_args(vargs: &str) -> Result<(usize, u64)> {
481    let mut sp = vargs.splitn(2, ';');
482    let (size, time) = (
483        sp.next().ok_or_else(|| {
484            Error(ErrorOrigin::OsLayer, ErrorKind::Configuration)
485                .log_error("Failed to parse VAT size")
486        })?,
487        sp.next().unwrap_or("0"),
488    );
489    let size = usize::from_str_radix(size, 16).map_err(|_| {
490        Error(ErrorOrigin::OsLayer, ErrorKind::Configuration).log_error("Failed to parse VAT size")
491    })?;
492    let time = time.parse::<u64>().map_err(|_| {
493        Error(ErrorOrigin::OsLayer, ErrorKind::Configuration)
494            .log_error("Failed to parse VAT validity time")
495    })?;
496    Ok((size, time))
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502
503    #[test]
504    pub fn from_str() {
505        let argstr = "opt1=test1,opt2=test2,opt3=test3";
506        let args: Args = argstr.parse().unwrap();
507        assert_eq!(args.get("opt1").unwrap(), "test1");
508        assert_eq!(args.get("opt2").unwrap(), "test2");
509        assert_eq!(args.get("opt3").unwrap(), "test3");
510    }
511
512    #[test]
513    pub fn from_str_default() {
514        let argstr = "test0,opt1=test1,opt2=test2,opt3=test3";
515        let args: Args = argstr.parse().unwrap();
516        assert_eq!(args.get_default().unwrap(), "test0");
517        assert_eq!(args.get("opt1").unwrap(), "test1");
518        assert_eq!(args.get("opt2").unwrap(), "test2");
519        assert_eq!(args.get("opt3").unwrap(), "test3");
520    }
521
522    #[test]
523    pub fn from_str_default2() {
524        let argstr = "opt1=test1,test0";
525        let args: Args = argstr.parse().unwrap();
526        assert_eq!(args.get_default(), None);
527        assert_eq!(args.get("opt1").unwrap(), "test1");
528    }
529
530    #[test]
531    pub fn builder() {
532        let args = Args::new().insert("arg1", "test1").insert("arg2", "test2");
533        assert_eq!(args.get("arg1").unwrap(), "test1");
534        assert_eq!(args.get("arg2").unwrap(), "test2");
535    }
536
537    #[test]
538    pub fn parse_empty() {
539        let argstr = "opt1=test1,test0";
540        let _: Args = argstr.parse().unwrap();
541    }
542
543    #[test]
544    pub fn to_string() {
545        let argstr = "opt1=test1,opt2=test2,opt3=test3";
546        let args: Args = argstr.parse().unwrap();
547        let args2: Args = args.to_string().parse().unwrap();
548        assert_eq!(args2.get_default(), None);
549        assert_eq!(args2.get("opt1").unwrap(), "test1");
550        assert_eq!(args2.get("opt2").unwrap(), "test2");
551        assert_eq!(args2.get("opt3").unwrap(), "test3");
552    }
553
554    #[test]
555    pub fn to_string_with_default() {
556        let argstr = "test0,opt1=test1,opt2=test2,opt3=test3";
557        let args: Args = argstr.parse().unwrap();
558        let args2: Args = args.to_string().parse().unwrap();
559        assert_eq!(args2.get_default().unwrap(), "test0");
560        assert_eq!(args2.get("opt1").unwrap(), "test1");
561        assert_eq!(args2.get("opt2").unwrap(), "test2");
562        assert_eq!(args2.get("opt3").unwrap(), "test3");
563    }
564
565    #[test]
566    pub fn double_quotes() {
567        let argstr = "opt1=test1,test0,opt2=\"test2,test3\"";
568        let args: Args = argstr.parse().unwrap();
569        let args2: Args = args.to_string().parse().unwrap();
570        assert_eq!(args2.get("opt1").unwrap(), "test1");
571        assert_eq!(args2.get("opt2").unwrap(), "test2,test3");
572    }
573
574    #[test]
575    pub fn double_quotes_eq() {
576        let argstr = "opt1=test1,test0,opt2=\"test2,test3=test4\"";
577        let args: Args = argstr.parse().unwrap();
578        let args2: Args = args.to_string().parse().unwrap();
579        assert_eq!(args2.get("opt1").unwrap(), "test1");
580        assert_eq!(args2.get("opt2").unwrap(), "test2,test3=test4");
581    }
582
583    #[test]
584    pub fn slashes() {
585        let argstr = "device=vmware://,remote=rpc://insecure:computername.local";
586        let args: Args = argstr.parse().unwrap();
587        let args2: Args = args.to_string().parse().unwrap();
588        assert_eq!(args2.get("device").unwrap(), "vmware://");
589        assert_eq!(
590            args2.get("remote").unwrap(),
591            "rpc://insecure:computername.local"
592        );
593    }
594
595    #[test]
596    pub fn slashes_quotes_split() {
597        let v: Vec<_> = split_str_args(
598            "url1=\"uri://ip=test:test@test,test\",url2=\"test:test@test.de,test2:test2@test2.de\"",
599            ',',
600        )
601        .collect();
602        assert_eq!(
603            v,
604            [
605                "url1=\"uri://ip=test:test@test,test\"",
606                "url2=\"test:test@test.de,test2:test2@test2.de\""
607            ]
608        );
609    }
610
611    #[test]
612    pub fn slashes_quotes() {
613        let argstr = "device=\"RAWUDP://ip=127.0.0.1\"";
614        let args: Args = argstr.parse().unwrap();
615        let args2: Args = args.to_string().parse().unwrap();
616        assert_eq!(args2.get("device").unwrap(), "RAWUDP://ip=127.0.0.1");
617    }
618
619    #[test]
620    pub fn slashes_mixed_quotes() {
621        let argstr = "device=`RAWUDP://ip=127.0.0.1`";
622        let args: Args = argstr.parse().unwrap();
623        assert_eq!(args.get("device").unwrap(), "RAWUDP://ip=127.0.0.1");
624
625        let arg2str = args.to_string();
626        assert_eq!(arg2str, "device=\"RAWUDP://ip=127.0.0.1\"");
627
628        let args2: Args = arg2str.parse().unwrap();
629        assert_eq!(args2.get("device").unwrap(), "RAWUDP://ip=127.0.0.1");
630    }
631
632    #[test]
633    pub fn slashes_quotes_complex() {
634        let argstr =
635            "url1=\"uri://ip=test:test@test,test\",url2=\"test:test@test.de,test2:test2@test2.de\"";
636        let args: Args = argstr.parse().unwrap();
637        let args2: Args = args.to_string().parse().unwrap();
638        assert_eq!(args2.get("url1").unwrap(), "uri://ip=test:test@test,test");
639        assert_eq!(
640            args2.get("url2").unwrap(),
641            "test:test@test.de,test2:test2@test2.de"
642        );
643    }
644
645    #[test]
646    pub fn validator_success() {
647        let validator = ArgsValidator::new()
648            .arg(ArgDescriptor::new("default"))
649            .arg(ArgDescriptor::new("opt1"));
650
651        let argstr = "test0,opt1=test1";
652        let args: Args = argstr.parse().unwrap();
653
654        assert_eq!(validator.validate(&args), Ok(()));
655    }
656
657    #[test]
658    pub fn validator_success_optional() {
659        let validator = ArgsValidator::new().arg(ArgDescriptor::new("opt1").required(false));
660
661        let args: Args = "".parse().unwrap();
662
663        assert_eq!(validator.validate(&args), Ok(()));
664    }
665
666    #[test]
667    pub fn validator_error_required() {
668        let validator = ArgsValidator::new().arg(ArgDescriptor::new("opt1").required(true));
669
670        let args: Args = "".parse().unwrap();
671
672        assert_eq!(
673            validator.validate(&args),
674            Err(Error(
675                ErrorOrigin::ArgsValidator,
676                ErrorKind::RequiredArgNotFound
677            ))
678        );
679    }
680
681    #[test]
682    pub fn validator_error_notexist() {
683        let validator = ArgsValidator::new().arg(ArgDescriptor::new("opt1"));
684
685        let argstr = "opt2=arg2";
686        let args: Args = argstr.parse().unwrap();
687
688        assert_eq!(
689            validator.validate(&args),
690            Err(Error(ErrorOrigin::ArgsValidator, ErrorKind::ArgNotExists))
691        );
692    }
693
694    #[test]
695    pub fn validator_validate_success() {
696        let validator =
697            ArgsValidator::new().arg(ArgDescriptor::new("default").validator(Box::new(|arg| {
698                match arg == "valid_option" {
699                    true => Ok(()),
700                    false => Err("argument must be 'valid_option'"),
701                }
702            })));
703
704        let argstr = "default=valid_option";
705        let args: Args = argstr.parse().unwrap();
706
707        assert_eq!(validator.validate(&args), Ok(()));
708    }
709
710    #[test]
711    pub fn validator_validate_fail() {
712        let validator =
713            ArgsValidator::new().arg(ArgDescriptor::new("default").validator(Box::new(|arg| {
714                match arg == "valid_option" {
715                    true => Ok(()),
716                    false => Err("argument must be 'valid_option'"),
717                }
718            })));
719
720        let argstr = "invalid_option";
721        let args: Args = argstr.parse().unwrap();
722
723        assert_eq!(
724            validator.validate(&args),
725            Err(Error(ErrorOrigin::ArgsValidator, ErrorKind::ArgValidation))
726        );
727    }
728}