use crate::{
TEST_PRIVATE_KEY,
root::{Env, Gitignore},
source::{MainFile, SourceDirectory},
};
use leo_errors::{PackageError, Result};
use leo_retriever::{Manifest, NetworkName};
use serde::Deserialize;
use snarkvm::prelude::{Network, PrivateKey};
use std::{path::Path, str::FromStr};
#[derive(Deserialize)]
pub struct Package {
pub name: String,
pub version: String,
pub description: Option<String>,
pub license: Option<String>,
pub network: NetworkName,
}
impl Package {
pub fn new(package_name: &str, network: NetworkName) -> Result<Self> {
if !Self::is_aleo_name_valid(package_name) {
return Err(PackageError::invalid_package_name(package_name).into());
}
Ok(Self {
name: package_name.to_owned(),
version: "0.1.0".to_owned(),
description: None,
license: None,
network,
})
}
pub fn is_aleo_name_valid(name: &str) -> bool {
if name.is_empty() {
tracing::error!("Aleo names must be nonempty");
return false;
}
let first = name.chars().next().unwrap();
if first == '_' {
tracing::error!("Aleo names cannot begin with an underscore");
return false;
}
if first.is_numeric() {
tracing::error!("Aleo names cannot begin with a number");
return false;
}
for current in name.chars() {
if !current.is_ascii_alphanumeric() && current != '_' {
tracing::error!("Aleo names must can only contain ASCII alphanumeric characters and underscores.");
return false;
}
}
true
}
pub fn can_initialize(package_name: &str, path: &Path) -> bool {
if !Self::is_aleo_name_valid(package_name) {
return false;
}
let mut result = true;
let mut existing_files = vec![];
if MainFile::exists_at(path) {
existing_files.push(MainFile::filename());
result = false;
}
if !existing_files.is_empty() {
tracing::error!("File(s) {:?} already exist", existing_files);
}
result
}
pub fn is_initialized(package_name: &str, path: &Path) -> bool {
if !Self::is_aleo_name_valid(package_name) {
return false;
}
if !MainFile::exists_at(path) {
return false;
}
true
}
pub fn initialize<N: Network>(package_name: &str, path: &Path, endpoint: String) -> Result<()> {
let path = path.join(package_name);
if path.exists() {
return Err(
PackageError::failed_to_initialize_package(package_name, &path, "Directory already exists").into()
);
}
std::fs::create_dir(&path).map_err(|e| PackageError::failed_to_initialize_package(package_name, &path, e))?;
std::env::set_current_dir(&path)
.map_err(|e| PackageError::failed_to_initialize_package(package_name, &path, e))?;
Gitignore::new().write_to(&path)?;
Env::<N>::new(Some(PrivateKey::<N>::from_str(TEST_PRIVATE_KEY)?), endpoint)?.write_to(&path)?;
let manifest = Manifest::default(package_name);
manifest.write_to_dir(&path)?;
SourceDirectory::create(&path)?;
MainFile::new(package_name).write_to(&path)?;
if !Self::is_initialized(package_name, &path) {
return Err(PackageError::failed_to_initialize_package(
package_name,
&path,
"Failed to correctly initialize package",
)
.into());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_package_name_valid() {
assert!(Package::is_aleo_name_valid("foo"));
assert!(Package::is_aleo_name_valid("foo_bar"));
assert!(Package::is_aleo_name_valid("foo1"));
assert!(Package::is_aleo_name_valid("foo_bar___baz_"));
assert!(!Package::is_aleo_name_valid("foo-bar"));
assert!(!Package::is_aleo_name_valid("foo-bar-baz"));
assert!(!Package::is_aleo_name_valid("foo-1"));
assert!(!Package::is_aleo_name_valid(""));
assert!(!Package::is_aleo_name_valid("-"));
assert!(!Package::is_aleo_name_valid("-foo"));
assert!(!Package::is_aleo_name_valid("-foo-"));
assert!(!Package::is_aleo_name_valid("_foo"));
assert!(!Package::is_aleo_name_valid("foo--bar"));
assert!(!Package::is_aleo_name_valid("foo---bar"));
assert!(!Package::is_aleo_name_valid("foo--bar--baz"));
assert!(!Package::is_aleo_name_valid("foo---bar---baz"));
assert!(!Package::is_aleo_name_valid("foo*bar"));
assert!(!Package::is_aleo_name_valid("foo,bar"));
assert!(!Package::is_aleo_name_valid("1-foo"));
}
}