Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impr docs #1262

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7d18cd0
tmp
PolyProgrammist Nov 19, 2024
3803218
tmp
PolyProgrammist Nov 20, 2024
d1015f0
nearschema
PolyProgrammist Nov 20, 2024
245c1c9
link for store crate, neargas and neartoken types
PolyProgrammist Nov 20, 2024
34d774f
promises in env
PolyProgrammist Nov 20, 2024
5f0aeb1
fmt and env intro
PolyProgrammist Nov 20, 2024
ed0bdef
promise_index and promise structs
PolyProgrammist Nov 20, 2024
589d61c
storage and functionerror
PolyProgrammist Nov 20, 2024
1911c50
small
PolyProgrammist Nov 20, 2024
b6951e7
denbite review
PolyProgrammist Dec 20, 2024
3b1ce54
Merge branch 'master' into impr-docs
PolyProgrammist Dec 20, 2024
feaa181
comments from PR 1259
PolyProgrammist Dec 20, 2024
8d178bd
fmt
PolyProgrammist Dec 20, 2024
aef8c14
clippy
PolyProgrammist Dec 20, 2024
49bc57b
fmt
PolyProgrammist Dec 20, 2024
f29575c
docs test fix
PolyProgrammist Dec 20, 2024
48ccae6
fix doc
PolyProgrammist Dec 20, 2024
4d92375
fix docs
PolyProgrammist Dec 20, 2024
63b8b0a
fix docs
PolyProgrammist Dec 20, 2024
d46cd16
fix
PolyProgrammist Dec 20, 2024
9bbd19a
fix
PolyProgrammist Dec 20, 2024
08dd4ff
Merge branch 'master' into impr-docs
PolyProgrammist Dec 20, 2024
99c7e13
fix ci test
PolyProgrammist Dec 26, 2024
e1c6c57
Merge branch 'master' into impr-docs
PolyProgrammist Dec 26, 2024
22e4098
fmt
PolyProgrammist Dec 26, 2024
c63918d
ignore to no_run
PolyProgrammist Dec 27, 2024
c2732a2
remove unnecessary near-primitives and near-vm-runner for docs
PolyProgrammist Dec 27, 2024
f0c05da
fix no-run tests
PolyProgrammist Dec 28, 2024
8e16e83
fmt
PolyProgrammist Dec 28, 2024
916e81d
fix tests
PolyProgrammist Dec 28, 2024
3d5cb4e
fmt
PolyProgrammist Dec 28, 2024
bc34ace
fix tests
PolyProgrammist Dec 29, 2024
b1b185f
checkj
PolyProgrammist Dec 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 172 additions & 120 deletions near-sdk-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,57 +43,140 @@ struct NearMacroArgs {
inside_nearsdk: Option<bool>,
}

/// This attribute macro is used to enhance the near_bindgen macro.
/// It is used to add Borsh and Serde derives for serialization and deserialization.
/// It also adds `BorshSchema` and `JsonSchema` if needed
///
/// If you would like to add Borsh or Serde serialization and deserialization to your contract,
/// you can use the abi attribute and pass in the serializers you would like to use.
/// This attribute macro is used on a struct and its implementations
/// to generate the necessary code to expose `pub` methods from the contract as well
/// as generating the glue code to be a valid NEAR contract.
///
/// # Example
/// ## Example
///
/// ```ignore
Copy link
Collaborator

Choose a reason for hiding this comment

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

rust tag

Copy link
Collaborator

Choose a reason for hiding this comment

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

this is probably not possible as is,
doing

/// ```rust
/// use near_sdk_macros::near;
/// 
/// #[near(serializers=[borsh, json])]
/// struct MyStruct {
///    pub name: String,
/// }
/// ```

results in

---- near-sdk-macros/src/lib.rs - near (line 55) stdout ----
error[E0433]: failed to resolve: could not find `near_sdk` in the list of imported crates
 --> near-sdk-macros/src/lib.rs:59:1
  |
7 | #[near(serializers=[borsh, json])]
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not find `near_sdk` in the list of imported crates
  |
  = note: this error originates in the attribute macro `near` (in Nightly builds, run with -Z macro-backtrace for more info)

error: cannot find attribute `borsh` in this scope
 --> near-sdk-macros/src/lib.rs:59:1
  |
7 | #[near(serializers=[borsh, json])]
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: this error originates in the attribute macro `near` (in Nightly builds, run with -Z macro-backtrace for more info)

error: cannot find attribute `serde` in this scope
 --> near-sdk-macros/src/lib.rs:59:1
  |
7 | #[near(serializers=[borsh, json])]
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `serde` is in scope, but it is a crate, not an attribute
  = note: this error originates in the attribute macro `near` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 3 previous errors

this is why #1288 was suggested

Copy link
Collaborator

@dj8yfo dj8yfo Jan 15, 2025

Choose a reason for hiding this comment

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

alternatively to #1288, a circular [dev-dependency] appears to allow this too:

diff --git a/near-sdk-macros/Cargo.toml b/near-sdk-macros/Cargo.toml
index 1f0388d4..31974b8d 100644
--- a/near-sdk-macros/Cargo.toml
+++ b/near-sdk-macros/Cargo.toml
@@ -21,19 +21,20 @@ strum = { version = "0.26", default-features = false }
 strum_macros = "0.26"
 quote = { version = "1.0", default-features = false }
 Inflector = { version = "0.11.4", default-features = false, features = [] }
 darling = { version = "0.20.3", default-features = false }
 serde = { version = "1", default-features = false, features = ["serde_derive"] }
 serde_json = "1"
 
 [dev-dependencies]
 insta = { version = "1.31.0", features = ["yaml"] }
 prettyplease = { version = "0.2.15" }
+near-sdk = { path = "../near-sdk" }
 
 
 [features]
 abi = []
 __abi-embed = ["abi"]
 __abi-generate = ["abi"]
 
 [package.metadata.docs.rs]
 features = ["__abi-generate"]

not sure if this entails additional problems, but this looks a one-liner compared to moving larger blocks of code.
And near-sdk kind of re-exports all of its dependencies it uses for derive macros (does it?)
Please leave a comment on #1288, if you decide to try a circular [dev-dependency] instead

Copy link
Collaborator

Choose a reason for hiding this comment

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

To be honest, I agree that it's better to document on near sdk level. I doubt that a lot of people will go in the macro crate at all...

Copy link
Collaborator

@dj8yfo dj8yfo Jan 15, 2025

Choose a reason for hiding this comment

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

the doc will be re-exported from near-sdk-macros anyway, as it is now

image

Another example/explanation: doc at https://docs.rs/borsh/1.5.4/borsh/derive.BorshSerialize.html gets concatenated of 2 pieces: one at doc-comment at site of re-export, one at original doc-comment site:
image

the only subtlety i see , is that if one does

[dev-dependencies]
near-sdk = { path = "../near-sdk" , features = "__abi-generate" }

then near-sdk/__abi-generate => near-sdk-macros/__abi-generate feature becomes implicitly enabled
when anyone runs cargo test for near-sdk-macros crate (which wouldn't be otherwise enabled) , but i think it's kind of ok here.
so it looks like trying a one-liner is simpler, than moving a few blocks of code.
Anyway, @akorchyn should make a final call for the pr here, as he owns the review here

(i'm pro #1288 approach , doc at reexport in near-sdk should describe how to use the macros,
doc in near-sdk-macros of various non-public items might explain how the macros work internally, if someone wants to understand/extend them, the latter is only possible when documenting private items is enabled for proc-macro crates)

Copy link
Collaborator

Choose a reason for hiding this comment

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

I like the #1288 approach more, to be honest.

/// #[near(serializers=[borsh, json])]
/// struct MyStruct {
/// pub name: String,
/// }
/// ```
/// effectively becomes:
///
/// This macro will generate code to load and deserialize state if the `self` parameter is included
/// as well as saving it back to state if `&mut self` is used.
///
/// # Parameter and result serialization
/// If the macro is used with Impl section, for parameter serialization, this macro will generate a struct with all of the parameters as
/// fields and derive deserialization for it. By default this will be JSON deserialized with `serde`
/// but can be overwritten by using `#[serializer(borsh)]`:
/// ```ignore
Copy link
Collaborator

Choose a reason for hiding this comment

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

add rust tag

/// use borsh::{BorshSerialize, BorshDeserialize};
/// use serde::{Serialize, Deserialize};
/// use near_sdk_macro::NearSchema;
/// #[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, NearSchema)]
/// #[borsh(crate = "near_sdk::borsh")]
/// #[serde(crate = "near_sdk::serde")]
/// struct MyStruct {
/// pub name: String,
/// #[near]
/// impl Adder {
/// #[result_serializer(borsh)]
/// pub fn borsh_parameters(&self, #[serializer(borsh)] a: Pair, #[serializer(borsh)] b: Pair) -> Pair {
/// /// ...
/// }
/// }
/// ```
///
/// `#[near]` will also handle serializing and setting the return value of the
/// function execution based on what type is returned by the function. By default, this will be
/// done through `serde` serialized as JSON, but this can be overwritten using
/// `#[result_serializer(borsh)]`:
/// ```ignore
Copy link
Collaborator

Choose a reason for hiding this comment

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

rust

/// #[near]
/// impl Adder {
/// #[result_serializer(borsh)]
/// pub fn borsh_parameters(&self) -> Pair {
/// /// ...
/// }
/// }
/// ```
/// Please note that `BorshSchema` and `JsonSchema` are added inside NearSchema whenever you use near macro for struct or enum.
/// By default, if no serializers are passed, Borsh is used.
///
/// If you want this struct to be a contract state, you can pass in the contract_state argument.
/// # Usage for enum / struct
///
/// If the macro is used with struct or enum, it will make the struct or enum serializable with either
/// Borsh or Json depending on serializers passed. Use `#[near(serializers=[borsh])]` to make it serializable with Borsh.
/// Or use `#[near(serializers=[json])]` to make it serializable with Json. By default, borsh is used.
/// You can also specify both and none. BorshSchema or JsonSchema are always generated.
///
/// # Example
/// If you want the struct to be a contract state, you can pass in the contract_state argument.
///
/// ## Example
/// ```ignore
Copy link
Collaborator

Choose a reason for hiding this comment

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

same

/// use near_sdk::near;
///
/// #[near(contract_state)]
/// struct MyStruct {
/// pub name: String,
/// pub struct Contract {
/// data: i8,
/// }
///
/// #[near]
/// impl Contract {
/// pub fn some_function(&self) {}
/// }
/// ```
/// becomes:
/// As well, the macro supports arguments like `event_json` and `contract_metadata`.
///
/// # Events Standard:
///
/// By passing `event_json` as an argument `near_bindgen` will generate the relevant code to format events
/// according to NEP-297
///
/// For parameter serialization, this macro will generate a wrapper struct to include the NEP-297 standard fields `standard` and `version
/// as well as include serialization reformatting to include the `event` and `data` fields automatically.
/// The `standard` and `version` values must be included in the enum and variant declaration (see example below).
/// By default this will be JSON deserialized with `serde`
///
///
/// ## Examples
///
/// ```ignore
/// #[near_bindgen]
/// #[derive(BorshSerialize, BorshDeserialize, NearSchema)]
/// #[borsh(crate = "near_sdk::borsh")]
/// struct MyStruct {
/// pub name: String,
/// use near_sdk::near;
///
/// #[near(event_json(standard = "nepXXX"))]
/// pub enum MyEvents {
/// #[event_version("1.0.0")]
/// Swap { token_in: AccountId, token_out: AccountId, amount_in: u128, amount_out: u128 },
///
/// #[event_version("2.0.0")]
/// StringEvent(String),
///
/// #[event_version("3.0.0")]
/// EmptyEvent
/// }
///
/// #[near]
/// impl Contract {
/// pub fn some_function(&self) {
/// MyEvents::StringEvent(
/// String::from("some_string")
/// ).emit();
/// }
///
/// }
/// ```
///
/// As well, the macro supports arguments like `event_json` and `contract_metadata`.
/// # Contract Source Metadata Standard:
///
/// By using `contract_metadata` as an argument `near` will populate the contract metadata
/// according to [`NEP-330`](<https://github.com/near/NEPs/blob/master/neps/nep-0330.md>) standard. This still applies even when `#[near]` is used without
/// any arguments.
///
/// All fields(version, link, standard) are optional and will be populated with defaults from the Cargo.toml file if not specified.
///
/// The `contract_source_metadata()` view function will be added and can be used to retrieve the source metadata.
/// Also, the source metadata will be stored as a constant, `CONTRACT_SOURCE_METADATA`, in the contract code.
///
/// ## Examples
/// ```ignore
/// use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
/// use near_sdk::near;
///
/// #[derive(Default, BorshSerialize, BorshDeserialize)]
/// #[near(contract_metadata(
/// version = "39f2d2646f2f60e18ab53337501370dc02a5661c",
/// link = "https://github.com/near-examples/nft-tutorial",
/// standard(standard = "nep330", version = "1.1.0"),
/// standard(standard = "nep171", version = "1.0.0"),
/// standard(standard = "nep177", version = "2.0.0"),
/// ))]
/// struct Contract {}
/// ```
#[proc_macro_attribute]
pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
if attr.to_string().contains("event_json") {
Expand Down Expand Up @@ -235,103 +318,23 @@ pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}

/// This attribute macro is used on a struct and its implementations
/// to generate the necessary code to expose `pub` methods from the contract as well
/// as generating the glue code to be a valid NEAR contract.
///
/// This macro will generate code to load and deserialize state if the `self` parameter is included
/// as well as saving it back to state if `&mut self` is used.
///
/// For parameter serialization, this macro will generate a struct with all of the parameters as
/// fields and derive deserialization for it. By default this will be JSON deserialized with `serde`
/// but can be overwritten by using `#[serializer(borsh)]`.
///
/// `#[near_bindgen]` will also handle serializing and setting the return value of the
/// function execution based on what type is returned by the function. By default, this will be
/// done through `serde` serialized as JSON, but this can be overwritten using
/// `#[result_serializer(borsh)]`.
///
/// # Examples
///
/// This macro is deprecated. Use [#\[near\]](./attr.near.html) instead. The difference between #\[near\] and #\[near_bindgen\] is that
/// with #\[near_bindgen\] you have to manually add boilerplate code for structs and enums so that they become Json- and Borsh-serializable:
/// ```ignore
/// use near_sdk::near_bindgen;
///
/// #[near_bindgen]
/// pub struct Contract {
/// data: i8,
/// }
///
/// #[near_bindgen]
/// impl Contract {
/// pub fn some_function(&self) {}
/// #[derive(BorshSerialize, BorshDeserialize, NearSchema)]
/// #[borsh(crate = "near_sdk::borsh")]
/// struct MyStruct {
/// pub name: String,
/// }
/// ```
///
/// Events Standard:
///
/// By passing `event_json` as an argument `near_bindgen` will generate the relevant code to format events
/// according to NEP-297
///
/// For parameter serialization, this macro will generate a wrapper struct to include the NEP-297 standard fields `standard` and `version
/// as well as include serialization reformatting to include the `event` and `data` fields automatically.
/// The `standard` and `version` values must be included in the enum and variant declaration (see example below).
/// By default this will be JSON deserialized with `serde`
///
///
/// # Examples
///
/// Instead of:
/// ```ignore
/// use near_sdk::near_bindgen;
///
/// #[near_bindgen(event_json(standard = "nepXXX"))]
/// pub enum MyEvents {
/// #[event_version("1.0.0")]
/// Swap { token_in: AccountId, token_out: AccountId, amount_in: u128, amount_out: u128 },
///
/// #[event_version("2.0.0")]
/// StringEvent(String),
///
/// #[event_version("3.0.0")]
/// EmptyEvent
/// }
///
/// #[near_bindgen]
/// impl Contract {
/// pub fn some_function(&self) {
/// MyEvents::StringEvent(
/// String::from("some_string")
/// ).emit();
/// }
///
/// #[near]
/// struct MyStruct {
/// pub name: String,
/// }
/// ```
///
/// Contract Source Metadata Standard:
///
/// By using `contract_metadata` as an argument `near_bindgen` will populate the contract metadata
/// according to [`NEP-330`](<https://github.com/near/NEPs/blob/master/neps/nep-0330.md>) standard. This still applies even when `#[near_bindgen]` is used without
/// any arguments.
///
/// All fields(version, link, standard) are optional and will be populated with defaults from the Cargo.toml file if not specified.
///
/// The `contract_source_metadata()` view function will be added and can be used to retrieve the source metadata.
/// Also, the source metadata will be stored as a constant, `CONTRACT_SOURCE_METADATA`, in the contract code.
///
/// # Examples
/// ```ignore
/// use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
/// use near_sdk::near_bindgen;
///
/// #[derive(Default, BorshSerialize, BorshDeserialize)]
/// #[near_bindgen(contract_metadata(
/// version = "39f2d2646f2f60e18ab53337501370dc02a5661c",
/// link = "https://github.com/near-examples/nft-tutorial",
/// standard(standard = "nep330", version = "1.1.0"),
/// standard(standard = "nep171", version = "1.0.0"),
/// standard(standard = "nep177", version = "2.0.0"),
/// ))]
/// struct Contract {}
/// ```
#[proc_macro_attribute]
pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
if attr.to_string().contains("event_json") {
Expand Down Expand Up @@ -459,7 +462,7 @@ fn process_impl_block(
/// Each of these static methods takes positional arguments defined by the Trait,
/// then the receiver_id, the attached deposit and the amount of gas and returns a new Promise.
///
/// # Examples
/// ## Examples
///
/// ```ignore
/// use near_sdk::ext_contract;
Expand All @@ -469,7 +472,19 @@ fn process_impl_block(
/// fn mult(&self, a: u64, b: u64) -> u128;
/// fn sum(&self, a: u128, b: u128) -> u128;
/// }
///
/// #[near]
/// impl Contract {
/// pub fn multiply_by_five(&mut self, number: u64) -> Promise {
/// ext_calculator::ext(self.calculator_account.clone())
/// .with_static_gas(Gas::from_tgas(5))
/// .mult(number, 5)
/// }
/// }
///
/// ```
///
/// See more information about role of ext_contract in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/crosscontract)
PolyProgrammist marked this conversation as resolved.
Show resolved Hide resolved
#[proc_macro_attribute]
pub fn ext_contract(attr: TokenStream, item: TokenStream) -> TokenStream {
if let Ok(mut input) = syn::parse::<ItemTrait>(item) {
Expand Down Expand Up @@ -553,6 +568,18 @@ struct DeriveNearSchema {
borsh: Option<bool>,
}

/// `NearSchema` is a derive macro that generates `BorshSchema` and / or `JsonSchema` implementations.
/// Use `#[abi(json)]` attribute to generate code for `JsonSchema`. And `#[abi(borsh)]` for `BorshSchema`.
PolyProgrammist marked this conversation as resolved.
Show resolved Hide resolved
/// You can use both and none as well.
/// ## Example
/// ```ignore
/// #[derive(NearSchema)]
/// #[abi(borsh)]
/// struct Value {
/// field: InnerValue,
/// }
/// ```
/// In this example, BorshSchema will be generated for `Value` struct.
#[proc_macro_derive(NearSchema, attributes(abi, serde, borsh, schemars, validate, inside_nearsdk))]
pub fn derive_near_schema(#[allow(unused)] input: TokenStream) -> TokenStream {
#[cfg(not(feature = "abi"))]
Expand Down Expand Up @@ -787,6 +814,31 @@ pub fn derive_no_default(item: TokenStream) -> TokenStream {
/// `BorshStorageKey` generates implementation for `BorshIntoStorageKey` trait.
/// It allows the type to be passed as a unique prefix for persistent collections.
/// The type should also implement or derive `BorshSerialize` trait.
///
/// More information about storage keys in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/storage)
PolyProgrammist marked this conversation as resolved.
Show resolved Hide resolved
/// ## Example
/// ```ignore
/// #[derive(BorshSerialize, BorshDeserialize, BorshStorageKey)]
/// #[borsh(crate = "near_sdk::borsh")]
/// pub enum StorageKey {
/// Messages,
/// }
///
/// // Define the contract structure
/// #[near(contract_state)]
/// pub struct Contract {
/// messages: Vector<String>
/// }
///
/// // Define the default, which automatically initializes the contract
/// impl Default for Contract {
/// fn default() -> Self {
/// Self {
/// messages: Vector::new(StorageKey::Messages)
/// }
/// }
/// }
/// ```
#[proc_macro_derive(BorshStorageKey)]
pub fn borsh_storage_key(item: TokenStream) -> TokenStream {
let (name, generics) = if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
Expand Down
Loading
Loading