lordeckcodes 0.4.0

Legends of Runeterra deck encoder/decoder
Documentation
use std::fs::File;
use std::io::Read;
use std::io::{BufRead, BufReader};

use serde_json::Error;

use lordeckcodes::{encoder, CardCodeAndCount, Deck, LorError};

#[test]
fn basic_decode_test() {
    let deck = encoder::deck_from_code(String::from(
        "CIBAEAIBAQTQMAIAAILSQLBNGUBACAIBFYDACAAHBEHR2IBLAEBACAIFAY",
    ));
    assert!(deck.is_ok());
}

#[test]
fn basic_encode_test() {
    let deck: Deck = [
        ("01SI015", 3),
        ("01SI044", 3),
        ("01SI048", 3),
        ("01SI054", 3),
        ("01FR003", 3),
        ("01FR012", 3),
        ("01FR020", 3),
        ("01FR024", 3),
        ("01FR033", 3),
        ("01FR036", 3),
        ("01FR039", 3),
        ("01FR052", 3),
        ("01SI005", 2),
        ("01FR004", 2),
    ]
    .iter()
    .collect();

    let code = encoder::code_from_deck(&deck);
    assert_eq!(
        code.unwrap(),
        "CIBAIAIFB4WDANQIAEAQGDAUDAQSIJZUAIAQCAIEAEAQKBIA"
    );
}

#[test]
fn encode_decode_recommended() -> Result<(), Box<dyn std::error::Error>> {
    let f = File::open("tests/DeckCodesTestData.txt").unwrap();
    let mut f = BufReader::new(f);

    let mut codes = vec![];
    let mut decks = vec![];

    loop {
        match f.by_ref().lines().next() {
            Some(code) => {
                codes.push(code?);
            }
            None => {
                break;
            }
        }

        let deck = f
            .by_ref()
            .lines()
            .map(|l| l.unwrap())
            .take_while(|l| !l.is_empty())
            .fold(Deck::new(), |mut deck, line| {
                let mut parts = line.rsplit(":");
                deck.add_from_data(
                    parts.next().unwrap(),
                    parts.next().unwrap().parse().unwrap(),
                )
                .unwrap();
                deck
            });

        decks.push(deck);
    }

    for (i, _deck) in decks.iter().enumerate() {
        assert_eq!(encoder::code_from_deck(&decks[i]).unwrap(), codes[i]);
        assert!(verify_rehydration(
            &encoder::deck_from_code(codes[i].to_string()).unwrap(),
            &decks[i]
        ));
    }

    Ok(())
}

#[test]
fn small_deck() {
    let deck = Deck::from_vec(vec![CardCodeAndCount::from_data("01DE002", 1).unwrap()]);

    let code = encoder::code_from_deck(&deck);
    let decoded_deck = encoder::deck_from_code(code.unwrap());
    assert_eq!(deck, decoded_deck.unwrap());
}

#[test]
fn large_deck() {
    let deck = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 3).unwrap(),
        CardCodeAndCount::from_data("01DE003", 3).unwrap(),
        CardCodeAndCount::from_data("01DE004", 3).unwrap(),
        CardCodeAndCount::from_data("01DE005", 3).unwrap(),
        CardCodeAndCount::from_data("01DE006", 3).unwrap(),
        CardCodeAndCount::from_data("01DE007", 3).unwrap(),
        CardCodeAndCount::from_data("01DE008", 3).unwrap(),
        CardCodeAndCount::from_data("01DE009", 3).unwrap(),
        CardCodeAndCount::from_data("01DE010", 3).unwrap(),
        CardCodeAndCount::from_data("01DE011", 3).unwrap(),
        CardCodeAndCount::from_data("01DE012", 3).unwrap(),
        CardCodeAndCount::from_data("01DE013", 3).unwrap(),
        CardCodeAndCount::from_data("01DE014", 3).unwrap(),
        CardCodeAndCount::from_data("01DE015", 3).unwrap(),
        CardCodeAndCount::from_data("01DE016", 3).unwrap(),
        CardCodeAndCount::from_data("01DE017", 3).unwrap(),
        CardCodeAndCount::from_data("01DE018", 3).unwrap(),
        CardCodeAndCount::from_data("01DE019", 3).unwrap(),
        CardCodeAndCount::from_data("01DE020", 3).unwrap(),
        CardCodeAndCount::from_data("01DE021", 3).unwrap(),
    ]);

    let code = encoder::code_from_deck(&deck);
    let decoded_deck = encoder::deck_from_code(code.unwrap());
    assert!(verify_rehydration(&deck, &decoded_deck.unwrap()));
}

#[test]
fn deck_with_counts_more_than_3_small() {
    let deck = Deck::from_vec(vec![CardCodeAndCount::from_data("01DE002", 4).unwrap()]);

    let code = encoder::code_from_deck(&deck);
    let decoded_deck = encoder::deck_from_code(code.unwrap());
    assert!(verify_rehydration(&deck, &decoded_deck.unwrap()));
}

#[test]
fn deck_with_more_than_3_large() {
    let deck = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 3).unwrap(),
        CardCodeAndCount::from_data("01DE003", 3).unwrap(),
        CardCodeAndCount::from_data("01DE004", 3).unwrap(),
        CardCodeAndCount::from_data("01DE005", 3).unwrap(),
        CardCodeAndCount::from_data("01DE006", 4).unwrap(),
        CardCodeAndCount::from_data("01DE007", 5).unwrap(),
        CardCodeAndCount::from_data("01DE008", 6).unwrap(),
        CardCodeAndCount::from_data("01DE009", 7).unwrap(),
        CardCodeAndCount::from_data("01DE010", 8).unwrap(),
        CardCodeAndCount::from_data("01DE011", 9).unwrap(),
        CardCodeAndCount::from_data("01DE012", 3).unwrap(),
        CardCodeAndCount::from_data("01DE013", 3).unwrap(),
        CardCodeAndCount::from_data("01DE014", 3).unwrap(),
        CardCodeAndCount::from_data("01DE015", 3).unwrap(),
        CardCodeAndCount::from_data("01DE016", 3).unwrap(),
        CardCodeAndCount::from_data("01DE017", 3).unwrap(),
        CardCodeAndCount::from_data("01DE018", 3).unwrap(),
        CardCodeAndCount::from_data("01DE019", 3).unwrap(),
        CardCodeAndCount::from_data("01DE020", 3).unwrap(),
        CardCodeAndCount::from_data("01DE021", 3).unwrap(),
    ]);

    let code = encoder::code_from_deck(&deck);
    let decoded_deck = encoder::deck_from_code(code.unwrap());
    assert!(verify_rehydration(&deck, &decoded_deck.unwrap()));
}

#[test]
fn single_card_40_times() {
    let deck = Deck::from_vec(vec![CardCodeAndCount::from_data("01DE002", 40).unwrap()]);

    let code = encoder::code_from_deck(&deck);
    let decoded_deck = encoder::deck_from_code(code.unwrap());
    assert!(verify_rehydration(&deck, &decoded_deck.unwrap()));
}

#[test]
fn worst_case_length() {
    let deck = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 4).unwrap(),
        CardCodeAndCount::from_data("01DE003", 4).unwrap(),
        CardCodeAndCount::from_data("01DE004", 4).unwrap(),
        CardCodeAndCount::from_data("01DE005", 4).unwrap(),
        CardCodeAndCount::from_data("01DE006", 4).unwrap(),
        CardCodeAndCount::from_data("01DE007", 5).unwrap(),
        CardCodeAndCount::from_data("01DE008", 6).unwrap(),
        CardCodeAndCount::from_data("01DE009", 7).unwrap(),
        CardCodeAndCount::from_data("01DE010", 8).unwrap(),
        CardCodeAndCount::from_data("01DE011", 9).unwrap(),
        CardCodeAndCount::from_data("01DE012", 4).unwrap(),
        CardCodeAndCount::from_data("01DE013", 4).unwrap(),
        CardCodeAndCount::from_data("01DE014", 4).unwrap(),
        CardCodeAndCount::from_data("01DE015", 4).unwrap(),
        CardCodeAndCount::from_data("01DE016", 4).unwrap(),
        CardCodeAndCount::from_data("01DE017", 4).unwrap(),
        CardCodeAndCount::from_data("01DE018", 4).unwrap(),
        CardCodeAndCount::from_data("01DE019", 4).unwrap(),
        CardCodeAndCount::from_data("01DE020", 4).unwrap(),
        CardCodeAndCount::from_data("01DE021", 4).unwrap(),
    ]);

    let code = encoder::code_from_deck(&deck);
    let decoded_deck = encoder::deck_from_code(code.unwrap());
    assert!(verify_rehydration(&deck, &decoded_deck.unwrap()));
}

#[test]
fn order_should_not_matter_1() {
    let deck1 = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 1).unwrap(),
        CardCodeAndCount::from_data("01DE003", 2).unwrap(),
        CardCodeAndCount::from_data("02DE003", 3).unwrap(),
    ]);

    let deck2 = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE003", 2).unwrap(),
        CardCodeAndCount::from_data("02DE003", 3).unwrap(),
        CardCodeAndCount::from_data("01DE002", 1).unwrap(),
    ]);

    assert_eq!(
        encoder::code_from_deck(&deck1).unwrap(),
        encoder::code_from_deck(&deck2).unwrap()
    );

    let deck3 = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 4).unwrap(),
        CardCodeAndCount::from_data("01DE003", 2).unwrap(),
        CardCodeAndCount::from_data("02DE003", 3).unwrap(),
    ]);

    let deck4 = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE003", 2).unwrap(),
        CardCodeAndCount::from_data("02DE003", 3).unwrap(),
        CardCodeAndCount::from_data("01DE002", 4).unwrap(),
    ]);

    assert_eq!(
        encoder::code_from_deck(&deck3).unwrap(),
        encoder::code_from_deck(&deck4).unwrap()
    );
}

#[test]
fn order_should_not_matter_2() {
    let deck1 = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 4).unwrap(),
        CardCodeAndCount::from_data("01DE003", 2).unwrap(),
        CardCodeAndCount::from_data("02DE003", 3).unwrap(),
        CardCodeAndCount::from_data("01DE004", 5).unwrap(),
    ]);

    let deck2 = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE004", 5).unwrap(),
        CardCodeAndCount::from_data("01DE003", 2).unwrap(),
        CardCodeAndCount::from_data("02DE003", 3).unwrap(),
        CardCodeAndCount::from_data("01DE002", 4).unwrap(),
    ]);

    assert_eq!(
        encoder::code_from_deck(&deck1).unwrap(),
        encoder::code_from_deck(&deck2).unwrap()
    );
}

#[test]
fn bilgewater_set() {
    let deck = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 4).unwrap(),
        CardCodeAndCount::from_data("02BW003", 2).unwrap(),
        CardCodeAndCount::from_data("02BW010", 3).unwrap(),
        CardCodeAndCount::from_data("01DE004", 5).unwrap(),
    ]);

    let code = encoder::code_from_deck(&deck);
    let decoded_deck = encoder::deck_from_code(code.unwrap());
    assert!(verify_rehydration(&deck, &decoded_deck.unwrap()));
}

#[test]
fn mttargon_set() {
    let deck = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 4).unwrap(),
        CardCodeAndCount::from_data("03MT003", 2).unwrap(),
        CardCodeAndCount::from_data("03MT010", 3).unwrap(),
        CardCodeAndCount::from_data("02BW004", 5).unwrap(),
    ]);

    let code = encoder::code_from_deck(&deck);
    let decoded_deck = encoder::deck_from_code(code.unwrap());
    assert!(verify_rehydration(&deck, &decoded_deck.unwrap()));
}

#[test]
fn bad_version() -> Result<(), Box<dyn std::error::Error>> {
    // make sure that a deck with an invalid version fails
    let deck = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01DE002", 4).unwrap(),
        CardCodeAndCount::from_data("01DE003", 2).unwrap(),
        CardCodeAndCount::from_data("02DE003", 3).unwrap(),
        CardCodeAndCount::from_data("01DE004", 5).unwrap(),
    ]);

    let mut bytes_from_deck =
        data_encoding::BASE32_NOPAD.decode(encoder::code_from_deck(&deck).unwrap().as_bytes())?;
    bytes_from_deck.remove(0);
    bytes_from_deck.insert(0, 88u8);

    let bad_version_code = data_encoding::BASE32_NOPAD.encode(&bytes_from_deck);
    let bad_deck = encoder::deck_from_code(bad_version_code);
    assert!(bad_deck.is_err());

    Ok(())
}

#[test]
fn bad_card_codes() {
    assert!(CardCodeAndCount::from_data("01DE02", 1).is_err());
    assert!(CardCodeAndCount::from_data("01YY002", 1).is_err());
    assert!(CardCodeAndCount::from_data("01DE002", 0).is_err());
}

#[test]
fn bad_count() {
    assert!(CardCodeAndCount::from_data("01DE002", 0).is_err());
    assert!(CardCodeAndCount::from_data("01DE002", -1).is_err());

    match CardCodeAndCount::from_data("01DE002", -1) {
        Err(LorError::InvalidCard) => assert!(true),
        _ => assert!(false),
    }
}

#[test]
fn garbage_decoding() {
    let bad_encoding_not_base32 = String::from("I'm no card code!");
    let bad_encoding32 = String::from("ABCDEFG");
    let bad_encoding_empty = String::from("");

    assert!(encoder::deck_from_code(&bad_encoding_not_base32).is_err());
    assert!(encoder::deck_from_code(&bad_encoding32).is_err());
    assert!(encoder::deck_from_code(&bad_encoding_empty).is_err());

    match encoder::deck_from_code(&bad_encoding_empty) {
        Err(LorError::InvalidCard) => assert!(true),
        _ => assert!(false),
    }
}

#[test]
fn deck_serialize_deserialize() {
    let deck = Deck::from_vec(vec![
        CardCodeAndCount::from_data("01SI015", 3).unwrap(),
        CardCodeAndCount::from_data("01SI044", 3).unwrap(),
        CardCodeAndCount::from_data("01SI048", 3).unwrap(),
        CardCodeAndCount::from_data("01SI054", 3).unwrap(),
        CardCodeAndCount::from_data("01FR003", 3).unwrap(),
        CardCodeAndCount::from_data("01FR012", 3).unwrap(),
        CardCodeAndCount::from_data("01FR020", 3).unwrap(),
        CardCodeAndCount::from_data("01FR024", 3).unwrap(),
        CardCodeAndCount::from_data("01FR033", 3).unwrap(),
        CardCodeAndCount::from_data("01FR036", 3).unwrap(),
        CardCodeAndCount::from_data("01FR039", 3).unwrap(),
        CardCodeAndCount::from_data("01FR052", 3).unwrap(),
        CardCodeAndCount::from_data("01SI005", 2).unwrap(),
        CardCodeAndCount::from_data("01FR004", 2).unwrap(),
    ]);
    let deck_json = serde_json::to_string(&deck);
    assert!(deck_json.is_ok());

    let code = encoder::code_from_deck(&deck);
    assert!(code.is_ok());

    let deck_from_json: Result<Deck, Error> = serde_json::from_str(&deck_json.unwrap());
    assert!(deck_from_json.is_ok());

    assert!(verify_rehydration(&deck, &deck_from_json.unwrap()));
}

fn verify_rehydration(d: &Deck, other: &Deck) -> bool {
    if d.cards().len() != other.cards().len() {
        return false;
    }

    for card_count in d.cards() {
        let res = other.cards().iter().find(|x| *x == card_count);
        if res.is_none() {
            return false;
        }
    }

    return true;
}