2023-01-26 23:07:16 +01:00
use serde ::Deserialize ;
2023-02-01 18:47:27 +01:00
use std ::fmt ::{ self , Display , Write } ;
2023-02-02 11:34:58 +01:00
use time ::Date ;
2023-02-03 16:45:06 +01:00
use crate ::{ IMG_HOST , SETS_BY_NAME } ;
2023-01-26 23:07:16 +01:00
#[ derive(Debug, Deserialize, PartialEq, Eq, Clone) ]
pub struct CardInfo {
pub data : Vec < Card > ,
}
#[ derive(Debug, Deserialize, PartialEq, Eq, Clone, Default) ]
pub struct Card {
2023-02-03 16:19:26 +01:00
pub id : usize ,
2023-01-26 23:07:16 +01:00
#[ serde(rename = " type " ) ]
2023-02-03 16:19:26 +01:00
pub card_type : String ,
pub name : String ,
2023-01-26 23:07:16 +01:00
#[ serde(rename = " desc " ) ]
2023-02-03 16:19:26 +01:00
pub text : String ,
2023-01-26 23:07:16 +01:00
// Will also be None for ?
2023-02-03 16:19:26 +01:00
pub atk : Option < i32 > ,
pub def : Option < i32 > ,
pub attribute : Option < String > ,
2023-01-26 23:07:16 +01:00
#[ serde(rename = " race " ) ]
2023-02-03 16:19:26 +01:00
pub r#type : String ,
2023-01-26 23:07:16 +01:00
// also includes rank
2023-02-03 16:19:26 +01:00
pub level : Option < i32 > ,
2023-01-26 23:07:16 +01:00
#[ serde(rename = " linkval " ) ]
2023-02-03 16:19:26 +01:00
pub link_rating : Option < i32 > ,
2023-01-26 23:07:16 +01:00
#[ serde(rename = " linkmarkers " ) ]
2023-02-03 16:19:26 +01:00
pub link_arrows : Option < Vec < String > > ,
2023-02-01 18:47:27 +01:00
#[ serde(default) ]
2023-02-03 16:19:26 +01:00
pub card_sets : Vec < CardSet > ,
pub banlist_info : Option < BanlistInfo > ,
}
#[ derive(Debug, Deserialize, PartialEq, Eq, Clone, Copy, Default) ]
pub struct BanlistInfo {
#[ serde(default) ]
pub ban_tcg : BanlistStatus ,
}
#[ derive(Debug, Deserialize, PartialEq, Eq, Clone, Copy, Default) ]
pub enum BanlistStatus {
Banned = 0 ,
Limited = 1 ,
#[ serde(rename = " Semi-Limited " ) ]
SemiLimited = 2 ,
#[ default ]
Unlimited = 3 ,
2023-02-01 18:47:27 +01:00
}
#[ derive(Debug, Deserialize, PartialEq, Eq, Clone, Default) ]
pub struct CardSet {
pub set_name : String ,
pub set_code : String ,
pub set_rarity : String ,
}
2023-02-02 11:34:58 +01:00
#[ derive(Debug, Deserialize, PartialEq, Eq, Clone) ]
pub struct Set {
pub set_name : String ,
pub tcg_date : Option < Date > ,
}
2023-02-01 18:47:27 +01:00
impl Card {
pub fn extended_info ( & self ) -> Result < String , fmt ::Error > {
let mut s = String ::with_capacity ( 1000 ) ;
2023-02-17 15:37:03 +01:00
write! ( s , " <p>Click <a href= \" https://db.ygorganization.com/search#card:{} \" >here</a> for rulings.</p> " , & self . name ) ? ;
2023-02-01 18:47:27 +01:00
s . push_str ( " <h3>Printings:</h3> " ) ;
for printing in & self . card_sets {
2023-02-02 11:34:58 +01:00
write! ( s , " {}: {} ({}) " , printing . set_name , printing . set_code , printing . set_rarity ) ? ;
if let Some ( date ) = SETS_BY_NAME . get ( & printing . set_name . to_lowercase ( ) ) . and_then ( | s | s . tcg_date ) {
2023-02-13 11:55:05 +01:00
write! ( s , " - {date} " ) ? ;
2023-02-02 11:34:58 +01:00
}
s . push_str ( " <br/> " ) ;
2023-02-01 18:47:27 +01:00
}
Ok ( s )
}
2023-01-26 23:07:16 +01:00
2023-02-13 11:55:05 +01:00
pub fn short_info ( & self ) -> Result < String , fmt ::Error > {
let mut s = String ::new ( ) ;
s . push_str ( & self . name ) ;
s . push ( '\n' ) ;
self . basic_info ( & mut s , " \n " ) ? ;
Ok ( s )
}
fn basic_info < W : Write > ( & self , f : & mut W , newline : & str ) -> fmt ::Result {
2023-01-26 23:07:16 +01:00
if let Some ( level ) = self . level {
2023-01-30 11:39:42 +01:00
if self . card_type . contains ( " XYZ " ) {
f . write_str ( " Rank " ) ? ;
} else {
f . write_str ( " Level " ) ? ;
}
write! ( f , " {level} " ) ? ;
2023-01-26 23:07:16 +01:00
} else if let Some ( lr ) = self . link_rating {
write! ( f , " Link {lr} " ) ? ;
}
if let Some ( attr ) = & self . attribute {
write! ( f , " {attr}/ " ) ? ;
}
2023-01-30 15:57:08 +01:00
write! ( f , " {} {} " , self . r#type , self . card_type ) ? ;
2023-01-26 23:07:16 +01:00
if self . card_type . contains ( & String ::from ( " Monster " ) ) {
2023-02-13 11:55:05 +01:00
f . write_str ( newline ) ? ;
2023-01-26 23:07:16 +01:00
match ( self . atk , self . def ) {
2023-01-27 00:03:00 +01:00
( Some ( atk ) , Some ( def ) ) = > write! ( f , " {atk} ATK / {def} DEF " ) ? ,
2023-01-26 23:07:16 +01:00
( Some ( atk ) , None ) if self . link_rating . is_some ( ) = > write! ( f , " {atk} ATK " ) ? ,
2023-01-27 00:03:00 +01:00
( None , Some ( def ) ) = > write! ( f , " ? ATK / {def} DEF " ) ? ,
( Some ( atk ) , None ) = > write! ( f , " {atk} ATK / ? DEF " ) ? ,
( None , None ) = > write! ( f , " ? ATK / ? DEF " ) ? ,
2023-01-26 23:07:16 +01:00
}
}
2023-02-13 11:55:05 +01:00
Ok ( ( ) )
}
}
impl Display for Card {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
write! (
f ,
r # "<h2 class="cardname">{} {}</h2><em>"# ,
& self . name ,
match self . banlist_info . map ( | bi | bi . ban_tcg ) {
Some ( BanlistStatus ::Banned ) = > format! ( r # "<img class="banlist-icon" src="{}/static/forbidden.svg"/>"# , IMG_HOST . as_str ( ) ) ,
Some ( BanlistStatus ::Limited ) = > format! ( r # "<img class="banlist-icon" src="{}/static/limited.svg"/>"# , IMG_HOST . as_str ( ) ) ,
Some ( BanlistStatus ::SemiLimited ) = >
format! ( r # "<img class="banlist-icon" src="{}/static/semi_limited.svg"/>"# , IMG_HOST . as_str ( ) ) ,
_ = > String ::new ( ) ,
}
) ? ;
self . basic_info ( f , " <br/> " ) ? ;
2023-02-17 15:37:03 +01:00
write! ( f , " </em><hr/><p>{}</p> " , & self . text ) ? ;
2023-01-26 23:07:16 +01:00
Ok ( ( ) )
}
}
#[ cfg(test) ]
pub mod tests {
use super ::* ;
pub const RAW_SPELL : & str = r #"
{
" id " : 41142615 ,
" name " : " The Cheerful Coffin " ,
" type " : " Spell Card " ,
" desc " : " Discard up to 3 Monster Cards from your hand to the Graveyard. " ,
2023-02-01 18:47:27 +01:00
" race " : " Normal " ,
" card_sets " : [
{
" set_name " : " Dark Beginning 1 " ,
" set_code " : " DB1-EN167 " ,
" set_rarity " : " Common " ,
" set_rarity_code " : " (C) " ,
" set_price " : " 1.41 "
} ,
{
" set_name " : " Metal Raiders " ,
" set_code " : " MRD-059 " ,
" set_rarity " : " Common " ,
" set_rarity_code " : " (C) " ,
" set_price " : " 1.55 "
}
]
2023-01-26 23:07:16 +01:00
} " #;
pub const RAW_MONSTER : & str = r #"
{
" id " : 2326738 ,
" name " : " Des Lacooda " ,
" type " : " Effect Monster " ,
" desc " : " Once per turn: You can change this card to face-down Defense Position. When this card is Flip Summoned: Draw 1 card. " ,
" atk " : 500 ,
" def " : 600 ,
" level " : 3 ,
" race " : " Zombie " ,
2023-02-01 18:47:27 +01:00
" attribute " : " EARTH " ,
" card_sets " : [
{
" set_name " : " Astral Pack Three " ,
" set_code " : " AP03-EN018 " ,
" set_rarity " : " Common " ,
" set_rarity_code " : " (C) " ,
" set_price " : " 1.24 "
} ,
{
" set_name " : " Gold Series " ,
" set_code " : " GLD1-EN010 " ,
" set_rarity " : " Common " ,
" set_rarity_code " : " (C) " ,
" set_price " : " 2.07 "
}
]
2023-01-26 23:07:16 +01:00
} " #;
#[ test ]
fn test_spell ( ) {
let coffin : Card = serde_json ::from_str ( RAW_SPELL ) . unwrap ( ) ;
assert_eq! (
coffin ,
Card {
id : 41142615 ,
card_type : " Spell Card " . to_owned ( ) ,
name : " The Cheerful Coffin " . to_owned ( ) ,
text : " Discard up to 3 Monster Cards from your hand to the Graveyard. " . to_owned ( ) ,
r#type : " Normal " . to_owned ( ) ,
2023-02-01 18:47:27 +01:00
card_sets : vec ! [
CardSet {
set_name : " Dark Beginning 1 " . to_owned ( ) ,
set_code : " DB1-EN167 " . to_owned ( ) ,
set_rarity : " Common " . to_owned ( ) ,
} ,
CardSet { set_name : " Metal Raiders " . to_owned ( ) , set_code : " MRD-059 " . to_owned ( ) , set_rarity : " Common " . to_owned ( ) }
] ,
2023-01-26 23:07:16 +01:00
.. Default ::default ( )
}
)
}
#[ test ]
fn test_monster ( ) {
let munch : Card = serde_json ::from_str ( RAW_MONSTER ) . unwrap ( ) ;
assert_eq! (
munch ,
Card {
id : 2326738 ,
card_type : " Effect Monster " . to_owned ( ) ,
name : " Des Lacooda " . to_owned ( ) ,
text :
" Once per turn: You can change this card to face-down Defense Position. When this card is Flip Summoned: Draw 1 card. "
. to_owned ( ) ,
atk : Some ( 500 ) ,
def : Some ( 600 ) ,
level : Some ( 3 ) ,
r#type : " Zombie " . to_owned ( ) ,
attribute : Some ( " EARTH " . to_owned ( ) ) ,
2023-02-01 18:47:27 +01:00
card_sets : vec ! [
CardSet {
set_name : " Astral Pack Three " . to_owned ( ) ,
set_code : " AP03-EN018 " . to_owned ( ) ,
set_rarity : " Common " . to_owned ( ) ,
} ,
CardSet { set_name : " Gold Series " . to_owned ( ) , set_code : " GLD1-EN010 " . to_owned ( ) , set_rarity : " Common " . to_owned ( ) }
] ,
2023-01-26 23:07:16 +01:00
.. Default ::default ( )
} ,
)
}
}