XP5PZABSMS4EPHYEWH53VQULD5O2542EYAXRNSGIWSN4HNK36A7QC T4XHW5DFBYGXQ7DOYOFFMGJUHVIC2JXJSF6QWWOXFTMNGFV65GDAC DOPKLXQZP3TDISHODQNI6GZ57EYE42NG225WHSZMQ3L355YVYPNAC 4XD3XLRSTHAURHKAJF6LIJHUOTLZLJTF6OOLJWR4CNJTP6G7RE3AC DN2F55HBRBTHQAJDU4GOOGAEAXH5MIFPQSPEK2NNAPDJPZOOFJFQC J4QXG3NCNCHLF2Z7FERKXPBZKL45MPMZQ2XLKPLURAOIRGHGF7IQC SNYOEZI7JMTLJNLM2YTAHBPJEKK2BJZJDOHRIX5676JCF2VNET3QC data ReadError = IllegalFormat | RefineError !RefineException deriving (Show)
data ReadError= IllegalFormat| ParseFailure Text| RefineError !RefineExceptionderiving (Show)type RecordParser = StateT CategoryName Parserdata Record = Record{ recordName :: CardName, recordCopies :: PositiveNumber, recordCategory :: CategoryName}deriving (Show)cardNameP :: Parser CardNamecardNameP = doch <- anyCharremaining <- T.pack <$> manyTill anyChar (endOfLine <|> endOfInput)return . NonEmptyText . reallyUnsafeRefine $ T.cons ch remainingcopiesP :: Parser CopiescopiesP = donum <- decimalif num == 0then fail "not a positive number"else return . PositiveNumber . reallyUnsafeRefine $ numrecordP :: CategoryName -> Parser RecordrecordP recordCategory = dorecordCopies <- copiesPvoid $ optional $ char 'x'skipSpacerecordName <- cardNamePreturn $ Record {recordName, recordCopies, recordCategory}
readRecord :: Text -> Either [ReadError] (CardName, Copies)readRecord input =let toError (l, r) = case l ofNothing -> Left $ singleton IllegalFormatJust x -> Right (x, r)refined :: (Predicate p a) => a -> Either ReadError (Refined p a)refined = mapLeft RefineError . refinevalidate =arr (fmap PositiveNumber . refined) *** arr (fmap NonEmptyText . refined)>>> arr( \case(Right x, Right y) -> Right (x, y)(Left x, Left y) -> Left [x, y](Left x, _) -> Left $ singleton x(_, Left x) -> Left $ singleton x)toCard tup = (\(x, y) -> (y, x)) <$> validate tupin (toError . bimap (readMaybe @Natural) T.pack . word1 . T.unpack $ input)>>= toCard
decklistP :: RecordParser [Record]decklistP = domb <- lift $ manyTill (recordP mainboard) (endOfLine <|> endOfInput)lift skipSpace
let (mb, sb) =bimap(mapM (parseRecord mainboard) . T.lines)(mapM (parseRecord sideboard) . T.lines . T.stripStart). T.breakOn "\n\n"$ windowsToLinuxNewLines contentin liftA2 (++) mb sbwhereparseRecord cat = readRecord >=> \(c, q) -> return $ add c cat q
let parsed =mapLeft (singleton . ParseFailure . T.pack) . flip parseOnly content $(evalStateT decklistP mainboard <* endOfInput)in map (\Record {..} -> add recordName recordCategory recordCopies) <$> parsed