diff --git a/near-sdk-macros/src/core_impl/contract_metadata/build_info.rs b/near-sdk-macros/src/core_impl/contract_metadata/build_info.rs new file mode 100644 index 000000000..33f0b2176 --- /dev/null +++ b/near-sdk-macros/src/core_impl/contract_metadata/build_info.rs @@ -0,0 +1,46 @@ +#[derive(serde::Serialize)] +pub(crate) struct BuildInfo { + build_environment: String, + build_command: Vec, + contract_path: String, + source_code_snapshot: String, +} + +const ERR_EMPTY_BUILD_ENVIRONMENT: &str = "`NEP330_BUILD_INFO_BUILD_ENVIRONMENT` is set, \ + but it's set to empty string!"; +const ERR_EMPTY_BUILD_COMMAND: &str = "`NEP330_BUILD_INFO_BUILD_COMMAND` is required, \ + when `NEP330_BUILD_INFO_BUILD_ENVIRONMENT` is set, \ + but it's either not set or empty!"; +const ERR_PARSE_BUILD_COMMAND: &str = "problem parsing `NEP330_BUILD_INFO_BUILD_COMMAND` value"; + +const ERR_UNSET_CONTRACT_PATH: &str = "`NEP330_BUILD_INFO_CONTRACT_PATH` was provided, \ + but it's not set!"; + +const ERR_UNSET_OR_EMPTY_SOURCE_SNAPSHOT: &str = "`NEP330_BUILD_INFO_SOURCE_CODE_SNAPSHOT` is \ + required, when `NEP330_BUILD_INFO_BUILD_ENVIRONMENT` \ + is set, but it's either not set or empty!"; + +impl BuildInfo { + pub(super) fn from_env() -> Result { + let build_environment = std::env::var("NEP330_BUILD_INFO_BUILD_ENVIRONMENT") + .ok() + .filter(|build_environment| !build_environment.is_empty()) + .ok_or(ERR_EMPTY_BUILD_ENVIRONMENT.to_string())?; + + let build_command = std::env::var("NEP330_BUILD_INFO_BUILD_COMMAND") + .ok() + .filter(|build_command| !build_command.is_empty()) + .ok_or(ERR_EMPTY_BUILD_COMMAND.to_string())?; + let build_command: Vec = serde_json::from_str(&build_command) + .map_err(|err| format!("{}: {}", ERR_PARSE_BUILD_COMMAND, err))?; + + let source_code_snapshot = std::env::var("NEP330_BUILD_INFO_SOURCE_CODE_SNAPSHOT") + .ok() + .filter(|source_code_snapshot| !source_code_snapshot.is_empty()) + .ok_or(ERR_UNSET_OR_EMPTY_SOURCE_SNAPSHOT.to_string())?; + let contract_path = std::env::var("NEP330_BUILD_INFO_CONTRACT_PATH") + .map_err(|_| ERR_UNSET_CONTRACT_PATH.to_string())?; + + Ok(Self { build_environment, build_command, contract_path, source_code_snapshot }) + } +} diff --git a/near-sdk-macros/src/core_impl/contract_metadata/mod.rs b/near-sdk-macros/src/core_impl/contract_metadata/mod.rs index 75a183133..b2d51588e 100644 --- a/near-sdk-macros/src/core_impl/contract_metadata/mod.rs +++ b/near-sdk-macros/src/core_impl/contract_metadata/mod.rs @@ -1,7 +1,11 @@ +#![allow(clippy::manual_unwrap_or_default)] + use darling::{ast::NestedMeta, Error, FromMeta}; use proc_macro2::TokenStream; use quote::quote; +mod build_info; + #[derive(FromMeta)] struct MacroConfig { contract_metadata: Option, @@ -11,8 +15,12 @@ struct MacroConfig { pub(crate) struct ContractMetadata { version: Option, link: Option, + #[darling(multiple, rename = "standard")] standards: Vec, + + #[darling(skip)] + build_info: Option, } impl quote::ToTokens for ContractMetadata { @@ -47,17 +55,16 @@ struct Standard { impl ContractMetadata { fn populate(mut self) -> Self { - if self.version.is_none() { - let version = std::env::var("CARGO_PKG_VERSION").unwrap_or(String::from("")); - if !version.is_empty() { - self.version = Some(version); + if self.link.is_none() { + let field_val = std::env::var("NEP330_LINK").unwrap_or(String::from("")); + if !field_val.is_empty() { + self.link = Some(field_val); } } - - if self.link.is_none() { - let repo = std::env::var("CARGO_PKG_REPOSITORY").unwrap_or(String::from("")); - if !repo.is_empty() { - self.link = Some(repo); + if self.version.is_none() { + let field_val = std::env::var("NEP330_VERSION").unwrap_or(String::from("")); + if !field_val.is_empty() { + self.version = Some(field_val); } } @@ -66,7 +73,14 @@ impl ContractMetadata { || self.standards.iter().all(|s| s.standard.to_ascii_lowercase() != "nep330") { self.standards - .push(Standard { standard: "nep330".to_string(), version: "1.1.0".to_string() }); + .push(Standard { standard: "nep330".to_string(), version: "1.2.0".to_string() }); + } + + if std::env::var("NEP330_BUILD_INFO_BUILD_ENVIRONMENT").is_ok() { + self.build_info = Some( + build_info::BuildInfo::from_env() + .expect("Build Details Extension field not provided or malformed"), + ); } self