multiversx_sc_meta/cli/
cli_args_validate.rs1use 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
109pub(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}