sugar_cli/validate/
format.rs1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3
4use super::ValidateParserError;
5use crate::{common::*, validate::parser};
6
7#[derive(Debug, Clone, Deserialize, Default, Serialize)]
8pub struct Metadata {
9 pub name: String,
10 pub symbol: Option<String>,
11 pub description: String,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub seller_fee_basis_points: Option<u16>,
14 pub image: String,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub animation_url: Option<String>,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub external_url: Option<String>,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub attributes: Option<Vec<Attribute>>,
21 pub properties: Property,
22}
23
24impl Metadata {
25 pub fn validate(&mut self) -> Result<(), ValidateParserError> {
26 parser::check_name(&self.name)?;
27 parser::check_url(&self.image)?;
28
29 if let Some(sfbp) = &self.seller_fee_basis_points {
31 parser::check_seller_fee_basis_points(*sfbp)?;
32 }
33 if let Some(symbol) = &self.symbol {
34 parser::check_symbol(symbol)?;
35 }
36
37 if let Some(creators) = &self.properties.creators {
38 parser::check_creators_shares(creators)?;
39 parser::check_creators_addresses(creators)?;
40 }
41
42 if self.properties.category.is_none() {
43 let category = match &self.animation_url {
44 Some(_) => "video",
45 None => "image",
46 };
47 self.properties.category = Some(category.to_string());
48
49 println!(
50 "{} missing `properties.category` for nft {}, defaulting to {}",
51 WARNING_EMOJI, &self.name, category
52 );
53 }
54 parser::check_category(
55 self.properties
56 .category
57 .as_ref()
58 .expect("unreachable, should never throw"),
59 )?;
60
61 if let Some(animation_url) = &self.animation_url {
62 parser::check_url(animation_url)?;
63 }
64
65 if let Some(external_url) = &self.external_url {
66 parser::check_url(external_url)?;
67 }
68
69 Ok(())
70 }
71}
72
73#[derive(Debug, Clone, Deserialize, Default, Serialize)]
74pub struct Property {
75 pub files: Vec<FileAttr>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub creators: Option<Vec<Creator>>,
78 pub category: Option<String>,
79}
80
81#[derive(Debug, Clone, Deserialize, Default, Serialize)]
82pub struct Creator {
83 pub address: String,
84 pub share: u16,
85}
86
87#[derive(Debug, Clone, Deserialize, Default, Serialize)]
88pub struct Attribute {
89 pub trait_type: String,
90 pub value: String,
91}
92
93#[derive(Debug, Clone, Deserialize, Default, Serialize)]
94pub struct FileAttr {
95 pub uri: String,
96 #[serde(rename = "type")]
97 pub file_type: String,
98 #[serde(default, skip_serializing_if = "bool_is_false")]
99 pub cdn: bool,
100}
101
102fn bool_is_false(value: &bool) -> bool {
103 !value
104}