Skip to main content

multiversx_sc_meta/cli/
cli_args_validate.rs

1use std::{path::Path, process};
2
3use multiversx_sc_meta_lib::version_history::{VERSIONS, validate_template_tag};
4
5use crate::cmd::code_report::generate_report::{JSON, MD};
6
7use super::{
8    AccountArgs, CodeReportAction, CodeReportArgs, CompareArgs, CompileArgs, ConvertArgs,
9    InstallArgs, TemplateArgs, TemplateListArgs, UpgradeArgs,
10};
11
12impl TemplateArgs {
13    pub fn validate_args(&self) {
14        if let Some(name) = &self.name {
15            if !validate_contract_name(name) {
16                user_error(&format!(
17                    "Invalid contract name `{}`: Rust crate names must start with a letter or underscore and contain only letters, numbers, and underscores (_). Dots (.) and dashes (-) are not allowed.",
18                    name
19                ));
20            }
21        }
22
23        if let Some(tag) = &self.tag {
24            if !validate_template_tag(tag) {
25                user_error(&format!("Invalid template tag `{}`.", tag));
26            }
27        }
28    }
29}
30
31impl InstallArgs {
32    pub fn validate_args(&self) {
33        if self.command.is_none() {
34            user_error("Command expected after `install`");
35        }
36    }
37}
38
39impl TemplateListArgs {
40    pub fn validate_args(&self) {
41        if let Some(tag) = &self.tag {
42            if !validate_template_tag(tag) {
43                user_error(&format!("Invalid template tag `{}`.", tag));
44            }
45        }
46    }
47}
48
49impl CompileArgs {
50    pub fn validate_args(&self) {
51        if !matches_extension(&self.output, JSON) && !matches_extension(&self.output, MD) {
52            user_error("Create report is only available for Markdown or JSON output file.");
53        }
54    }
55}
56
57impl ConvertArgs {
58    pub fn validate_args(&self) {
59        if !matches_extension(&self.output, MD) {
60            user_error("Conversion output is only available for Markdown file extension");
61        }
62
63        if !matches_extension(&self.input, JSON) {
64            user_error("Conversion only available from JSON file extension");
65        }
66    }
67}
68
69impl CompareArgs {
70    pub fn validate_args(&self) {
71        if !matches_extension(&self.output, MD) {
72            user_error("Compare output is only available for Markdown file extension.");
73        }
74
75        if !matches_extension(&self.baseline, JSON) && !matches_extension(&self.new, JSON) {
76            user_error("Compare baseline and new are only available for JSON file extension.");
77        }
78    }
79}
80
81impl CodeReportArgs {
82    pub fn validate_args(&self) {
83        match &self.command {
84            CodeReportAction::Compile(compile_args) => compile_args.validate_args(),
85            CodeReportAction::Compare(compare_args) => compare_args.validate_args(),
86            CodeReportAction::Convert(convert_args) => convert_args.validate_args(),
87        }
88    }
89}
90
91impl AccountArgs {
92    pub fn validate_args(&self) {
93        if self.api.is_none() {
94            user_error("API needs to be specified");
95        }
96    }
97}
98
99impl UpgradeArgs {
100    pub fn validate_args(&self) {
101        if let Some(override_target_v) = &self.override_target_version {
102            if !VERSIONS.iter().any(|v| v.to_string() == *override_target_v) {
103                user_error(&format!("Invalid requested version: {}", override_target_v));
104            }
105        }
106    }
107}
108
109// helpers
110pub(crate) fn validate_contract_name(name: &str) -> bool {
111    let mut chars = name.chars();
112
113    let Some(first) = chars.next() else {
114        return false;
115    };
116
117    if !first.is_ascii_alphabetic() && first != '_' {
118        return false;
119    }
120
121    chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
122}
123
124pub fn matches_extension(path: &Path, extension: &str) -> bool {
125    path.extension()
126        .and_then(|e| e.to_str())
127        .map(|e| e == extension)
128        .unwrap_or(false)
129}
130
131pub(crate) fn user_error(msg: &str) -> ! {
132    eprintln!("Error: {}", msg);
133    process::exit(1);
134}