pub mod snippets;
use crate::codegen::snippets::{
CARGO_TOML_PLAIN, CARGO_TOML_PLAIN_V4, CARGO_TOML_PLAIN_V5, CARGO_TOML_SNIPPET,
CARGO_TOML_SNIPPET_V4, CARGO_TOML_SNIPPET_V5, CONTRACT_PLAIN, CONTRACT_PLAIN_V4,
CONTRACT_PLAIN_V5, CONTRACT_SNIPPET, CONTRACT_SNIPPET_V4, CONTRACT_SNIPPET_V5,
};
use crate::{utils, Version};
#[derive(Debug, PartialEq, Eq)]
pub struct Project {
pub lib: ProjectFile,
pub cargo: ProjectFile,
}
#[derive(Debug, PartialEq, Eq)]
pub struct ProjectFile {
pub plain: String,
pub snippet: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
PackageName,
ContractName,
}
pub fn new_project(name: String, version: Version) -> Result<Project, Error> {
if name.is_empty()
|| !name
.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '-')
{
return Err(Error::PackageName);
}
if !name.chars().next().is_some_and(char::is_alphabetic) {
return Err(Error::ContractName);
}
let module_name = name.replace('-', "_");
let struct_name = utils::pascal_case(&module_name);
Ok(Project {
lib: ProjectFile {
plain: if version.is_legacy() {
CONTRACT_PLAIN_V4
} else if version.is_v5() {
CONTRACT_PLAIN_V5
} else {
CONTRACT_PLAIN
}
.replace("my_contract", &module_name)
.replace("MyContract", &struct_name),
snippet: Some(
if version.is_legacy() {
CONTRACT_SNIPPET_V4
} else if version.is_v5() {
CONTRACT_SNIPPET_V5
} else {
CONTRACT_SNIPPET
}
.replace("my_contract", &module_name)
.replace("MyContract", &struct_name),
),
},
cargo: ProjectFile {
plain: if version.is_legacy() {
CARGO_TOML_PLAIN_V4
} else if version.is_v5() {
CARGO_TOML_PLAIN_V5
} else {
CARGO_TOML_PLAIN
}
.replace("my_contract", &name),
snippet: Some(
if version.is_legacy() {
CARGO_TOML_SNIPPET_V4
} else if version.is_v5() {
CARGO_TOML_SNIPPET_V5
} else {
CARGO_TOML_SNIPPET
}
.replace("my_contract", &name),
),
},
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Analysis, MinorVersion};
#[test]
fn invalid_project_name_fails() {
for (name, expected_error) in [
("", Error::PackageName),
("hello!", Error::PackageName),
("hello world", Error::PackageName),
("💝", Error::PackageName),
("1hello", Error::ContractName),
("-hello", Error::ContractName),
("_hello", Error::ContractName),
] {
assert_eq!(
new_project(name.to_owned(), Version::Legacy),
Err(expected_error)
);
}
}
#[test]
fn valid_project_name_works() {
for name in ["hello", "hello_world", "hello-world"] {
let result = new_project(name.to_owned(), Version::Legacy);
assert!(result.is_ok());
let contract_code = result.unwrap().lib.plain;
let analysis = Analysis::new(&contract_code, Version::Legacy);
assert_eq!(analysis.diagnostics().len(), 0);
}
}
#[test]
fn new_project_works() {
for version in [
Version::Legacy,
Version::V5(MinorVersion::Latest),
Version::V6,
] {
let result = new_project("hello_world".to_owned(), version);
assert!(result.is_ok());
let project = result.unwrap();
let cargo_toml = project.cargo.plain;
assert!(cargo_toml.contains(if version.is_legacy() {
r#"ink = { version = "4"#
} else if version.is_v5() {
r#"ink = { version = "5"#
} else {
r#"version = "6"#
}));
let contract_code = project.lib.plain;
let analysis = Analysis::new(&contract_code, version);
assert_eq!(analysis.diagnostics().len(), 0);
}
}
}