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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
use crate::error::Error;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, fmt, path::PathBuf, str::FromStr};
use toml::Value as TomlValue;

/// Output languages for code generation
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum OutputLanguage {
    /// used for code generation of language-independent project files
    Poly,
    /// HTML documentation
    Html,
    /// Rust
    Rust,
    /// AssemblyScript (not currently supported)
    AssemblyScript,
    /// TinyGo (not currently supported)
    TinyGo,
    /// Go (not currently supported)
    Go,
    /// Python
    Python,
    /// C++ (not currently supported)
    Clang,
}

impl std::str::FromStr for OutputLanguage {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s.to_ascii_lowercase().as_str() {
            "poly" => Ok(OutputLanguage::Poly),
            "html" => Ok(OutputLanguage::Html),
            "rust" => Ok(OutputLanguage::Rust),
            "assemblyscript" => Ok(OutputLanguage::AssemblyScript),
            "tinygo" => Ok(OutputLanguage::TinyGo),
            "go" => Ok(OutputLanguage::Go),
            "python" => Ok(OutputLanguage::Python),
            "c++" | "clang" => Ok(OutputLanguage::Clang),
            _ => Err(Error::UnsupportedLanguage(s.to_string())),
        }
    }
}

impl fmt::Display for OutputLanguage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                OutputLanguage::Poly => "Poly",
                OutputLanguage::Html => "Html",
                OutputLanguage::Rust => "Rust",
                OutputLanguage::AssemblyScript => "AssemblyScript",
                OutputLanguage::TinyGo => "TinyGo",
                OutputLanguage::Go => "Go",
                OutputLanguage::Python => "Python",
                OutputLanguage::Clang => "Clang",
            }
        )
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct CodegenConfig {
    /// model inputs
    #[serde(default)]
    pub models: Vec<ModelSource>,

    /// This can be set by the cli to limit the languages generated.
    /// Without this flag, all languages in the toml file are generated.
    #[serde(default)]
    pub output_languages: Vec<OutputLanguage>,

    /// Language-specific output configuration
    #[serde(flatten)]
    pub languages: BTreeMap<OutputLanguage, LanguageConfig>,

    /// The directory containing the codegen.toml file, and the base_dir
    /// used for evaluating all relative paths in the file.
    /// This is not set inside the toml file but is set by the file reader,
    /// It is always set to an absolute path
    #[serde(default)]
    pub base_dir: PathBuf,
}

/// Source directory or url prefix for finding model files
/// For Paths, the `path` and `files` can be model files, or directories, which will
/// be searched recursively for model files with `.json` or `.smithy` extensions.
/// `files` array is optional if url or path directly references a model file,
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ModelSource {
    Url {
        url: String,
        #[serde(default)]
        files: Vec<String>,
    },
    Path {
        path: PathBuf,
        #[serde(default)]
        files: Vec<String>,
    },
}

pub enum ModelSourceKind {
    Url(String),
    Path(PathBuf),
}

impl FromStr for ModelSource {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        Ok(if s.starts_with("https:") || s.starts_with("http:") {
            ModelSource::Url {
                url: s.to_string(),
                files: Vec::default(),
            }
        } else {
            ModelSource::Path {
                path: s.into(),
                files: Vec::default(),
            }
        })
    }
}

impl ModelSource {
    /// convenience function to create a ModelSource for a single file path
    pub fn from_file<P: Into<std::path::PathBuf>>(path: P) -> ModelSource {
        ModelSource::Path {
            path: path.into(),
            files: Vec::default(),
        }
    }
}

impl fmt::Display for ModelSource {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                ModelSource::Url { url, files: _ } => format!("url({})", url),
                ModelSource::Path { path, files: _ } => format!("path({})", path.display()),
            }
        )
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct LanguageConfig {
    /// list of template files or template folders for importing templates.
    /// Overwrites any compiled-in templates with the same name(s)
    #[serde(default)]
    pub templates: Option<PathBuf>,

    /// Output directory. Required.
    /// (with weld cli, this will be relative to the output-dir on the command line)
    pub output_dir: PathBuf,

    /// Additional parameters
    #[serde(default)]
    pub parameters: BTreeMap<String, TomlValue>,

    /// Settings specific to individual output files
    #[serde(default)]
    pub files: Vec<OutputFile>,
}

/// Output-file specific settings
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct OutputFile {
    /// path to output file, relative to language output_dir. Required.
    pub path: PathBuf,

    /// name of handlebars template file (without hbs extension)
    /// Not used for files generated by codegen
    #[serde(default)]
    pub hbs: Option<String>,

    /// True if file should be generated only for 'create' operations
    #[serde(default)]
    pub create_only: bool,

    /// Optionally, limit code generation for this file to shapes in this namespace
    #[serde(default)]
    pub namespace: Option<String>,

    /// optional additional parameters for this file
    #[serde(flatten)]
    pub params: BTreeMap<String, TomlValue>,
}

impl FromStr for CodegenConfig {
    type Err = Error;

    fn from_str(content: &str) -> std::result::Result<CodegenConfig, Self::Err> {
        let config =
            toml::from_str(content).map_err(|e| Error::Other(format!("codegen: {}", e)))?;
        Ok(config)
    }
}