Skip to content

Commit 6eacad4

Browse files
cli: Add program template with multiple files (solana-foundation#2602)
1 parent 454f1dd commit 6eacad4

File tree

4 files changed

+263
-105
lines changed

4 files changed

+263
-105
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ The minor version will be incremented upon a breaking change and the patch versi
2020
- 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)).
2121
- spl: Export `mpl-token-metadata` crate ([#2583](https://github.com/coral-xyz/anchor/pull/2583)).
2222
- spl: Add `TokenRecordAccount` for pNFTs ([#2597](https://github.com/coral-xyz/anchor/pull/2597)).
23-
- ts: Add support for unnamed(tuple) enum in accounts([#2601](https://github.com/coral-xyz/anchor/pull/2601)).
23+
- ts: Add support for unnamed(tuple) enum in accounts ([#2601](https://github.com/coral-xyz/anchor/pull/2601)).
24+
- cli: Add program template with multiple files for instructions, state... ([#2602](https://github.com/coral-xyz/anchor/pull/2602)).
2425

2526
### Fixes
2627

cli/src/lib.rs

+69-33
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use heck::{ToKebabCase, ToSnakeCase};
2020
use regex::{Regex, RegexBuilder};
2121
use reqwest::blocking::multipart::{Form, Part};
2222
use reqwest::blocking::Client;
23+
use rust_template::ProgramTemplate;
2324
use semver::{Version, VersionReq};
2425
use serde::{Deserialize, Serialize};
2526
use serde_json::{json, Map, Value as JsonValue};
@@ -69,15 +70,23 @@ pub struct Opts {
6970
pub enum Command {
7071
/// Initializes a workspace.
7172
Init {
73+
/// Workspace name
7274
name: String,
75+
/// Use JavaScript instead of TypeScript
7376
#[clap(short, long)]
7477
javascript: bool,
78+
/// Use Solidity instead of Rust
7579
#[clap(short, long)]
7680
solidity: bool,
81+
/// Don't initialize git
7782
#[clap(long)]
7883
no_git: bool,
84+
/// Use `jest` instead of `mocha` for tests
7985
#[clap(long)]
8086
jest: bool,
87+
/// Rust program template to use
88+
#[clap(value_enum, short, long, default_value = "single")]
89+
template: ProgramTemplate,
8190
},
8291
/// Builds the workspace.
8392
#[clap(name = "build", alias = "b")]
@@ -207,9 +216,14 @@ pub enum Command {
207216
},
208217
/// Creates a new program.
209218
New {
219+
/// Program name
220+
name: String,
221+
/// Use Solidity instead of Rust
210222
#[clap(short, long)]
211223
solidity: bool,
212-
name: String,
224+
/// Rust program template to use
225+
#[clap(value_enum, short, long, default_value = "single")]
226+
template: ProgramTemplate,
213227
},
214228
/// Commands for interacting with interface definitions.
215229
Idl {
@@ -456,8 +470,21 @@ pub fn entry(opts: Opts) -> Result<()> {
456470
solidity,
457471
no_git,
458472
jest,
459-
} => init(&opts.cfg_override, name, javascript, solidity, no_git, jest),
460-
Command::New { solidity, name } => new(&opts.cfg_override, solidity, name),
473+
template,
474+
} => init(
475+
&opts.cfg_override,
476+
name,
477+
javascript,
478+
solidity,
479+
no_git,
480+
jest,
481+
template,
482+
),
483+
Command::New {
484+
solidity,
485+
name,
486+
template,
487+
} => new(&opts.cfg_override, solidity, name, template),
461488
Command::Build {
462489
idl,
463490
idl_ts,
@@ -604,6 +631,7 @@ fn init(
604631
solidity: bool,
605632
no_git: bool,
606633
jest: bool,
634+
template: ProgramTemplate,
607635
) -> Result<()> {
608636
if Config::discover(cfg_override)?.is_some() {
609637
return Err(anyhow!("Workspace already initialized"));
@@ -682,17 +710,11 @@ fn init(
682710

683711
// Build the program.
684712
if solidity {
685-
fs::create_dir("solidity")?;
686-
687-
new_solidity_program(&project_name)?;
713+
solidity_template::create_program(&project_name)?;
688714
} else {
689-
// Build virtual manifest for rust programs
690-
fs::write("Cargo.toml", rust_template::virtual_manifest())?;
691-
692-
fs::create_dir("programs")?;
693-
694-
new_rust_program(&project_name)?;
715+
rust_template::create_program(&project_name, template)?;
695716
}
717+
696718
// Build the test suite.
697719
fs::create_dir("tests")?;
698720
// Build the migrations directory.
@@ -783,7 +805,12 @@ fn install_node_modules(cmd: &str) -> Result<std::process::Output> {
783805
}
784806

785807
// Creates a new program crate in the `programs/<name>` directory.
786-
fn new(cfg_override: &ConfigOverride, solidity: bool, name: String) -> Result<()> {
808+
fn new(
809+
cfg_override: &ConfigOverride,
810+
solidity: bool,
811+
name: String,
812+
template: ProgramTemplate,
813+
) -> Result<()> {
787814
with_workspace(cfg_override, |cfg| {
788815
match cfg.path().parent() {
789816
None => {
@@ -802,10 +829,10 @@ fn new(cfg_override: &ConfigOverride, solidity: bool, name: String) -> Result<()
802829
name.clone(),
803830
ProgramDeployment {
804831
address: if solidity {
805-
new_solidity_program(&name)?;
832+
solidity_template::create_program(&name)?;
806833
solidity_template::default_program_id()
807834
} else {
808-
new_rust_program(&name)?;
835+
rust_template::create_program(&name, template)?;
809836
rust_template::get_or_create_program_id(&name)
810837
},
811838
path: None,
@@ -823,26 +850,32 @@ fn new(cfg_override: &ConfigOverride, solidity: bool, name: String) -> Result<()
823850
})
824851
}
825852

826-
// Creates a new rust program crate in the current directory with `name`.
827-
fn new_rust_program(name: &str) -> Result<()> {
828-
if !PathBuf::from("Cargo.toml").exists() {
829-
fs::write("Cargo.toml", rust_template::virtual_manifest())?;
853+
/// Array of (path, content) tuple.
854+
pub type Files = Vec<(PathBuf, String)>;
855+
856+
/// Create files from the given (path, content) tuple array.
857+
///
858+
/// # Example
859+
///
860+
/// ```ignore
861+
/// crate_files(vec![("programs/my_program/src/lib.rs".into(), "// Content".into())])?;
862+
/// ```
863+
pub fn create_files(files: &Files) -> Result<()> {
864+
for (path, content) in files {
865+
let path = Path::new(path);
866+
if path.exists() {
867+
continue;
868+
}
869+
870+
match path.extension() {
871+
Some(_) => {
872+
fs::create_dir_all(path.parent().unwrap())?;
873+
fs::write(path, content)?;
874+
}
875+
None => fs::create_dir_all(path)?,
876+
}
830877
}
831-
fs::create_dir_all(format!("programs/{name}/src/"))?;
832-
let mut cargo_toml = File::create(format!("programs/{name}/Cargo.toml"))?;
833-
cargo_toml.write_all(rust_template::cargo_toml(name).as_bytes())?;
834-
let mut xargo_toml = File::create(format!("programs/{name}/Xargo.toml"))?;
835-
xargo_toml.write_all(rust_template::xargo_toml().as_bytes())?;
836-
let mut lib_rs = File::create(format!("programs/{name}/src/lib.rs"))?;
837-
lib_rs.write_all(rust_template::lib_rs(name).as_bytes())?;
838-
Ok(())
839-
}
840878

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

@@ -4224,6 +4257,7 @@ mod tests {
42244257
false,
42254258
false,
42264259
false,
4260+
ProgramTemplate::default(),
42274261
)
42284262
.unwrap();
42294263
}
@@ -4241,6 +4275,7 @@ mod tests {
42414275
false,
42424276
false,
42434277
false,
4278+
ProgramTemplate::default(),
42444279
)
42454280
.unwrap();
42464281
}
@@ -4258,6 +4293,7 @@ mod tests {
42584293
false,
42594294
false,
42604295
false,
4296+
ProgramTemplate::default(),
42614297
)
42624298
.unwrap();
42634299
}

0 commit comments

Comments
 (0)