1use clap::ValueEnum;
2use std::{
3 cmp::Ordering,
4 collections::HashMap,
5 error::Error,
6 fmt,
7 fs::{self, File},
8 io::{Read, Write},
9 path::Path,
10 process::exit,
11 str::FromStr,
12};
13
14use serde::{Deserialize, Serialize};
15
16pub mod rocket_models;
17pub mod rocket_utils;
18pub mod utils;
19
20#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
21pub enum ORM {
22 TypeORM,
23 SQLAlchemy,
24 DjangoORM,
25 Diesel,
26}
27
28impl fmt::Display for ORM {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 match self {
31 ORM::TypeORM => write!(f, "TypeORM"),
32 ORM::SQLAlchemy => write!(f, "SQLAlchemy"),
33 ORM::DjangoORM => write!(f, "DjangoORM"),
34 ORM::Diesel => write!(f, "Diesel"),
35 }
36 }
37}
38
39#[derive(Deserialize, Debug, Serialize, Clone)]
40pub struct ConsumerDBSchema {
41 pub url: String,
42 pub lang: LANG,
43 pub orm: ORM,
44 pub root: String,
45 pub schema_id: Option<String>,
46 pub cache_schema_id: Option<String>,
47 pub message_queue_schema_id: Option<String>,
48 pub branch: Option<String>,
49}
50
51#[derive(Deserialize, Debug, Serialize, Clone)]
52pub struct ConsumerDBTables {
53 pub names: Vec<String>,
54}
55
56#[derive(Deserialize, Debug, Serialize, Clone)]
57pub struct ConsumerDBConfig {
58 pub schema: ConsumerDBSchema,
59 pub tables: ConsumerDBTables,
60}
61
62pub fn write_consumer_db_config<P: AsRef<Path>>(path: P, config: &ConsumerDBConfig) -> () {
63 let toml_string = toml::to_string(config).unwrap();
64 let mut file = File::create(path).unwrap();
65 file.write_all(toml_string.as_bytes()).unwrap();
66}
67
68pub fn read_consumer_db_config<P: AsRef<Path>>(
69 path: P,
70) -> Result<ConsumerDBConfig, Box<dyn Error>> {
71 let mut file = File::open(&path).map_err(|e| {
73 format!(
74 "Failed to open the file '{}': {}",
75 path.as_ref().display(),
76 e
77 )
78 })?;
79
80 let mut contents = String::new();
81
82 file.read_to_string(&mut contents).map_err(|e| {
84 format!(
85 "Failed to read the file '{}': {}",
86 path.as_ref().display(),
87 e
88 )
89 })?;
90
91 toml::from_str(&contents).map_err(|e| {
93 format!(
94 "Failed to parse TOML from file '{}': {}",
95 path.as_ref().display(),
96 e
97 )
98 .into()
99 })
100}
101
102#[derive(Debug, Clone)]
103pub struct Service {
104 pub schema_url: String,
105 pub name: String,
106}
107
108#[derive(Debug, Serialize, Deserialize, Clone, Copy, ValueEnum)]
109pub enum LANG {
110 Rust,
111 TS,
112 Python,
113 Shell,
114}
115
116impl fmt::Display for LANG {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 match self {
119 LANG::Rust => write!(f, "Rust"),
120 LANG::TS => write!(f, "TS"),
121 LANG::Python => write!(f, "Python"),
122 LANG::Shell => write!(f, "Shell"),
123 }
124 }
125}
126
127impl LANG {
128 pub fn all() -> Vec<LANG> {
129 vec![LANG::Rust, LANG::TS, LANG::Python, LANG::Shell]
130 }
131}
132
133#[derive(Deserialize, Debug, Serialize, Clone)]
134pub struct ServiceConfig {
135 pub services: Option<HashMap<String, HashMap<String, String>>>,
136 pub portals_refs: Option<HashMap<String, HashMap<String, String>>>,
137 pub ws_refs: Option<HashMap<String, HashMap<String, String>>>,
138 pub lang: LANG,
139 pub organization_id: String,
140 pub dir: Option<String>, pub refs_file: Option<String>,
142 pub spec_url: Option<String>,
143 pub urls: Option<HashMap<String, String>>,
144 pub urls_ws: Option<HashMap<String, String>>,
145 pub override_name: Option<String>,
146 pub service_type: Option<String>,
147 pub portal_config: Option<PortalConfig>,
148}
149
150#[derive(Deserialize, Debug, Serialize, Clone)]
151pub struct PortalConfig {
152 pub id: String,
153 pub logo_url: String,
154 pub disabled: bool,
155 pub access_group_id: Option<i64>,
156 pub tnc_url: Option<String>,
157 pub allow_registration: bool,
158 pub auth_redirection_path: Option<String>,
159 pub has_web_interface: bool,
160 pub friendly_name: String,
161}
162
163#[derive(Debug, Deserialize, Serialize, Clone)]
164pub struct Link {
165 pub internal: bool,
166 pub label: String,
167 pub icon: String,
168 pub link: String,
169}
170
171impl fmt::Display for Link {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 write!(
174 f,
175 "[internal: {}, label: {}, icon: {}]",
176 self.internal, self.label, self.icon
177 )
178 }
179}
180
181impl PartialEq for Link {
182 fn eq(&self, other: &Self) -> bool {
183 self.internal == other.internal
184 && self.label == other.label
185 && self.icon == other.icon
186 && self.link == other.link
187 }
188}
189
190#[derive(Deserialize, Debug, Serialize, Clone)]
191pub struct PackageMetadata {
192 pub lang: LANG,
193 pub package_type: String,
194 #[serde(default = "default_links")]
195 pub links: Vec<Link>,
196}
197
198fn default_links() -> Vec<Link> {
199 vec![]
200}
201
202#[derive(Debug, Clone)]
203pub enum FileType {
204 Py,
205 Toml,
206 Json,
207 Unknown,
208}
209
210impl FileType {
211 pub fn from_extension(ext: Option<&str>) -> FileType {
212 match ext {
213 Some("py") => FileType::Py,
214 Some("toml") => FileType::Toml,
215 Some("json") => FileType::Json,
216 _ => FileType::Unknown,
217 }
218 }
219}
220
221impl fmt::Display for FileType {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 match self {
224 FileType::Py => write!(f, "Py"),
225 FileType::Toml => write!(f, "Toml"),
226 FileType::Json => write!(f, "Json"),
227 FileType::Unknown => write!(f, "Unknown"),
228 }
229 }
230}
231
232#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
233pub enum Channel {
234 Final,
235 Nightly, Alpha,
237 Beta,
238}
239impl fmt::Display for Channel {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 match self {
242 Channel::Nightly => write!(f, "nightly"),
243 Channel::Final => write!(f, "final"),
244 Channel::Alpha => write!(f, "alpha"),
245 Channel::Beta => write!(f, "beta"),
246 }
247 }
248}
249
250impl From<&str> for Channel {
251 fn from(channel: &str) -> Self {
252 match channel {
253 "nightly" => Channel::Nightly,
254 "alpha" => Channel::Alpha,
255 "beta" => Channel::Beta,
256 "final" => Channel::Final,
257 c => {
258 println!("Unable to recognize the channel {:?}", c);
259 exit(1)
260 }
261 }
262 }
263}
264
265#[derive(Debug, Deserialize, Serialize, Clone, Copy, Eq)]
266pub struct Version {
267 pub channel: Channel,
268 pub major: u32,
269 pub minor: u32,
270 pub patch: u32,
271 pub revision: u32,
272}
273
274impl Version {
275 pub fn formatted(&self) -> String {
276 match &self.channel {
277 Channel::Final => {
278 format!("{}.{}.{}", self.major, self.minor, self.patch)
279 }
280 _ => {
281 format!(
282 "{}.{}.{}-{}.{}",
283 self.major, self.minor, self.patch, self.channel, self.revision
284 )
285 }
286 }
287 }
288 pub fn tuple(&self) -> String {
289 format!(
290 "({}, {}, {}, \"{}\", {})",
291 self.major, self.minor, self.patch, self.channel, self.revision
292 )
293 }
294
295 pub fn from_str(version: &str) -> Self {
296 let parts: Vec<&str> = version.split(|c| c == '.' || c == '-').collect();
297 let major = parts[0].parse().unwrap_or(0);
298 let minor = parts[1].parse().unwrap_or(0);
299 let patch = parts[2].parse().unwrap_or(0);
300 let (channel, revision) = if parts.len() > 3 {
301 (Channel::from(parts[3]), parts[4].parse().unwrap_or(0))
302 } else {
303 (Channel::Final, 0)
304 };
305
306 Version {
307 major,
308 minor,
309 patch,
310 channel,
311 revision,
312 }
313 }
314}
315
316impl Ord for Version {
317 fn cmp(&self, other: &Self) -> Ordering {
318 self.major
319 .cmp(&other.major)
320 .then(self.minor.cmp(&other.minor))
321 .then(self.patch.cmp(&other.patch))
322 .then(self.channel.cmp(&other.channel))
323 .then(self.revision.cmp(&other.revision))
324 }
325}
326
327impl PartialOrd for Version {
328 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
329 Some(self.cmp(other))
330 }
331}
332
333impl PartialEq for Version {
334 fn eq(&self, other: &Self) -> bool {
335 self.major == other.major
336 && self.minor == other.minor
337 && self.patch == other.patch
338 && self.channel == other.channel
339 && self.revision == other.revision
340 }
341}
342#[derive(Debug, Serialize, Deserialize, Clone)]
343pub enum OutputType {
344 String,
345 Tuple,
346}
347
348impl fmt::Display for OutputType {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 match self {
351 OutputType::String => write!(f, "String"),
352 OutputType::Tuple => write!(f, "Tuple"),
353 }
354 }
355}
356
357#[derive(Debug, Deserialize, Serialize, Clone)]
358pub struct Reference {
359 pub file_name: String,
360 #[serde(default = "default_output_type")] pub output_type: OutputType, pub variable: String,
363 #[serde(skip, default = "default_file_type")] pub file_type: FileType,
365}
366
367fn default_file_type() -> FileType {
368 FileType::Unknown
369}
370
371fn default_output_type() -> OutputType {
372 OutputType::String }
374
375#[derive(Debug, Deserialize, Serialize, Clone)]
376pub struct ReleaserSettings {
377 pub git_url_prefix: Option<String>,
378 #[serde(default = "default_take_snapshots")]
379 pub take_snapshots: bool,
380}
381
382fn default_take_snapshots() -> bool {
383 false
384}
385
386#[derive(Debug, Deserialize, Serialize, Clone)]
387pub struct ReleaserConfig {
388 pub settings: ReleaserSettings,
389 pub version: Version,
390 #[serde(default = "default_references")]
391 pub references: Vec<Reference>,
392}
393
394fn default_references() -> Vec<Reference> {
395 vec![]
396}
397
398pub fn read_releaser_config_file<P: AsRef<Path>>(
399 file_path: P,
400) -> Result<ReleaserConfig, Box<dyn std::error::Error>> {
401 let contents = fs::read_to_string(file_path)?;
403
404 let settings: ReleaserConfig = toml::de::from_str(&contents)?;
406
407 Ok(settings)
408}
409
410pub fn write_releaser_config_file(
411 file_path: &str,
412 config: &ReleaserConfig,
413) -> Result<(), Box<dyn Error>> {
414 let toml_str = toml::to_string(config)?;
415 fs::write(file_path, toml_str)?;
416 Ok(())
417}
418
419pub fn read_service_config_file<P: AsRef<Path>>(path: P) -> Result<ServiceConfig, Box<dyn Error>> {
420 let content = fs::read_to_string(path)?;
421 let config: ServiceConfig = toml::from_str(&content)?;
422 Ok(config)
423}
424
425pub fn read_package_metadata_file<P: AsRef<Path>>(
426 path: P,
427) -> Result<PackageMetadata, Box<dyn Error>> {
428 let content = fs::read_to_string(path)?;
429 let config: PackageMetadata = toml::from_str(&content)?;
430 Ok(config)
431}
432
433pub fn write_service_config_file<P: AsRef<Path>>(
434 path: P,
435 config: &ServiceConfig,
436) -> Result<(), Box<dyn Error>> {
437 let content = toml::to_string(config)?;
438 fs::write(path, content)?;
439 Ok(())
440}
441
442#[derive(Debug, Deserialize, Serialize, PartialEq)]
443pub struct GingerDBConfig {
444 pub branch: String,
445 pub organization_id: String,
446 pub database: Vec<DatabaseConfig>, }
448
449#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)]
450pub struct DatabaseConfig {
451 pub db_type: DbType, pub description: String,
453 pub enable: bool,
454 pub id: Option<String>,
455 pub name: String,
456 pub port: String,
457 pub studio_port: Option<String>,
458 #[serde(default = "default_links")]
459 pub links: Vec<Link>,
460}
461
462impl fmt::Display for DatabaseConfig {
463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464 write!(f, "{}", self.name)
465 }
466}
467
468#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)]
469#[serde(rename_all = "lowercase")] pub enum DbType {
471 Rdbms,
472 DocumentDb,
473 Cache,
474 MessageQueue,
475}
476
477impl fmt::Display for DbType {
478 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
479 let db_type_str = match self {
480 DbType::Rdbms => "rdbms",
481 DbType::DocumentDb => "documentdb",
482 DbType::Cache => "cache",
483 DbType::MessageQueue => "messagequeue",
484 };
485 write!(f, "{}", db_type_str)
486 }
487}
488
489impl FromStr for DbType {
490 type Err = String;
491
492 fn from_str(s: &str) -> Result<Self, Self::Err> {
493 match s.to_lowercase().as_str() {
494 "rdbms" => Ok(DbType::Rdbms),
495 "documentdb" => Ok(DbType::DocumentDb),
496 "cache" => Ok(DbType::Cache),
497 "messagequeue" => Ok(DbType::MessageQueue),
498 _ => Err(format!("'{}' is not a valid DbType", s)),
499 }
500 }
501}
502
503pub fn read_db_config(file_path: &str) -> Result<GingerDBConfig, Box<dyn std::error::Error>> {
504 let contents = fs::read_to_string(file_path)?;
505 let config: GingerDBConfig = toml::from_str(&contents)?;
506 Ok(config)
507}
508
509pub fn write_db_config(
510 file_path: &str,
511 config: &GingerDBConfig,
512) -> Result<(), Box<dyn std::error::Error>> {
513 let toml_string = toml::to_string(config)?;
514 let mut file = fs::File::create(file_path)?;
515 file.write_all(toml_string.as_bytes())?;
516 Ok(())
517}
518
519#[derive(ValueEnum, Clone, PartialEq)]
520pub enum Environment {
521 Dev,
522 Stage,
523 Prod,
524 ProdK8,
525 StageK8,
526}
527
528impl fmt::Display for Environment {
529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530 match self {
531 Environment::Dev => write!(f, "dev"),
532 Environment::Stage => write!(f, "stage"),
533 Environment::Prod => write!(f, "prod"),
534 Environment::ProdK8 => write!(f, "prod_k8"),
535 Environment::StageK8 => write!(f, "stage_k8"),
536 }
537 }
538}