Skip to content

Commit

Permalink
cli: Add program template with multiple files (#2602)
Browse files Browse the repository at this point in the history
  • Loading branch information
acheroncrypto authored Aug 15, 2023
1 parent 454f1dd commit 6eacad4
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 105 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ The minor version will be incremented upon a breaking change and the patch versi
- ts: Add ability to access workspace programs independent of the casing used, e.g. `anchor.workspace.myProgram`, `anchor.workspace.MyProgram`... ([#2579](https://github.com/coral-xyz/anchor/pull/2579)).
- spl: Export `mpl-token-metadata` crate ([#2583](https://github.com/coral-xyz/anchor/pull/2583)).
- spl: Add `TokenRecordAccount` for pNFTs ([#2597](https://github.com/coral-xyz/anchor/pull/2597)).
- ts: Add support for unnamed(tuple) enum in accounts([#2601](https://github.com/coral-xyz/anchor/pull/2601)).
- ts: Add support for unnamed(tuple) enum in accounts ([#2601](https://github.com/coral-xyz/anchor/pull/2601)).
- cli: Add program template with multiple files for instructions, state... ([#2602](https://github.com/coral-xyz/anchor/pull/2602)).

### Fixes

Expand Down
102 changes: 69 additions & 33 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use heck::{ToKebabCase, ToSnakeCase};
use regex::{Regex, RegexBuilder};
use reqwest::blocking::multipart::{Form, Part};
use reqwest::blocking::Client;
use rust_template::ProgramTemplate;
use semver::{Version, VersionReq};
use serde::{Deserialize, Serialize};
use serde_json::{json, Map, Value as JsonValue};
Expand Down Expand Up @@ -69,15 +70,23 @@ pub struct Opts {
pub enum Command {
/// Initializes a workspace.
Init {
/// Workspace name
name: String,
/// Use JavaScript instead of TypeScript
#[clap(short, long)]
javascript: bool,
/// Use Solidity instead of Rust
#[clap(short, long)]
solidity: bool,
/// Don't initialize git
#[clap(long)]
no_git: bool,
/// Use `jest` instead of `mocha` for tests
#[clap(long)]
jest: bool,
/// Rust program template to use
#[clap(value_enum, short, long, default_value = "single")]
template: ProgramTemplate,
},
/// Builds the workspace.
#[clap(name = "build", alias = "b")]
Expand Down Expand Up @@ -207,9 +216,14 @@ pub enum Command {
},
/// Creates a new program.
New {
/// Program name
name: String,
/// Use Solidity instead of Rust
#[clap(short, long)]
solidity: bool,
name: String,
/// Rust program template to use
#[clap(value_enum, short, long, default_value = "single")]
template: ProgramTemplate,
},
/// Commands for interacting with interface definitions.
Idl {
Expand Down Expand Up @@ -456,8 +470,21 @@ pub fn entry(opts: Opts) -> Result<()> {
solidity,
no_git,
jest,
} => init(&opts.cfg_override, name, javascript, solidity, no_git, jest),
Command::New { solidity, name } => new(&opts.cfg_override, solidity, name),
template,
} => init(
&opts.cfg_override,
name,
javascript,
solidity,
no_git,
jest,
template,
),
Command::New {
solidity,
name,
template,
} => new(&opts.cfg_override, solidity, name, template),
Command::Build {
idl,
idl_ts,
Expand Down Expand Up @@ -604,6 +631,7 @@ fn init(
solidity: bool,
no_git: bool,
jest: bool,
template: ProgramTemplate,
) -> Result<()> {
if Config::discover(cfg_override)?.is_some() {
return Err(anyhow!("Workspace already initialized"));
Expand Down Expand Up @@ -682,17 +710,11 @@ fn init(

// Build the program.
if solidity {
fs::create_dir("solidity")?;

new_solidity_program(&project_name)?;
solidity_template::create_program(&project_name)?;
} else {
// Build virtual manifest for rust programs
fs::write("Cargo.toml", rust_template::virtual_manifest())?;

fs::create_dir("programs")?;

new_rust_program(&project_name)?;
rust_template::create_program(&project_name, template)?;
}

// Build the test suite.
fs::create_dir("tests")?;
// Build the migrations directory.
Expand Down Expand Up @@ -783,7 +805,12 @@ fn install_node_modules(cmd: &str) -> Result<std::process::Output> {
}

// Creates a new program crate in the `programs/<name>` directory.
fn new(cfg_override: &ConfigOverride, solidity: bool, name: String) -> Result<()> {
fn new(
cfg_override: &ConfigOverride,
solidity: bool,
name: String,
template: ProgramTemplate,
) -> Result<()> {
with_workspace(cfg_override, |cfg| {
match cfg.path().parent() {
None => {
Expand All @@ -802,10 +829,10 @@ fn new(cfg_override: &ConfigOverride, solidity: bool, name: String) -> Result<()
name.clone(),
ProgramDeployment {
address: if solidity {
new_solidity_program(&name)?;
solidity_template::create_program(&name)?;
solidity_template::default_program_id()
} else {
new_rust_program(&name)?;
rust_template::create_program(&name, template)?;
rust_template::get_or_create_program_id(&name)
},
path: None,
Expand All @@ -823,26 +850,32 @@ fn new(cfg_override: &ConfigOverride, solidity: bool, name: String) -> Result<()
})
}

// Creates a new rust program crate in the current directory with `name`.
fn new_rust_program(name: &str) -> Result<()> {
if !PathBuf::from("Cargo.toml").exists() {
fs::write("Cargo.toml", rust_template::virtual_manifest())?;
/// Array of (path, content) tuple.
pub type Files = Vec<(PathBuf, String)>;

/// Create files from the given (path, content) tuple array.
///
/// # Example
///
/// ```ignore
/// crate_files(vec![("programs/my_program/src/lib.rs".into(), "// Content".into())])?;
/// ```
pub fn create_files(files: &Files) -> Result<()> {
for (path, content) in files {
let path = Path::new(path);
if path.exists() {
continue;
}

match path.extension() {
Some(_) => {
fs::create_dir_all(path.parent().unwrap())?;
fs::write(path, content)?;
}
None => fs::create_dir_all(path)?,
}
}
fs::create_dir_all(format!("programs/{name}/src/"))?;
let mut cargo_toml = File::create(format!("programs/{name}/Cargo.toml"))?;
cargo_toml.write_all(rust_template::cargo_toml(name).as_bytes())?;
let mut xargo_toml = File::create(format!("programs/{name}/Xargo.toml"))?;
xargo_toml.write_all(rust_template::xargo_toml().as_bytes())?;
let mut lib_rs = File::create(format!("programs/{name}/src/lib.rs"))?;
lib_rs.write_all(rust_template::lib_rs(name).as_bytes())?;
Ok(())
}

// Creates a new solidity program in the current directory with `name`.
fn new_solidity_program(name: &str) -> Result<()> {
fs::create_dir_all("solidity")?;
let mut lib_rs = File::create(format!("solidity/{name}.sol"))?;
lib_rs.write_all(solidity_template::solidity(name).as_bytes())?;
Ok(())
}

Expand Down Expand Up @@ -4224,6 +4257,7 @@ mod tests {
false,
false,
false,
ProgramTemplate::default(),
)
.unwrap();
}
Expand All @@ -4241,6 +4275,7 @@ mod tests {
false,
false,
false,
ProgramTemplate::default(),
)
.unwrap();
}
Expand All @@ -4258,6 +4293,7 @@ mod tests {
false,
false,
false,
ProgramTemplate::default(),
)
.unwrap();
}
Expand Down
Loading

1 comment on commit 6eacad4

@vercel
Copy link

@vercel vercel bot commented on 6eacad4 Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

anchor-docs – ./

anchor-docs-git-master-200ms.vercel.app
anchor-docs-200ms.vercel.app
www.anchor-lang.com
anchor-lang.com

Please sign in to comment.