{-# LANGUAGE OverloadedStrings #-} -- | Core functionality for conversion between binary formats and PGP word -- lists. module Data.Text.PgpWordlist.Internal.Convert where import qualified Data.Text.PgpWordlist.Internal.AltList as Alt import Data.Text.PgpWordlist.Internal.Types import Data.Text.PgpWordlist.Internal.Word8Bimap import Data.Text.PgpWordlist.Internal.Words import qualified Data.ByteString.Lazy as BSL import Data.Text (Text) import qualified Data.Text as T import Data.Word -- | Inverse of 'fromText', modulo whitespace count. -- -- >>> toText (BSL.pack [104, 101, 108, 108, 111]) -- "frighten glossary glucose handiwork gremlin" toText :: BSL.ByteString -> Text toText = T.intercalate " " . Alt.toList . Alt.bimap (unEvenWord . toEvenWord) (unOddWord . toOddWord) . Alt.fromList . BSL.unpack -- | Convert a text of whitespace-separated words to their binary -- representation. The whitespace splitting behaviour is given by 'T.words'. -- -- >>> fromText (T.pack "frighten glossary glucose handiwork gremlin") -- Right "hello" -- -- Invalid words are recognized: -- -- >>> fromText (T.pack "frighten dragon glucose handiwork gremlin") -- Left (BadWord "dragon") -- -- Typical mistakes include accidentally swapping numbers, which leads to a -- parity error: -- -- >>> fromText (T.pack "frighten glucose glossary handiwork gremlin") -- Left (BadParity "glucose" 108) fromText :: Text -> Either TranslationError BSL.ByteString fromText = fmap (BSL.pack . Alt.toList) . Alt.bitraverse fromEvenWord fromOddWord . Alt.fromList . T.words -- | Look up the word corresponding to a byte. toEvenWord :: Word8 -> EvenWord toEvenWord = lookupL evenMap -- | Look up the word corresponding to a byte. toOddWord :: Word8 -> OddWord toOddWord = lookupL oddMap -- | Simple conversion, taking into account invalid words. fromEvenWord :: Text -> Either TranslationError Word8 fromEvenWord word = case lookupEvenOdd word of (Just byte, _x ) -> Right byte (Nothing , Just byte) -> Left (BadParity word byte) (Nothing , Nothing ) -> Left (BadWord word) -- | Simple conversion, taking into account invalid words. fromOddWord :: Text -> Either TranslationError Word8 fromOddWord word = case lookupEvenOdd word of (_x , Just byte) -> Right byte (Just byte, Nothing ) -> Left (BadParity word byte) (Nothing , Nothing ) -> Left (BadWord word) -- | Look up a word in both databases. lookupEvenOdd :: Text -> (Maybe Word8, Maybe Word8) lookupEvenOdd word = ( lookupR evenMap (EvenWord word) , lookupR oddMap (OddWord word) ) -- | Mapping from and to 'EvenWord's evenMap :: Word8Bimap EvenWord evenMap = unsafeConstruct (map pick12 wordList) where pick12 :: (a,b,c) -> (a,b) pick12 (i,e,_) = (i,e) -- | Mapping from and to 'OddWord's oddMap :: Word8Bimap OddWord oddMap = unsafeConstruct (map pick13 wordList) where pick13 :: (a,b,c) -> (a,c) pick13 (i,_,o) = (i,o)