Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions Sources/ScryfallKit/Models/Card/Card+RelatedCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,30 @@ extension Card {
/// - Note: In the documentation of this struct, "this card" will refer to the `RelatedCard` object while "the original card" will refer to the `Card` object that contains this object
public struct RelatedCard: Codable, Identifiable, Hashable, Sendable {
/// The type of relationship
public enum Component: String, Codable, CaseIterable, Hashable, Sendable {
public enum Component: RawRepresentable, Codable, CaseIterable, Hashable, Sendable, Equatable {
/// This card is a token that's made by the original card
case token
/// This card melds with the original card
case meldPart = "meld_part"
case meldPart
/// This card is the result of melding the original card with its other half
case meldResult = "meld_result"
case meldResult
/// This card combos with the original card
case comboPiece = "combo_piece"
case comboPiece
/// A relationship that hasn't been added to ScryfallKit yet
case unknown(String)

/// All known relationships
public static let allCases: [Component] = [.token, .meldPart, .meldResult, .comboPiece]

public var rawValue: String {
switch self {
case .meldPart: "meld_part"
case .meldResult: "meld_result"
case .comboPiece: "combo_piece"
case .unknown(let unknownRawValue): unknownRawValue
default: String(describing: self)
}
}
}

/// The Scryfall ID of this card
Expand Down
14 changes: 13 additions & 1 deletion Sources/ScryfallKit/Models/Card/Card+Ruling.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,21 @@ extension Card {
}

/// A computer-readable string indicating which company produced this ruling
public enum Source: String, Codable, Sendable {
public enum Source: RawRepresentable, Codable, CaseIterable, Sendable, Equatable, Hashable {
case scryfall
case wotc
/// A source that hasn't been added to ScryfallKit yet
case unknown(String)

/// All known ruling sources
public static let allCases: [Source] = [.scryfall, .wotc]

public var rawValue: String {
switch self {
case .unknown(let unknownRawValue): unknownRawValue
default: String(describing: self)
}
}
}

/// A computer-readable string indicating which company produced this ruling
Expand Down
85 changes: 75 additions & 10 deletions Sources/ScryfallKit/Models/Card/Card+enums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,20 @@ extension Card {
}

/// Finishes for a printed card
public enum Finish: String, Codable, CaseIterable, Sendable {
public enum Finish: RawRepresentable, Codable, CaseIterable, Sendable, Equatable, Hashable {
case nonfoil, foil, etched, glossy
/// A finish that hasn't been added to ScryfallKit yet
case unknown(String)

/// All known finishes
public static let allCases: [Card.Finish] = [.nonfoil, .foil, .etched, .glossy]

public var rawValue: String {
switch self {
case .unknown(let unknownRawValue): unknownRawValue
default: String(describing: self)
}
}
}

/// Status of Scryfall's image asset for this card
Expand Down Expand Up @@ -146,14 +158,14 @@ extension Card {
/// [Scryfall documentation](https://scryfall.com/docs/api/layouts)
public enum Layout: RawRepresentable, CaseIterable, Codable, Sendable, Equatable, Hashable {
case normal, split, flip, transform, meld, leveler, saga, adventure, planar, scheme, vanguard,
token, emblem, augment, host, `class`, battle, `case`, mutate, prototype, modalDfc, doubleSided, doubleFacedToken, artSeries, reversibleCard
token, emblem, augment, host, `class`, battle, `case`, mutate, prototype, prepare, modalDfc, doubleSided, doubleFacedToken, artSeries, reversibleCard, frontCard

/// A layout that hasn't been added to ScryfallKit yet
case unknown(String)

/// All known Magic: the Gathering card layouts
public static let allCases: [Card.Layout] = [
.normal, .split, .flip, .transform, .meld, .leveler, .saga, .adventure, .planar, .scheme, .vanguard, .token, .emblem, .augment, .host, .class, .battle, .case, .mutate, .prototype, .modalDfc, .doubleSided, .doubleFacedToken, .artSeries, .reversibleCard,
.normal, .split, .flip, .transform, .meld, .leveler, .saga, .adventure, .planar, .scheme, .vanguard, .token, .emblem, .augment, .host, .class, .battle, .case, .mutate, .prototype, .prepare, .modalDfc, .doubleSided, .doubleFacedToken, .artSeries, .reversibleCard, .frontCard,
]

public var rawValue: String {
Expand All @@ -162,6 +174,7 @@ extension Card {
case .doubleSided: "double_sided"
case .doubleFacedToken: "double_faced_token"
case .artSeries: "art_series"
case .frontCard: "front_card"
case .reversibleCard: "reversible_card"
case .unknown(let string): string
default: String(describing: self)
Expand Down Expand Up @@ -201,20 +214,72 @@ extension Card {
}
}

/// A value that a card can produce, as reported by `producedMana`
///
/// As of this writing, only one card (Unfinity's Sole Performer) can produce "mana" that isn't a
/// normal color, so this enumeration is split from the core Color enumeration for convenience in
/// the overwhelmingly common cases.
public enum ProducedColor: RawRepresentable, Codable, CaseIterable, Sendable, Equatable, Hashable
{
// swiftlint:disable:next identifier_name
case W, U, B, R, G, C
/// A produced value that isn't a color, or that hasn't been added to ScryfallKit yet
case unknown(String)

/// All the produced values that are also colors, in the order Scryfall sorts them
public static let allCases: [Card.ProducedColor] = [.W, .U, .B, .R, .G, .C]

public var rawValue: String {
switch self {
case .unknown(let unknownRawValue): unknownRawValue
default: String(describing: self)
}
}

/// The equivalent ``Card/Color``, or nil if this value isn't one of Magic's colors
public var color: Color? { Color(rawValue: rawValue) }
}

/// Card border colors
public enum BorderColor: String, Codable, CaseIterable, Sendable {
public enum BorderColor: RawRepresentable, Codable, CaseIterable, Sendable, Equatable, Hashable {
case black, borderless, gold, silver, white, yellow
/// A border color that hasn't been added to ScryfallKit yet
case unknown(String)

/// All known border colors
public static let allCases: [Card.BorderColor] = [
.black, .borderless, .gold, .silver, .white, .yellow,
]

public var rawValue: String {
switch self {
case .unknown(let unknownRawValue): unknownRawValue
default: String(describing: self)
}
}
}

/// Card frames
///
/// [Scryfall documentation](https://scryfall.com/docs/api/frames)
public enum Frame: String, Codable, CaseIterable, Sendable {
case v1993 = "1993"
case v1997 = "1997"
case v2003 = "2003"
case v2015 = "2015"
case future
public enum Frame: RawRepresentable, Codable, CaseIterable, Sendable, Equatable, Hashable {
case v1993, v1997, v2003, v2015, future
/// A frame that hasn't been added to ScryfallKit yet
case unknown(String)

/// All known frames
public static let allCases: [Card.Frame] = [.v1993, .v1997, .v2003, .v2015, .future]

public var rawValue: String {
switch self {
case .v1993: "1993"
case .v1997: "1997"
case .v2003: "2003"
case .v2015: "2015"
case .unknown(let unknownRawValue): unknownRawValue
default: String(describing: self)
}
}
}

/// Effects applied to a Magic card frame
Expand Down
4 changes: 2 additions & 2 deletions Sources/ScryfallKit/Models/Card/Card.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public struct Card: Codable, Identifiable, Hashable, Sendable {
/// The power of this card if it's a creature
public var power: String?
/// The colors of mana that this card _could_ produce
public var producedMana: [Color]?
public var producedMana: [ProducedColor]?
/// True if this card is on the Reserved List
public var reserved: Bool
/// The toughness of this card if it's a creature
Expand Down Expand Up @@ -238,7 +238,7 @@ public struct Card: Codable, Identifiable, Hashable, Sendable {
pennyRank: Int? = nil,
oversized: Bool,
power: String? = nil,
producedMana: [Color]? = nil,
producedMana: [ProducedColor]? = nil,
reserved: Bool,
toughness: String? = nil,
typeLine: String? = nil,
Expand Down
14 changes: 13 additions & 1 deletion Sources/ScryfallKit/Models/Enums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,20 @@
import Foundation

/// Environments to play Magic: The Gathering in
public enum Game: String, Codable, CaseIterable, Sendable {
public enum Game: RawRepresentable, Codable, CaseIterable, Sendable, Equatable, Hashable {
case paper, mtgo, arena, astral, sega
/// A game that hasn't been added to ScryfallKit yet
case unknown(String)

/// All known games
public static let allCases: [Game] = [.paper, .mtgo, .arena, .astral, .sega]

public var rawValue: String {
switch self {
case .unknown(let unknownRawValue): unknownRawValue
default: String(describing: self)
}
}
}

/// Comparison strategies for determining what makes a card "unique"
Expand Down
7 changes: 7 additions & 0 deletions Sources/ScryfallKit/Models/UnknownDecodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@ extension UnknownDecodable {
}
}

extension Card.BorderColor: UnknownDecodable {}
extension Card.Finish: UnknownDecodable {}
extension Card.Frame: UnknownDecodable {}
extension Card.FrameEffect: UnknownDecodable {}
extension Card.ProducedColor: UnknownDecodable {}
extension Card.Layout: UnknownDecodable {}
extension Card.RelatedCard.Component: UnknownDecodable {}
extension Card.Ruling.Source: UnknownDecodable {}
extension Card.SecurityStamp: UnknownDecodable {}
extension Game: UnknownDecodable {}
extension MTGSet.Kind: UnknownDecodable {}
111 changes: 111 additions & 0 deletions Tests/ScryfallKitTests/CaseIterableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,13 @@ final class CaseIterableTests: XCTestCase {
case .case: Card.Layout.allCases.contains(.case)
case .mutate: Card.Layout.allCases.contains(.mutate)
case .prototype: Card.Layout.allCases.contains(.prototype)
case .prepare: Card.Layout.allCases.contains(.prepare)
case .modalDfc: Card.Layout.allCases.contains(.modalDfc)
case .doubleSided: Card.Layout.allCases.contains(.doubleSided)
case .doubleFacedToken: Card.Layout.allCases.contains(.doubleFacedToken)
case .artSeries: Card.Layout.allCases.contains(.artSeries)
case .reversibleCard: Card.Layout.allCases.contains(.reversibleCard)
case .frontCard: Card.Layout.allCases.contains(.frontCard)
case .unknown(let string):
// Unknown case shouldn't be in allCases
!Card.Layout.allCases.contains(.unknown(string))
Expand All @@ -110,6 +112,115 @@ final class CaseIterableTests: XCTestCase {
XCTAssertTrue(contains)
}

func testFinish() {
let stub = Card.Finish.foil
let contains = switch stub {
case .nonfoil: Card.Finish.allCases.contains(.nonfoil)
case .foil: Card.Finish.allCases.contains(.foil)
case .etched: Card.Finish.allCases.contains(.etched)
case .glossy: Card.Finish.allCases.contains(.glossy)
case .unknown(let string):
// Unknown case shouldn't be in allCases
!Card.Finish.allCases.contains(.unknown(string))
}

XCTAssertTrue(contains)
}

func testBorderColor() {
let stub = Card.BorderColor.black
let contains = switch stub {
case .black: Card.BorderColor.allCases.contains(.black)
case .borderless: Card.BorderColor.allCases.contains(.borderless)
case .gold: Card.BorderColor.allCases.contains(.gold)
case .silver: Card.BorderColor.allCases.contains(.silver)
case .white: Card.BorderColor.allCases.contains(.white)
case .yellow: Card.BorderColor.allCases.contains(.yellow)
case .unknown(let string):
// Unknown case shouldn't be in allCases
!Card.BorderColor.allCases.contains(.unknown(string))
}

XCTAssertTrue(contains)
}

func testFrame() {
let stub = Card.Frame.v2015
let contains = switch stub {
case .v1993: Card.Frame.allCases.contains(.v1993)
case .v1997: Card.Frame.allCases.contains(.v1997)
case .v2003: Card.Frame.allCases.contains(.v2003)
case .v2015: Card.Frame.allCases.contains(.v2015)
case .future: Card.Frame.allCases.contains(.future)
case .unknown(let string):
// Unknown case shouldn't be in allCases
!Card.Frame.allCases.contains(.unknown(string))
}

XCTAssertTrue(contains)
}

func testGame() {
let stub = Game.paper
let contains = switch stub {
case .paper: Game.allCases.contains(.paper)
case .mtgo: Game.allCases.contains(.mtgo)
case .arena: Game.allCases.contains(.arena)
case .astral: Game.allCases.contains(.astral)
case .sega: Game.allCases.contains(.sega)
case .unknown(let string):
// Unknown case shouldn't be in allCases
!Game.allCases.contains(.unknown(string))
}

XCTAssertTrue(contains)
}

func testComponent() {
let stub = Card.RelatedCard.Component.token
let contains = switch stub {
case .token: Card.RelatedCard.Component.allCases.contains(.token)
case .meldPart: Card.RelatedCard.Component.allCases.contains(.meldPart)
case .meldResult: Card.RelatedCard.Component.allCases.contains(.meldResult)
case .comboPiece: Card.RelatedCard.Component.allCases.contains(.comboPiece)
case .unknown(let string):
// Unknown case shouldn't be in allCases
!Card.RelatedCard.Component.allCases.contains(.unknown(string))
}

XCTAssertTrue(contains)
}

func testRulingSource() {
let stub = Card.Ruling.Source.wotc
let contains = switch stub {
case .scryfall: Card.Ruling.Source.allCases.contains(.scryfall)
case .wotc: Card.Ruling.Source.allCases.contains(.wotc)
case .unknown(let string):
// Unknown case shouldn't be in allCases
!Card.Ruling.Source.allCases.contains(.unknown(string))
}

XCTAssertTrue(contains)
}

func testProducedColor() {
let stub = Card.ProducedColor.W
let contains = switch stub {
case .W: Card.ProducedColor.allCases.contains(.W)
case .U: Card.ProducedColor.allCases.contains(.U)
case .B: Card.ProducedColor.allCases.contains(.B)
case .R: Card.ProducedColor.allCases.contains(.R)
case .G: Card.ProducedColor.allCases.contains(.G)
case .C: Card.ProducedColor.allCases.contains(.C)
case .unknown(let string):
// Unknown case shouldn't be in allCases
!Card.ProducedColor.allCases.contains(.unknown(string))
}

XCTAssertTrue(contains)
}

func testSecurityStamp() {
let stub = Card.SecurityStamp.oval
let contains = switch stub {
Expand Down
Loading
Loading