1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use std::collections::BTreeMap;
use std::str::FromStr;

use csv::WriterBuilder;
use lazy_static::lazy_static;
use serde_derive::{Deserialize, Serialize};
use serde_variant::to_variant_name;
use strum::EnumIter;
use strum::IntoEnumIterator;
use teller_providers::config::KV;

use crate::{Error, Result};

lazy_static! {
    pub static ref POSSIBLE_VALUES: String = {
        let providers: Vec<String> = Format::iter()
            .map(|provider| provider.to_string())
            .collect();
        providers.join(", ")
    };
}

#[derive(Serialize, Deserialize, Debug, Clone, EnumIter)]
pub enum Format {
    #[serde(rename = "csv")]
    CSV,
    #[serde(rename = "yaml")]
    YAML,
    #[serde(rename = "json")]
    JSON,
    #[serde(rename = "env")]
    ENV,
    #[serde(rename = "shell")]
    Shell,
}

impl std::fmt::Display for Format {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        to_variant_name(self).expect("only enum supported").fmt(f)
    }
}

impl FromStr for Format {
    type Err = &'static str;

    fn from_str(input: &str) -> Result<Self, Self::Err> {
        let providers = Self::iter()
            .map(|provider| (provider.to_string(), provider))
            .collect::<BTreeMap<String, Self>>();

        providers.get(input).map_or_else(
            || Err(&POSSIBLE_VALUES as &'static str),
            |provider| Ok(provider.clone()),
        )
    }
}

impl Format {
    /// Export current format type to string
    ///
    /// # Errors
    ///
    pub fn export(&self, kvs: &[KV]) -> Result<String> {
        match self {
            Self::YAML => Ok(serde_yaml::to_string(&KV::to_data(kvs))?),
            Self::JSON => Ok(serde_json::to_string(&KV::to_data(kvs))?),
            Self::CSV => Self::export_csv(kvs),
            Self::ENV => Ok(Self::export_env(kvs)),
            Self::Shell => Ok(Self::export_shell(kvs)),
        }
    }

    fn export_shell(kvs: &[KV]) -> String {
        let mut out = String::new();
        out.push_str("#!/bin/sh\n");

        for kv in kvs {
            out.push_str(&format!("export {}='{}'\n", kv.key, kv.value));
        }
        out
    }

    fn export_env(kvs: &[KV]) -> String {
        let mut out = String::new();
        for kv in kvs {
            out.push_str(&format!("{}={}\n", kv.key, kv.value));
        }
        out
    }

    fn export_csv(kvs: &[KV]) -> Result<String> {
        let mut wtr = WriterBuilder::new().from_writer(vec![]);
        for kv in kvs {
            wtr.write_record(&[kv.key.clone(), kv.value.clone()])?;
        }
        Ok(String::from_utf8(
            wtr.into_inner()
                .map_err(Box::from)
                .map_err(Error::CSVInner)?,
        )?)
    }
}