1#![warn(missing_docs)]
4#![warn(missing_debug_implementations)]
5#![warn(missing_copy_implementations)]
6
7use std::{convert::Infallible, str::FromStr};
8
9use async_trait::async_trait;
10
11use ethers::types::U256;
12use serde::{Deserialize, Serialize};
13use url::Url;
14
15pub mod generators;
17pub mod open_sea;
19
20#[cfg(feature = "axum")]
21pub mod server;
23
24use open_sea::{ContractMetadata, OpenSeaAttribute};
25
26pub mod prelude {
28 #[cfg(feature = "axum")]
29 pub use crate::server::{
30 serve_generator, serve_generator_with_span, serve_router, serve_router_with_span,
31 };
32 #[cfg(feature = "axum")]
33 pub use ethers::types::U256;
34
35 pub use crate::{open_sea::*, MetadataGenerator, NftImage, NftMetadata};
36 pub use url::Url;
37}
38
39#[derive(Clone, Debug, Serialize, Deserialize)]
41#[serde(untagged)]
42pub enum NftImage {
43 Url {
45 image: url::Url,
47 },
48 Data {
50 image_data: String,
52 },
53}
54
55impl FromStr for NftImage {
56 type Err = Infallible;
57
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 s.parse::<Url>().map(Into::into).or_else(|_| Ok(s.into()))
60 }
61}
62
63impl From<Url> for NftImage {
64 fn from(image: Url) -> Self {
65 NftImage::Url { image }
66 }
67}
68
69impl From<&str> for NftImage {
70 fn from(image_data: &str) -> Self {
71 NftImage::Data {
72 image_data: image_data.to_string(),
73 }
74 }
75}
76
77#[derive(Clone, Debug, Serialize, Deserialize)]
80pub struct NftMetadata {
81 pub name: String,
83 pub description: String,
85 pub external_url: Url,
87 #[serde(flatten)]
89 pub image: NftImage,
90 #[serde(default)]
92 pub attributes: Vec<OpenSeaAttribute>,
93 #[serde(default, skip_serializing_if = "Option::is_none")]
95 pub background_color: Option<String>,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub animation_url: Option<Url>,
99 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub youtube_url: Option<Url>,
102}
103
104#[async_trait]
112pub trait MetadataGenerator {
113 type Error: std::error::Error + Send + Sync + 'static;
115
116 async fn metadata_for(&self, token_id: U256) -> Result<Option<NftMetadata>, Self::Error>;
118
119 async fn contract_metadata(&self) -> Option<ContractMetadata>;
125}
126
127#[cfg(test)]
128mod tests {
129 use ethers::types::U256;
130 use serde_json::json;
131
132 #[test]
133 fn it_works() {
134 let num = "0x3e10".parse::<U256>().unwrap();
135 dbg!(num);
136 dbg!(num.to_string());
137 dbg!(format!("{}", num));
138 dbg!(json!({"number": 5u64}));
139 }
140}