diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d856e4..1fda5a91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Changed +- **aiken**: Add more flexibility around the management of Plutus blueprint files for `build`, `address`, `policy` and `apply` commands. See [#1055](https://github.com/aiken-lang/aiken/issues/1055). @KtorZ - **aiken**: Rename `--filter_traces` to `--trace_filter` for more consistency with `--trace_level`. An alias for `--filter_traces` still exists for backward compatibility. @KtorZ - **aiken-project**: Fix `aiken docs` wrongly formatting list constants as tuples. See [#1048](https://github.com/aiken-lang/aiken/issues/1048). @KtorZ - **aiken-project**: Fix `aiken docs` source linking crashing when generating docs for config modules. See [#1044](https://github.com/aiken-lang/aiken/issues/1044). @KtorZ diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index 0e1901bd..edb2c8b9 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -192,16 +192,25 @@ where self.defined_modules = checkpoint.defined_modules; } + pub fn blueprint_path(&self, filepath: Option<&Path>) -> PathBuf { + match filepath { + Some(filepath) => filepath.to_path_buf(), + None => self.root.join(Options::default().blueprint_path), + } + } + pub fn build( &mut self, uplc: bool, tracing: Tracing, + blueprint_path: PathBuf, env: Option, ) -> Result<(), Vec> { let options = Options { code_gen_mode: CodeGenMode::Build(uplc), tracing, env, + blueprint_path, }; self.compile(options) @@ -282,6 +291,7 @@ where property_max_success, } }, + blueprint_path: self.blueprint_path(None), }; self.compile(options) @@ -307,10 +317,6 @@ where Ok(()) } - pub fn blueprint_path(&self) -> PathBuf { - self.root.join("plutus.json") - } - fn config_definitions(&mut self, env: Option<&str>) -> Option> { if !self.config.config.is_empty() { let env = env.unwrap_or(ast::DEFAULT_ENV_MODULE); @@ -359,7 +365,7 @@ where CodeGenMode::Build(uplc_dump) => { self.event_listener .handle_event(Event::GeneratingBlueprint { - path: self.blueprint_path(), + path: options.blueprint_path.clone(), }); self.checked_modules.values_mut().for_each(|m| { @@ -381,10 +387,10 @@ where let json = serde_json::to_string_pretty(&blueprint).unwrap(); - fs::write(self.blueprint_path(), json).map_err(|error| { + fs::write(options.blueprint_path.as_path(), json).map_err(|error| { Error::FileIo { error, - path: self.blueprint_path(), + path: options.blueprint_path, } .into() }) @@ -444,6 +450,7 @@ where &self, title: Option<&String>, stake_address: Option<&String>, + blueprint_path: &Path, mainnet: bool, ) -> Result { // Parse stake address @@ -465,7 +472,7 @@ where }; // Read blueprint - let blueprint = File::open(self.blueprint_path()) + let blueprint = File::open(blueprint_path) .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; let blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint))?; @@ -502,9 +509,9 @@ where }) } - pub fn policy(&self, title: Option<&String>) -> Result { + pub fn policy(&self, title: Option<&String>, blueprint_path: &Path) -> Result { // Read blueprint - let blueprint = File::open(self.blueprint_path()) + let blueprint = File::open(blueprint_path) .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; let blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint))?; @@ -565,7 +572,7 @@ where pub fn construct_parameter_incrementally( &self, title: Option<&String>, - blueprint_input: &Option, + blueprint_path: &Path, ask: F, ) -> Result where @@ -575,11 +582,6 @@ where ) -> Result, { // Read blueprint - let project_blueprint_path = self.blueprint_path(); - let blueprint_path = blueprint_input - .as_ref() - .map(|p| p.as_path()) - .unwrap_or_else(|| &project_blueprint_path); let blueprint = File::open(blueprint_path) .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; let blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint))?; @@ -601,10 +603,11 @@ where pub fn apply_parameter( &self, title: Option<&String>, + blueprint_path: &Path, param: &PlutusData, ) -> Result { // Read blueprint - let blueprint = File::open(self.blueprint_path()) + let blueprint = File::open(blueprint_path) .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; let mut blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint))?; diff --git a/crates/aiken-project/src/options.rs b/crates/aiken-project/src/options.rs index c1551706..8e6a3019 100644 --- a/crates/aiken-project/src/options.rs +++ b/crates/aiken-project/src/options.rs @@ -1,9 +1,11 @@ use aiken_lang::ast::Tracing; +use std::path::PathBuf; pub struct Options { pub code_gen_mode: CodeGenMode, pub tracing: Tracing, pub env: Option, + pub blueprint_path: PathBuf, } impl Default for Options { @@ -12,6 +14,7 @@ impl Default for Options { code_gen_mode: CodeGenMode::NoOp, tracing: Tracing::silent(), env: None, + blueprint_path: PathBuf::from("plutus.json"), } } } diff --git a/crates/aiken/src/cmd/blueprint/address.rs b/crates/aiken/src/cmd/blueprint/address.rs index f29ead1d..4589e9fc 100644 --- a/crates/aiken/src/cmd/blueprint/address.rs +++ b/crates/aiken/src/cmd/blueprint/address.rs @@ -7,6 +7,18 @@ pub struct Args { /// Path to project directory: Option, + /// Optional path to the Plutus blueprint file to be used as input. + /// + /// [default: plutus.json] + #[clap( + short, + long = "in", + value_parser, + value_name = "FILEPATH", + verbatim_doc_comment + )] + input: Option, + /// Name of the validator's module within the project. Optional if there's only one validator #[clap(short, long)] module: Option, @@ -27,6 +39,7 @@ pub struct Args { pub fn exec( Args { directory, + input, module, validator, delegated_to, @@ -46,7 +59,12 @@ pub fn exec( let title = title.as_ref().or(validator.as_ref()); - let address = p.address(title, delegated_to.as_ref(), mainnet)?; + let address = p.address( + title, + delegated_to.as_ref(), + p.blueprint_path(input.as_deref()).as_path(), + mainnet, + )?; println!("{}", address.to_bech32().unwrap()); diff --git a/crates/aiken/src/cmd/blueprint/apply.rs b/crates/aiken/src/cmd/blueprint/apply.rs index 997f2ce0..335b4d8c 100644 --- a/crates/aiken/src/cmd/blueprint/apply.rs +++ b/crates/aiken/src/cmd/blueprint/apply.rs @@ -27,14 +27,22 @@ pub struct Args { #[clap(value_name = "CBOR")] parameter: Option, - /// Optional path to the blueprint file to be used as input. Default to 'plutus.json' when - /// omitted. - #[clap(short, long = "in", value_parser, value_name = "FILEPATH")] + /// Optional path to the blueprint file to be used as input. + /// + /// [default: plutus.json] + #[clap( + short, + long = "in", + value_parser, + value_name = "FILEPATH", + verbatim_doc_comment + )] input: Option, - /// Output file. Optional, print on stdout when omitted. - #[clap(short, long, value_name = "FILEPATH")] - out: Option, + /// Optional relative filepath to the generated Plutus blueprint. Default to printing to stdout + /// when omitted. + #[clap(short, long("out"), value_parser, value_name = "FILEPATH")] + output: Option, /// Name of the validator's module within the project. Optional if there's only one validator. #[clap(short, long)] @@ -49,7 +57,7 @@ pub fn exec( Args { parameter, input, - out, + output, module, validator, }: Args, @@ -74,6 +82,8 @@ pub fn exec( .if_supports_color(Stderr, |s| s.bold()), ); + let blueprint_input_path = p.blueprint_path(input.as_deref()); + let data: PlutusData = match ¶meter { Some(param) => { eprintln!( @@ -110,7 +120,9 @@ pub fn exec( }) } - None => p.construct_parameter_incrementally(title, &input, ask_schema)?, + None => { + p.construct_parameter_incrementally(title, &blueprint_input_path, ask_schema)? + } }; eprintln!( @@ -124,19 +136,22 @@ pub fn exec( } ); - let blueprint = p.apply_parameter(title, &data)?; + let blueprint = p.apply_parameter(title, &blueprint_input_path, &data)?; let json = serde_json::to_string_pretty(&blueprint).unwrap(); - match out { + match output { None => { println!("\n{}\n", json); Ok(()) } - Some(ref path) => fs::write(path, json).map_err(|error| Error::FileIo { - error, - path: p.blueprint_path(), - }), + Some(ref path) => { + let blueprint_output_path = p.blueprint_path(Some(path)); + fs::write(&blueprint_output_path, json).map_err(|error| Error::FileIo { + error, + path: blueprint_output_path, + }) + } }?; eprintln!( diff --git a/crates/aiken/src/cmd/blueprint/hash.rs b/crates/aiken/src/cmd/blueprint/hash.rs index f5410584..6b84c530 100644 --- a/crates/aiken/src/cmd/blueprint/hash.rs +++ b/crates/aiken/src/cmd/blueprint/hash.rs @@ -7,6 +7,18 @@ pub struct Args { /// Path to project directory: Option, + /// Optional path to the blueprint file to be used as input. + /// + /// [default: plutus.json] + #[clap( + short, + long = "in", + value_parser, + value_name = "FILEPATH", + verbatim_doc_comment + )] + input: Option, + /// Name of the validator's module within the project. Optional if there's only one validator #[clap(short, long)] module: Option, @@ -19,6 +31,7 @@ pub struct Args { pub fn exec( Args { directory, + input, module, validator, }: Args, @@ -36,7 +49,12 @@ pub fn exec( let title = title.as_ref().or(validator.as_ref()); - let address = p.address(title, None, false)?; + let address = p.address( + title, + None, + p.blueprint_path(input.as_deref()).as_path(), + false, + )?; println!("{}", address.payment().to_hex()); diff --git a/crates/aiken/src/cmd/blueprint/policy.rs b/crates/aiken/src/cmd/blueprint/policy.rs index 080926d5..1be8faf4 100644 --- a/crates/aiken/src/cmd/blueprint/policy.rs +++ b/crates/aiken/src/cmd/blueprint/policy.rs @@ -7,6 +7,18 @@ pub struct Args { /// Path to project directory: Option, + /// Optional path to the blueprint file to be used as input. + /// + /// [default: plutus.json] + #[clap( + short, + long = "in", + value_parser, + value_name = "FILEPATH", + verbatim_doc_comment + )] + input: Option, + /// Name of the validator's module within the project. Optional if there's only one validator #[clap(short, long)] module: Option, @@ -19,6 +31,7 @@ pub struct Args { pub fn exec( Args { directory, + input, module, validator, }: Args, @@ -36,7 +49,7 @@ pub fn exec( let title = title.as_ref().or(validator.as_ref()); - let policy = p.policy(title)?; + let policy = p.policy(title, p.blueprint_path(input.as_deref()).as_path())?; println!("{}", policy); diff --git a/crates/aiken/src/cmd/build.rs b/crates/aiken/src/cmd/build.rs index 7774975b..eb64a16b 100644 --- a/crates/aiken/src/cmd/build.rs +++ b/crates/aiken/src/cmd/build.rs @@ -25,6 +25,18 @@ pub struct Args { #[clap(long)] env: Option, + /// Optional relative filepath to the generated Plutus blueprint. + /// + /// [default: plutus.json] + #[clap( + short, + long("out"), + value_parser, + value_name = "FILEPATH", + verbatim_doc_comment + )] + output: Option, + /// Filter traces to be included in the generated program(s). /// /// - user-defined: @@ -67,6 +79,7 @@ pub fn exec( uplc, trace_filter, trace_level, + output, env, }: Args, ) -> miette::Result<()> { @@ -78,6 +91,7 @@ pub fn exec( Some(trace_filter) => trace_filter(trace_level), None => Tracing::All(trace_level), }, + p.blueprint_path(output.as_deref()), env.clone(), ) }) @@ -89,6 +103,7 @@ pub fn exec( Some(trace_filter) => trace_filter(trace_level), None => Tracing::All(trace_level), }, + p.blueprint_path(output.as_deref()), env.clone(), ) })