--- a/pallets/proxy-rmrk-core/src/lib.rs +++ b/pallets/proxy-rmrk-core/src/lib.rs @@ -443,7 +443,7 @@ >::get((collection_id, token_id)) .unwrap() .rmrk_nft_type() - .ok_or(>::NoAvailableNftId.into()) + .ok_or_else(|| >::NoAvailableNftId.into()) } pub fn ensure_nft_type(collection_id: CollectionId, token_id: TokenId, nft_type: NftType) -> DispatchResult { @@ -453,6 +453,65 @@ Ok(()) } + pub fn filter_theme_properties( + collection_id: CollectionId, + token_id: TokenId, + filter_keys: Option> + ) -> Result, DispatchError> { + filter_keys.map(|keys| { + let properties = keys.into_iter() + .filter_map(|key| { + let key: RmrkString = key.try_into().ok()?; + + let value = Self::get_nft_property( + collection_id, + token_id, + RmrkProperty::ThemeProperty(&key) + ).ok()?.decode_or_default(); + + let property = RmrkThemeProperty { + key, + value + }; + + Some(property) + }) + .collect(); + + Ok(properties) + }).unwrap_or_else(|| { + let properties = Self::iterate_theme_properties(collection_id, token_id)? + .collect(); + + Ok(properties) + }) + } + + pub fn iterate_theme_properties( + collection_id: CollectionId, + token_id: TokenId + ) -> Result, DispatchError> { + let key_prefix = rmrk_property!(Config=T, key: ThemeProperty(&RmrkString::default()))?; + + let properties = >::token_properties((collection_id, token_id)) + .into_iter() + .filter_map(move |(key, value)| { + let key = key.as_slice().strip_prefix(key_prefix.as_slice())?; + + let key: RmrkString = key.to_vec().try_into().ok()?; + let value: RmrkString = value.decode_or_default(); + + let property = RmrkThemeProperty { + key, + value + }; + + Some(property) + }); + + Ok(properties) + } + pub fn get_typed_nft_collection( collection_id: CollectionId, collection_type: CollectionType --- a/pallets/proxy-rmrk-core/src/property.rs +++ b/pallets/proxy-rmrk-core/src/property.rs @@ -1,7 +1,7 @@ use super::*; use core::convert::AsRef; -pub enum RmrkProperty { +pub enum RmrkProperty<'r> { Metadata, CollectionType, RoyaltyInfo, @@ -23,11 +23,11 @@ EquippableList, ZIndex, ThemeName, - ThemeProperty(RmrkString), + ThemeProperty(&'r RmrkString), ThemeInherit, } -impl RmrkProperty { +impl<'r> RmrkProperty<'r> { pub fn to_key(self) -> Result> { fn get_bytes>(container: &T) -> &[u8] { container.as_ref() --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -775,6 +775,10 @@ self.0.iter() } + pub fn into_iter(self) -> impl Iterator { + self.0.into_iter() + } + fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> { if key.is_empty() { return Err(PropertiesError::EmptyPropertyKey); @@ -848,6 +852,10 @@ pub fn iter(&self) -> impl Iterator { self.map.iter() } + + pub fn into_iter(self) -> impl Iterator { + self.map.into_iter() + } } impl TrySetProperty for Properties { @@ -936,7 +944,8 @@ pub type RmrkBaseInfo = BaseInfo; pub type RmrkPartType = PartType>; -pub type RmrkTheme = Theme>>; +pub type RmrkThemeProperty = ThemeProperty; +pub type RmrkTheme = Theme>; pub type RmrkRpcString = Vec; pub type RmrkThemeName = RmrkRpcString; --- a/runtime/common/src/runtime_apis.rs +++ b/runtime/common/src/runtime_apis.rs @@ -184,7 +184,7 @@ }, None => return Ok(None) }; - + let allowance = pallet_nonfungible::Allowance::::get((collection_id, nft_id)); Ok(Some(RmrkInstanceInfo { @@ -317,7 +317,7 @@ if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Resource).is_err() { return Ok(Vec::new()); } Ok(Vec::new(/*[RmrkResourceInfo { - + }]*/)) } @@ -379,16 +379,18 @@ fn theme_names(base_id: RmrkBaseId) -> Result, DispatchError> { use frame_support::BoundedVec; - use pallet_proxy_rmrk_core::{RmrkProperty, misc::{RmrkNft, RmrkDecode}}; + use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkNft, RmrkDecode}}; let collection_id = CollectionId(base_id); - // todo make sure this is theme + if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { + return Ok(Vec::new()); + } - let theme_names = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result, DispatchError>)? + let theme_names = (dispatch_unique_runtime!(collection_id.collection_tokens()))? .iter() .filter_map(|token_id| { let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap(); - + match nft_type { Theme => Some( RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::ThemeName).unwrap().decode_or_default() @@ -396,47 +398,59 @@ _ => None } }) - .collect::>(); + .collect(); Ok(theme_names) } fn theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option>) -> Result, DispatchError> { use frame_support::BoundedVec; + use pallet_proxy_rmrk_core::{ + RmrkProperty, + misc::{CollectionType, NftType, RmrkNft, RmrkDecode} + }; let collection_id = CollectionId(base_id); + if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { + return Ok(None); + } - // todo one theme. filter collection tokens according to theme name, should result in one - // (is it possible to search with iter_prefix for part of a struct that satisfies?..) - // filter properties according to filter_keys and load them into resulting theme.properties - let themes = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result, DispatchError>)? - .iter() - .filter_map(|token_id| { - let properties = Nonfungible::token_properties((collection_id, token_id)); + let theme_info = (dispatch_unique_runtime!(collection_id.collection_tokens()))? + .into_iter() + .find_map(|token_id| { + RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Theme).ok()?; + + let name: RmrkString = RmrkCore::get_nft_property( + collection_id, token_id, RmrkProperty::ThemeName + ).ok()?.decode_or_default(); - // todo ping properties for "rmrk:nft-type" - // if none, skip, None - // ugh gonna go through ALL properties, searching for matches for "rmrk:theme-property-" - let nft_type = "theme"; - match nft_type { - "theme" => Some(RmrkTheme { - name: BoundedVec::try_from( - >::get((collection_id, token_id)) - .map(|t| t.const_data) - .unwrap_or_default() - .into_inner() - ).unwrap(), - // todo? (dispatch_unique_runtime!(collection_id.const_metadata(token_id)) as Result, DispatchError>)?, - properties: Vec::new(), // pain in the ass - inherit: false, // "rmrk:theme-inherit" - }), - _ => None + if name == theme_name { + Some((name, token_id)) + } else { + None } - }) - .collect::>(); + }); + + let (name, theme_id) = match theme_info { + Some((name, theme_id)) => (name, theme_id), + None => return Ok(None) + }; - // todo - Ok(Some(themes[0].clone())) + let properties = RmrkCore::filter_theme_properties(collection_id, theme_id, filter_keys)?; + + let inherit = RmrkCore::get_nft_property( + collection_id, + theme_id, + RmrkProperty::ThemeInherit + )?.decode_or_default(); + + let theme = RmrkTheme { + name, + properties, + inherit, + }; + + Ok(Some(theme)) } } --- a/runtime/opal/src/lib.rs +++ b/runtime/opal/src/lib.rs @@ -1149,7 +1149,7 @@ let collection = ::CollectionDispatch::dispatch(>::try_get($collection)?); let dispatch = collection.as_dyn(); - Ok(dispatch.$method($($name),*)) + Ok::<_, DispatchError>(dispatch.$method($($name),*)) }}; } --- a/runtime/quartz/src/lib.rs +++ b/runtime/quartz/src/lib.rs @@ -1122,7 +1122,7 @@ let collection = ::CollectionDispatch::dispatch(>::try_get($collection)?); let dispatch = collection.as_dyn(); - Ok(dispatch.$method($($name),*)) + Ok::<_, DispatchError>(dispatch.$method($($name),*)) }}; } --- a/runtime/unique/src/lib.rs +++ b/runtime/unique/src/lib.rs @@ -1127,7 +1127,7 @@ let collection = ::CollectionDispatch::dispatch(>::try_get($collection)?); let dispatch = collection.as_dyn(); - Ok(dispatch.$method($($name),*)) + Ok::<_, DispatchError>(dispatch.$method($($name),*)) }}; }