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

Add Borsh/Json -Schema to types #1158

Merged
merged 29 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7f94d65
add nearschema wherever possible
PolyProgrammist Mar 15, 2024
26cc62e
tmp
PolyProgrammist Mar 18, 2024
52a3dfe
using nearschema
PolyProgrammist Mar 22, 2024
b36c3c4
fix cfg
PolyProgrammist Mar 22, 2024
8fccbd6
use get_schema_derive inside NearSchema
PolyProgrammist Mar 22, 2024
09d1d7d
tmp
PolyProgrammist Mar 22, 2024
5887f59
Fix problems with TokenMetadata, LookupMap
PolyProgrammist Mar 22, 2024
97f250b
Try switch to near macro for CurveType enum
PolyProgrammist Mar 25, 2024
5a1d1a4
tmp
PolyProgrammist Mar 27, 2024
4278ae0
tmp
PolyProgrammist Mar 27, 2024
2fd4874
Previously added serializers=[borsh(use_discriminant = true)], now re…
PolyProgrammist Mar 27, 2024
24a7ae0
refactor a bit
PolyProgrammist Mar 27, 2024
4a924e2
switch from borshserialize to near macro
PolyProgrammist Mar 27, 2024
6e1666c
fix clippy and fmt
PolyProgrammist Mar 27, 2024
680a7fb
update clap
PolyProgrammist Mar 27, 2024
1402f8f
tmp
PolyProgrammist Mar 27, 2024
3c5213c
tmp
PolyProgrammist Mar 27, 2024
ac6c311
allow dead_code
PolyProgrammist Mar 28, 2024
a2e9dd3
refactor
PolyProgrammist Mar 28, 2024
1668551
ci
PolyProgrammist Mar 28, 2024
32f5750
add tests
PolyProgrammist Mar 28, 2024
f985461
refactor: add incrementally borsh & json in near macro
PolyProgrammist Mar 28, 2024
e0ab667
add cfg_attr inside schema_derive
PolyProgrammist Mar 28, 2024
56781ce
refactor: incremental schema_derive
PolyProgrammist Mar 28, 2024
a35390c
refactor: near_bindgen
PolyProgrammist Mar 28, 2024
877cbce
fmt
PolyProgrammist Mar 28, 2024
f7bb6b1
refactor: remove prints
PolyProgrammist Mar 28, 2024
10d6846
fix compilation test
PolyProgrammist Mar 28, 2024
70669e6
fmt
PolyProgrammist Mar 28, 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
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ jobs:
- uses: Swatinem/rust-cache@v1
- name: Downgrade dependencies
run: |
cargo update -p [email protected].3 --precise 4.4.18
cd examples/adder && cargo update -p [email protected].3 --precise 4.4.18
cargo update -p [email protected].4 --precise 4.4.18
cd examples/adder && cargo update -p [email protected].4 --precise 4.4.18
- name: test
run: cargo test --all --features unstable,legacy
lint:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ pub struct NFTContractMetadata {
pub reference_hash: Option<Base64VecU8>, // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.
}

/// Metadata on the individual token level.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[near(serializers=[borsh, json])]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TokenMetadata {
pub title: Option<String>, // ex. "Arch Nemesis: Mail Carrier" or "Parcel #5055"
pub description: Option<String>, // free-form description
Expand Down
8 changes: 5 additions & 3 deletions near-contract-standards/src/non_fungible_token/token.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use crate::non_fungible_token::metadata::TokenMetadata;
use near_sdk::serde::{Deserialize, Serialize};
use near_sdk::{AccountId, NearSchema};
use near_sdk::{
serde::{Deserialize, Serialize},
AccountId, NearSchema,
};
use std::collections::HashMap;
/// Note that token IDs for NFTs are strings on NEAR. It's still fine to use autoincrementing numbers as unique IDs if desired, but they should be stringified. This is to make IDs more future-proof as chain-agnostic conventions and standards arise, and allows for more flexibility with considerations like bridging NFTs across chains, etc.
pub type TokenId = String;

/// In this implementation, the Token struct takes two extensions standards (metadata and approval) as optional fields, as they are frequently used in modern NFTs.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, NearSchema)]
#[derive(NearSchema, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(crate = "near_sdk::serde")]
pub struct Token {
pub token_id: TokenId,
Expand Down
218 changes: 119 additions & 99 deletions near-sdk-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@ use darling::ast::NestedMeta;
use darling::{Error, FromMeta};
use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens};
use syn::{parse_quote, ImplItem, ItemEnum, ItemImpl, ItemStruct, ItemTrait, WhereClause};
use syn::{parse_quote, Expr, ImplItem, ItemEnum, ItemImpl, ItemStruct, ItemTrait, WhereClause};

#[derive(Debug)]
struct IdentsVector {
vec: Vec<Ident>,
#[derive(Debug, Clone)]
struct Serializers {
vec: Vec<Expr>,
}

impl FromMeta for IdentsVector {
impl FromMeta for Serializers {
fn from_expr(expr: &syn::Expr) -> Result<Self, darling::Error> {
match expr {
syn::Expr::Array(expr_array) => Ok(IdentsVector {
syn::Expr::Array(expr_array) => Ok(Serializers {
vec: expr_array
.elems
.iter()
.map(<Ident as FromMeta>::from_expr)
.map(<Expr as FromMeta>::from_expr)
.map(|x| x.unwrap())
.collect::<Vec<_>>(),
}),
Expand All @@ -37,13 +37,13 @@ impl FromMeta for IdentsVector {

#[derive(FromMeta)]
struct NearMacroArgs {
serializers: Option<IdentsVector>,
serializers: Option<Serializers>,
contract_state: Option<bool>,
contract_metadata: Option<core_impl::ContractMetadata>,
inside_nearsdk: Option<bool>,
}

/// This attribute macro is used to reduce enhance near_bindgen macro.
/// 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
///
Expand Down Expand Up @@ -114,101 +114,107 @@ pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
}
};

let near_sdk_crate = if near_macro_args.inside_nearsdk.unwrap_or(false) {
quote! {crate}
} else {
quote! {::near_sdk}
};
let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
let string_serde_crate = quote! {#near_sdk_crate::serde}.to_string();

let mut expanded: proc_macro2::TokenStream = quote! {};

if near_macro_args.contract_state.unwrap_or(false) {
if let Some(metadata) = near_macro_args.contract_metadata {
expanded = quote! {#[#near_sdk_crate::near_bindgen(#metadata)]}
} else {
expanded = quote! {#[#near_sdk_crate::near_bindgen]}
}
};

let mut has_borsh = false;
let mut has_json = false;

let mut borsh_attr = quote! {};

match near_macro_args.serializers {
Some(serializers) => {
for arg in serializers.vec {
if arg == "borsh" {
has_borsh = true;
} else if arg == "json" {
has_json = true;
} else {
return TokenStream::from(
syn::Error::new(
Span::call_site(),
format!("Serializer of near macro is invalid: {}", arg),
)
.to_compile_error(),
);
let attr2 = serializers.clone();

attr2.vec.iter().for_each(|old_expr| {
let new_expr = &mut old_expr.clone();
match &mut *new_expr {
Expr::Call(ref mut call_expr) => {
if let Expr::Path(ref mut path) = &mut *call_expr.func {
if let Some(ident) = path.path.get_ident() {
if *ident == "json" {
has_json = true;
path.path =
syn::Path::from(Ident::new("serde", Span::call_site()));
call_expr.args.push(parse_quote! {crate=#string_serde_crate});
} else if *ident == "borsh" {
has_borsh = true;
call_expr.args.push(parse_quote! {crate=#string_borsh_crate});
}
}
}
borsh_attr = quote! {#[#new_expr]};
}
Expr::Path(ref mut path_expr) => {
if let Some(ident) = path_expr.path.get_ident() {
if *ident == "json" {
has_json = true;
}
if *ident == "borsh" {
has_borsh = true;
borsh_attr = quote! {#[borsh(crate=#string_borsh_crate)]};
}
}
}
_ => {}
}
}
});
}
None => {
has_borsh = true;
borsh_attr = quote! {#[borsh(crate = #string_borsh_crate)]};
}
}

let near_sdk_crate = if near_macro_args.inside_nearsdk.unwrap_or(false) {
quote! {crate}
} else {
quote! {::near_sdk}
};
let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
let string_serde_crate = quote! {#near_sdk_crate::serde}.to_string();

let inside_nearsdk_attr = if near_macro_args.inside_nearsdk.unwrap_or(false) {
quote! {#[inside_nearsdk]}
} else {
quote! {}
};
#[cfg(feature = "abi")]
{
let schema_derive: proc_macro2::TokenStream =
get_schema_derive(has_json, has_borsh, near_sdk_crate.clone(), false);
expanded = quote! {
#expanded
#schema_derive
};
}

let borsh = if has_borsh {
quote! {
if has_borsh {
expanded = quote! {
#expanded
#[derive(#near_sdk_crate::borsh::BorshSerialize, #near_sdk_crate::borsh::BorshDeserialize)]
#[borsh(crate = #string_borsh_crate)]
}
} else {
quote! {}
};
let json = if has_json {
quote! {
#borsh_attr
};
}

if has_json {
expanded = quote! {
#expanded
#[derive(#near_sdk_crate::serde::Serialize, #near_sdk_crate::serde::Deserialize)]
#[serde(crate = #string_serde_crate)]
}
} else {
quote! {}
};

let near_bindgen_annotation = if near_macro_args.contract_state.unwrap_or(false) {
if let Some(metadata) = near_macro_args.contract_metadata {
quote! {#[#near_sdk_crate::near_bindgen(#metadata)]}
} else {
quote! {#[#near_sdk_crate::near_bindgen]}
}
} else {
quote! {}
};

let mut abis = quote! {};
if has_borsh && has_json {
abis = quote! { #[abi(borsh, json)] };
} else if has_borsh {
abis = quote! { #[abi(borsh)] };
} else if has_json {
abis = quote! { #[abi(json)] };
};
}

let expanded;
if let Ok(input) = syn::parse::<ItemStruct>(item.clone()) {
expanded = quote! {
#near_bindgen_annotation
#[derive(#near_sdk_crate::NearSchema)]
#inside_nearsdk_attr
#borsh
#json
#abis
#expanded
#input
};
} else if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
expanded = quote! {
#near_bindgen_annotation
#[derive(#near_sdk_crate::NearSchema)]
#inside_nearsdk_attr
#borsh
#json
#abis
#expanded
#input
};
} else if let Ok(input) = syn::parse::<ItemImpl>(item) {
Expand Down Expand Up @@ -623,29 +629,11 @@ pub fn derive_near_schema(#[allow(unused)] input: TokenStream) -> TokenStream {
} else {
quote! {::near_sdk}
};
let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
let string_schemars_crate = quote! {#near_sdk_crate::schemars}.to_string();

// <unspecified> or #[abi(json)]
let json_schema = json_schema || !borsh_schema;

let derive = {
let mut derive = quote! {};
if borsh_schema {
derive = quote! {
#[derive(#near_sdk_crate::borsh::BorshSchema)]
#[borsh(crate = #string_borsh_crate)]
};
}
if json_schema {
derive = quote! {
#derive
#[derive(#near_sdk_crate::schemars::JsonSchema)]
#[schemars(crate = #string_schemars_crate)]
};
}
derive
};
let derive = get_schema_derive(json_schema, borsh_schema, near_sdk_crate.clone(), true);

let input_ident = &input.ident;

Expand Down Expand Up @@ -718,6 +706,38 @@ pub fn derive_near_schema(#[allow(unused)] input: TokenStream) -> TokenStream {
}
}

#[allow(dead_code)]
fn get_schema_derive(
json_schema: bool,
borsh_schema: bool,
near_sdk_crate: proc_macro2::TokenStream,
need_borsh_crate: bool,
) -> proc_macro2::TokenStream {
let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
let string_schemars_crate = quote! {#near_sdk_crate::schemars}.to_string();

let mut derive = quote! {};
if borsh_schema {
derive = quote! {
#[cfg_attr(not(target_arch = "wasm32"), derive(#near_sdk_crate::borsh::BorshSchema))]
};
if need_borsh_crate {
derive = quote! {
#derive
#[cfg_attr(not(target_arch = "wasm32"), borsh(crate = #string_borsh_crate))]
};
}
}
if json_schema {
derive = quote! {
#derive
#[cfg_attr(not(target_arch = "wasm32"), derive(#near_sdk_crate::schemars::JsonSchema))]
#[cfg_attr(not(target_arch = "wasm32"), schemars(crate = #string_schemars_crate))]
};
}
derive
}

#[cfg(feature = "abi")]
fn get_where_clause(
generics: &syn::Generics,
Expand Down
1 change: 1 addition & 0 deletions near-sdk/compilation_tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ fn compilation_tests() {
t.pass("compilation_tests/contract_metadata.rs");
t.compile_fail("compilation_tests/contract_metadata_fn_name.rs");
t.pass("compilation_tests/contract_metadata_bindgen.rs");
t.pass("compilation_tests/types.rs");
}
25 changes: 25 additions & 0 deletions near-sdk/compilation_tests/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! Check types from near_sdk.

use near_sdk::near;
use near_sdk::collections::{LookupMap, LookupSet, TreeMap, UnorderedMap, UnorderedSet, Vector};
use near_sdk::json_types::Base58CryptoHash;
use near_sdk::store::{Lazy, LazyOption};
use near_sdk::CurveType;

#[near(contract_state)]
struct TypesContainer {
lookup_map: LookupMap<u32, u64>,
lookup_set: LookupSet<u32>,
tree_map: TreeMap<u32, u64>,
unordered_map: UnorderedMap<u32, u64>,
unordered_set: UnorderedSet<u32>,
vector: Vector<u32>,
base58_crypto_hash: Base58CryptoHash,
u64_type: near_sdk::json_types::U64,
base64_vec_u8: near_sdk::json_types::Base64VecU8,
lazy: Lazy<u64>,
lazy_option: LazyOption<u64>,
curve_type: CurveType,
}

fn main() {}
5 changes: 3 additions & 2 deletions near-sdk/src/collections/legacy_tree_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![allow(deprecated)]

use borsh::{BorshDeserialize, BorshSerialize};
use near_sdk_macros::near;
use std::ops::Bound;

use crate::collections::UnorderedMap;
Expand All @@ -21,14 +22,14 @@ use crate::IntoStorageKey;
/// - `range` of K elements: O(Klog(N))
///
#[deprecated(since = "4.1.0", note = "Use near_sdk::collections::TreeMap")]
#[derive(BorshSerialize, BorshDeserialize)]
#[near(inside_nearsdk)]
pub struct LegacyTreeMap<K, V> {
root: u64,
val: UnorderedMap<K, V>,
tree: Vector<Node<K>>,
}

#[derive(Clone, BorshSerialize, BorshDeserialize)]
#[near(inside_nearsdk)]
pub struct Node<K> {
id: u64,
key: K, // key stored in a node
Expand Down
1 change: 0 additions & 1 deletion near-sdk/src/collections/lookup_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const ERR_VALUE_DESERIALIZATION: &str = "Cannot deserialize value with Borsh";
const ERR_VALUE_SERIALIZATION: &str = "Cannot serialize value with Borsh";

/// An non-iterable implementation of a map that stores its content directly on the trie.

#[near(inside_nearsdk)]
pub struct LookupMap<K, V> {
key_prefix: Vec<u8>,
Expand Down
Loading
Loading