1use crate::{
18 TEST_PRIVATE_KEY,
19 root::{Env, Gitignore},
20 source::{MainFile, SourceDirectory},
21};
22use leo_errors::{PackageError, Result};
23
24use leo_retriever::{Manifest, NetworkName};
25use serde::Deserialize;
26use snarkvm::prelude::{Network, PrivateKey};
27use std::{path::Path, str::FromStr};
28
29#[derive(Deserialize)]
30pub struct Package {
31 pub name: String,
32 pub version: String,
33 pub description: Option<String>,
34 pub license: Option<String>,
35 pub network: NetworkName,
36}
37
38impl Package {
39 pub fn new(package_name: &str, network: NetworkName) -> Result<Self> {
40 if !Self::is_aleo_name_valid(package_name) {
42 return Err(PackageError::invalid_package_name(package_name).into());
43 }
44
45 Ok(Self {
46 name: package_name.to_owned(),
47 version: "0.1.0".to_owned(),
48 description: None,
49 license: None,
50 network,
51 })
52 }
53
54 pub fn is_aleo_name_valid(name: &str) -> bool {
58 if name.is_empty() {
60 tracing::error!("Aleo names must be nonempty");
61 return false;
62 }
63
64 let first = name.chars().next().unwrap();
65
66 if first == '_' {
68 tracing::error!("Aleo names cannot begin with an underscore");
69 return false;
70 }
71
72 if first.is_numeric() {
74 tracing::error!("Aleo names cannot begin with a number");
75 return false;
76 }
77
78 for current in name.chars() {
80 if !current.is_ascii_alphanumeric() && current != '_' {
82 tracing::error!("Aleo names must can only contain ASCII alphanumeric characters and underscores.");
83 return false;
84 }
85 }
86
87 true
88 }
89
90 pub fn can_initialize(package_name: &str, path: &Path) -> bool {
92 if !Self::is_aleo_name_valid(package_name) {
94 return false;
95 }
96
97 let mut result = true;
98 let mut existing_files = vec![];
99
100 if MainFile::exists_at(path) {
102 existing_files.push(MainFile::filename());
103 result = false;
104 }
105
106 if !existing_files.is_empty() {
107 tracing::error!("File(s) {:?} already exist", existing_files);
108 }
109
110 result
111 }
112
113 pub fn is_initialized(package_name: &str, path: &Path) -> bool {
115 if !Self::is_aleo_name_valid(package_name) {
117 return false;
118 }
119
120 if !MainFile::exists_at(path) {
122 return false;
123 }
124
125 true
126 }
127
128 pub fn initialize<N: Network>(package_name: &str, path: &Path, endpoint: String) -> Result<()> {
130 let path = path.join(package_name);
132
133 if path.exists() {
135 return Err(
136 PackageError::failed_to_initialize_package(package_name, &path, "Directory already exists").into()
137 );
138 }
139
140 std::fs::create_dir(&path).map_err(|e| PackageError::failed_to_initialize_package(package_name, &path, e))?;
142
143 std::env::set_current_dir(&path)
145 .map_err(|e| PackageError::failed_to_initialize_package(package_name, &path, e))?;
146
147 Gitignore::new().write_to(&path)?;
149
150 Env::<N>::new(Some(PrivateKey::<N>::from_str(TEST_PRIVATE_KEY)?), endpoint)?.write_to(&path)?;
153
154 let manifest = Manifest::default(package_name);
156 manifest.write_to_dir(&path)?;
157
158 SourceDirectory::create(&path)?;
160
161 MainFile::new(package_name).write_to(&path)?;
163
164 if !Self::is_initialized(package_name, &path) {
166 return Err(PackageError::failed_to_initialize_package(
167 package_name,
168 &path,
169 "Failed to correctly initialize package",
170 )
171 .into());
172 }
173
174 Ok(())
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_is_package_name_valid() {
184 assert!(Package::is_aleo_name_valid("foo"));
185 assert!(Package::is_aleo_name_valid("foo_bar"));
186 assert!(Package::is_aleo_name_valid("foo1"));
187 assert!(Package::is_aleo_name_valid("foo_bar___baz_"));
188
189 assert!(!Package::is_aleo_name_valid("foo-bar"));
190 assert!(!Package::is_aleo_name_valid("foo-bar-baz"));
191 assert!(!Package::is_aleo_name_valid("foo-1"));
192 assert!(!Package::is_aleo_name_valid(""));
193 assert!(!Package::is_aleo_name_valid("-"));
194 assert!(!Package::is_aleo_name_valid("-foo"));
195 assert!(!Package::is_aleo_name_valid("-foo-"));
196 assert!(!Package::is_aleo_name_valid("_foo"));
197 assert!(!Package::is_aleo_name_valid("foo--bar"));
198 assert!(!Package::is_aleo_name_valid("foo---bar"));
199 assert!(!Package::is_aleo_name_valid("foo--bar--baz"));
200 assert!(!Package::is_aleo_name_valid("foo---bar---baz"));
201 assert!(!Package::is_aleo_name_valid("foo*bar"));
202 assert!(!Package::is_aleo_name_valid("foo,bar"));
203 assert!(!Package::is_aleo_name_valid("1-foo"));
204 }
205}