bottom (empty) → 0.1.0.0
raw patch · 9 files changed
+675/−0 lines, 9 filesdep +QuickCheckdep +basedep +bottomsetup-changed
Dependencies added: QuickCheck, base, bottom, bytestring, criterion, deepseq, hspec, megaparsec, optparse-applicative, text
Files
- CHANGELOG.md +3/−0
- LICENSE +190/−0
- README.md +54/−0
- Setup.hs +6/−0
- bench/Main.hs +57/−0
- bottom.cabal +91/−0
- cmd/bottom/Main.hs +71/−0
- src/Data/Encoding/Bottom.hs +155/−0
- test/Main.hs +48/−0
+ CHANGELOG.md view
@@ -0,0 +1,3 @@+# 0.1.0.0 (2021-03-21)++Initial release.
+ LICENSE view
@@ -0,0 +1,190 @@+ Apache License+ Version 2.0, January 2004+ https://www.apache.org/licenses/++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++ 1. Definitions.++ "License" shall mean the terms and conditions for use, reproduction,+ and distribution as defined by Sections 1 through 9 of this document.++ "Licensor" shall mean the copyright owner or entity authorized by+ the copyright owner that is granting the License.++ "Legal Entity" shall mean the union of the acting entity and all+ other entities that control, are controlled by, or are under common+ control with that entity. For the purposes of this definition,+ "control" means (i) the power, direct or indirect, to cause the+ direction or management of such entity, whether by contract or+ otherwise, or (ii) ownership of fifty percent (50%) or more of the+ outstanding shares, or (iii) beneficial ownership of such entity.++ "You" (or "Your") shall mean an individual or Legal Entity+ exercising permissions granted by this License.++ "Source" form shall mean the preferred form for making modifications,+ including but not limited to software source code, documentation+ source, and configuration files.++ "Object" form shall mean any form resulting from mechanical+ transformation or translation of a Source form, including but+ not limited to compiled object code, generated documentation,+ and conversions to other media types.++ "Work" shall mean the work of authorship, whether in Source or+ Object form, made available under the License, as indicated by a+ copyright notice that is included in or attached to the work+ (an example is provided in the Appendix below).++ "Derivative Works" shall mean any work, whether in Source or Object+ form, that is based on (or derived from) the Work and for which the+ editorial revisions, annotations, elaborations, or other modifications+ represent, as a whole, an original work of authorship. For the purposes+ of this License, Derivative Works shall not include works that remain+ separable from, or merely link (or bind by name) to the interfaces of,+ the Work and Derivative Works thereof.++ "Contribution" shall mean any work of authorship, including+ the original version of the Work and any modifications or additions+ to that Work or Derivative Works thereof, that is intentionally+ submitted to Licensor for inclusion in the Work by the copyright owner+ or by an individual or Legal Entity authorized to submit on behalf of+ the copyright owner. For the purposes of this definition, "submitted"+ means any form of electronic, verbal, or written communication sent+ to the Licensor or its representatives, including but not limited to+ communication on electronic mailing lists, source code control systems,+ and issue tracking systems that are managed by, or on behalf of, the+ Licensor for the purpose of discussing and improving the Work, but+ excluding communication that is conspicuously marked or otherwise+ designated in writing by the copyright owner as "Not a Contribution."++ "Contributor" shall mean Licensor and any individual or Legal Entity+ on behalf of whom a Contribution has been received by Licensor and+ subsequently incorporated within the Work.++ 2. Grant of Copyright License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ copyright license to reproduce, prepare Derivative Works of,+ publicly display, publicly perform, sublicense, and distribute the+ Work and such Derivative Works in Source or Object form.++ 3. Grant of Patent License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ (except as stated in this section) patent license to make, have made,+ use, offer to sell, sell, import, and otherwise transfer the Work,+ where such license applies only to those patent claims licensable+ by such Contributor that are necessarily infringed by their+ Contribution(s) alone or by combination of their Contribution(s)+ with the Work to which such Contribution(s) was submitted. If You+ institute patent litigation against any entity (including a+ cross-claim or counterclaim in a lawsuit) alleging that the Work+ or a Contribution incorporated within the Work constitutes direct+ or contributory patent infringement, then any patent licenses+ granted to You under this License for that Work shall terminate+ as of the date such litigation is filed.++ 4. Redistribution. You may reproduce and distribute copies of the+ Work or Derivative Works thereof in any medium, with or without+ modifications, and in Source or Object form, provided that You+ meet the following conditions:++ (a) You must give any other recipients of the Work or+ Derivative Works a copy of this License; and++ (b) You must cause any modified files to carry prominent notices+ stating that You changed the files; and++ (c) You must retain, in the Source form of any Derivative Works+ that You distribute, all copyright, patent, trademark, and+ attribution notices from the Source form of the Work,+ excluding those notices that do not pertain to any part of+ the Derivative Works; and++ (d) If the Work includes a "NOTICE" text file as part of its+ distribution, then any Derivative Works that You distribute must+ include a readable copy of the attribution notices contained+ within such NOTICE file, excluding those notices that do not+ pertain to any part of the Derivative Works, in at least one+ of the following places: within a NOTICE text file distributed+ as part of the Derivative Works; within the Source form or+ documentation, if provided along with the Derivative Works; or,+ within a display generated by the Derivative Works, if and+ wherever such third-party notices normally appear. The contents+ of the NOTICE file are for informational purposes only and+ do not modify the License. You may add Your own attribution+ notices within Derivative Works that You distribute, alongside+ or as an addendum to the NOTICE text from the Work, provided+ that such additional attribution notices cannot be construed+ as modifying the License.++ You may add Your own copyright statement to Your modifications and+ may provide additional or different license terms and conditions+ for use, reproduction, or distribution of Your modifications, or+ for any such Derivative Works as a whole, provided Your use,+ reproduction, and distribution of the Work otherwise complies with+ the conditions stated in this License.++ 5. Submission of Contributions. Unless You explicitly state otherwise,+ any Contribution intentionally submitted for inclusion in the Work+ by You to the Licensor shall be under the terms and conditions of+ this License, without any additional terms or conditions.+ Notwithstanding the above, nothing herein shall supersede or modify+ the terms of any separate license agreement you may have executed+ with Licensor regarding such Contributions.++ 6. Trademarks. This License does not grant permission to use the trade+ names, trademarks, service marks, or product names of the Licensor,+ except as required for reasonable and customary use in describing the+ origin of the Work and reproducing the content of the NOTICE file.++ 7. Disclaimer of Warranty. Unless required by applicable law or+ agreed to in writing, Licensor provides the Work (and each+ Contributor provides its Contributions) on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+ implied, including, without limitation, any warranties or conditions+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+ PARTICULAR PURPOSE. You are solely responsible for determining the+ appropriateness of using or redistributing the Work and assume any+ risks associated with Your exercise of permissions under this License.++ 8. Limitation of Liability. In no event and under no legal theory,+ whether in tort (including negligence), contract, or otherwise,+ unless required by applicable law (such as deliberate and grossly+ negligent acts) or agreed to in writing, shall any Contributor be+ liable to You for damages, including any direct, indirect, special,+ incidental, or consequential damages of any character arising as a+ result of this License or out of the use or inability to use the+ Work (including but not limited to damages for loss of goodwill,+ work stoppage, computer failure or malfunction, or any and all+ other commercial damages or losses), even if such Contributor+ has been advised of the possibility of such damages.++ 9. Accepting Warranty or Additional Liability. While redistributing+ the Work or Derivative Works thereof, You may choose to offer,+ and charge a fee for, acceptance of support, warranty, indemnity,+ or other liability obligations and/or rights consistent with this+ License. However, in accepting such obligations, You may act only+ on Your own behalf and on Your sole responsibility, not on behalf+ of any other Contributor, and only if You agree to indemnify,+ defend, and hold each Contributor harmless for any liability+ incurred by, or claims asserted against, such Contributor by reason+ of your accepting any such warranty or additional liability.++ END OF TERMS AND CONDITIONS++ Copyright 2021 Leo Zhang++ Licensed under the Apache License, Version 2.0 (the "License");+ you may not use this file except in compliance with the License.+ You may obtain a copy of the License at++ https://www.apache.org/licenses/LICENSE-2.0++ Unless required by applicable law or agreed to in writing, software+ distributed under the License is distributed on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ See the License for the specific language governing permissions and+ limitations under the License.
+ README.md view
@@ -0,0 +1,54 @@+# bottom-hs++[](https://github.com/bottom-software-foundation/bottom-hs/actions/workflows/main.yml)+[](https://hackage.haskell.org/package/bottom)+[](https://hackage.haskell.org/package/bottom)++Based off of the hilarious [bottom-rs](https://github.com/bottom-software-foundation/bottom-rs), built to the [Bottom spec](https://github.com/bottom-software-foundation/spec).++++## Using the CLI++```sh+$ cabal run bottom -- --help++Bottom translator 0.1.0++Usage: bottom ((-b|--bottomify) | (-r|--regress) | (-V|--version)) <TEXT>+ Fantastic (maybe) CLI for translating between bottom and human-readable text++Available options:+ -b,--bottomify Translate text to bottom+ -r,--regress Translate bottom to human-readable text (futile)+ -V,--version Prints version information+ -h,--help Show this help text+```+```sh+$ cabal run bottom -- --regress 🫂✨✨🥺,,👉👈💖💖✨✨🥺,,,,👉👈💖💖✨✨✨✨👉👈🫂✨✨🥺,,👉👈💖💖✨✨✨👉👈💖💖✨✨✨✨🥺,,👉👈🫂✨✨🥺,,👉👈💖💖✨✨🥺,,,,👉👈💖💖💖✨✨🥺,👉👈🫂✨✨🥺,,👉👈💖💖✨✨✨👉👈 💖💖✨✨✨✨👉👈+がんばれ+```+```sh+$ cabal run bottom -- --bottomify Test+💖✨✨✨,,,,👉👈💖💖,👉👈💖💖✨🥺👉👈💖💖✨🥺,👉👈+```++## Usage as a library++See the generate Haddock documentation in Hackage for module documentation.++## Development++Useful commands:++- `cabal run bottom -- --help`: Runs the CLI.+- `cabal repl`: Run the library in the REPL. Loads `Data.Encoding.Bottom` with `-XOverloadedStrings` by default (see `.ghci`).+- `cabal test --enable-coverage`: Run tests, with test coverage.+- `cabal bench`: Run benchmarks.+- `cabal haddock --haddock-all --enable-documentation`: Generate documentation.+- `ormolu --mode inplace $(find . -path ./dist-newstyle -prune -false -o -type f -name '*.hs')`: Format code (see [`ormolu`](https://github.com/tweag/ormolu)).+- `cabal-fmt -i bottom.cabal`: Format `.cabal` file (see [`cabal-fmt`](https://github.com/phadej/cabal-fmt)).++## License++Apache 2.
+ Setup.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import Distribution.Simple++main :: IO ()+main = defaultMain
+ bench/Main.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Control.DeepSeq (NFData)+import Criterion.Main (Benchmark, bench, bgroup, defaultMain, nf)+import Data.Encoding.Bottom (decode, encode)+import Data.Text (Text)+import qualified Data.Text as T++main :: IO ()+main =+ defaultMain+ [ bgroup "encoding" encodeBenches,+ bgroup "decoding" decodeBenches+ ]+ where+ -- Taken from https://github.com/bottom-software-foundation/bottom-rs/blob/need_top/bench/src/bench.rs+ examples :: [(String, Text)]+ examples =+ [ ("single", "a"),+ ("short", "abcd"),+ ( "long",+ T.pack $+ unlines+ [ "What the fuck did you just fucking say about me, you",+ "little bitch? I’ll have you know I graduated top of my class in the Navy",+ "Seals, and I’ve been involved in secret raids on Al-Quaeda, and I have over",+ "300 confirmed kills. I am trained in gorilla warfare and I’m the top sniper",+ "in the entire US armed forces. You are nothing to me but just another",+ "target. I will wipe you out with precision the likes of which has never been",+ "seen before on this Earth, mark my words. You think you can get away with",+ "saying shit to me over the Internet? Think again, fucker. As we speak I am",+ "contacting my network of spies across the USA and your IP is being traced",+ "right now so you better prepare for the storm, maggot. The storm that wipes",+ "out the pathetic little thing you call your life. You’re fucking dead, kid.",+ "I can be anywhere, anytime, and I can kill you in over seven hundred ways,",+ "and that’s just with my bare hands. Not only am I extensively trained in",+ "unarmed combat, but I have access to the entire arsenal of the United States",+ "Marine Corps and I will use it to its full extent to wipe your ass off the",+ "face of the continent, you little shit. If only you could have known what",+ "unholy retribution your little “clever” comment was about to bring down upon",+ "you, maybe you would have held your tongue. You didn’t, and now you’re",+ "paying the price, you goddamn idiot. I will shit all over you and you will",+ "drown in it. You’re fucking dead, kiddo."+ ]+ )+ ]++ makeBench :: (NFData b) => (a -> b) -> (Text -> a) -> (String, Text) -> Benchmark+ makeBench f transform (name, input) = bench name $ nf f $ transform input++ encodeBenches :: [Benchmark]+ encodeBenches = makeBench encode id <$> examples++ decodeBenches :: [Benchmark]+ decodeBenches = makeBench decode encode <$> examples
+ bottom.cabal view
@@ -0,0 +1,91 @@+cabal-version: 3.0+name: bottom+version: 0.1.0.0+synopsis: Encoding and decoding for the Bottom spec.+description:+ Encoding and decoding for the [Bottom spec](https://github.com/bottom-software-foundation/spec).++license: Apache-2.0+license-file: LICENSE+author: Leo Zhang+maintainer: leo@leozhang.me+copyright: 2021 Leo Zhang+category: Codec+build-type: Simple+homepage: https://github.com/bottom-software-foundation/bottom-hs+bug-reports:+ https://github.com/bottom-software-foundation/bottom-hs/issues++tested-with: GHC ==8.10.4+extra-source-files:+ CHANGELOG.md+ README.md++source-repository head+ type: git+ location: git://github.com/bottom-software-foundation/bottom-hs.git++common lang+ build-depends: base ^>=4.14+ default-language: Haskell2010+ ghc-options:+ -Wall -Wincomplete-uni-patterns -Wcompat+ -Wincomplete-record-updates -Wmissing-home-modules+ -Wmissing-export-lists -Wredundant-constraints++common deps+ build-depends:+ , bytestring ^>=0.11.1.0+ , text ^>=1.2.4.1++library+ import: lang+ import: deps+ hs-source-dirs: src++ -- cabal-fmt: expand src+ exposed-modules: Data.Encoding.Bottom+ build-depends:+ , deepseq ^>=1.4.4.0+ , megaparsec ^>=9.0.1++test-suite tests+ import: lang+ import: deps+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs++ -- cabal-fmt: expand test -Main+ other-modules:+ build-depends:+ , bottom+ , hspec ^>=2.7.8+ , QuickCheck ^>=2.14.2++benchmark bench+ import: lang+ import: deps+ type: exitcode-stdio-1.0+ hs-source-dirs: bench+ main-is: Main.hs++ -- cabal-fmt expand bench -Main+ other-modules:+ build-depends:+ , bottom+ , criterion ^>=1.5.9.0+ , deepseq ^>=1.4.4.0++executable bottom+ import: lang+ import: deps+ ghc-options: -threaded -with-rtsopts=-N+ hs-source-dirs: cmd/bottom+ main-is: Main.hs++ -- cabal-fmt: expand cmd/bottom -Main+ other-modules:+ build-depends:+ , bottom+ , optparse-applicative ^>=0.16.1.0
+ cmd/bottom/Main.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Data.Encoding.Bottom (decode', encode, unBottom)+import Data.Text (Text)+import qualified Data.Text as T+import Data.Text.Encoding (decodeUtf8, encodeUtf8)+import Options.Applicative+ ( Parser,+ ParserInfo,+ argument,+ execParser,+ flag',+ fullDesc,+ header,+ help,+ helper,+ info,+ long,+ metavar,+ progDesc,+ short,+ str,+ (<**>),+ (<|>),+ )++version :: String+version = "0.1.0"++data Command+ = Bottomify+ | Regress+ | Version+ deriving (Show)++commandParser :: Parser Command+commandParser = bottomifyParser <|> regressParser <|> versionParser+ where+ bottomifyParser = flag' Bottomify $ long "bottomify" <> short 'b' <> help "Translate text to bottom"+ regressParser = flag' Regress $ long "regress" <> short 'r' <> help "Translate bottom to human-readable text (futile)"+ versionParser = flag' Version $ long "version" <> short 'V' <> help "Prints version information"++data Options = Options+ { mode :: Command,+ text :: Text+ }+ deriving (Show)++optionsParser :: Parser Options+optionsParser = Options <$> commandParser <*> textParser+ where+ textParser = argument str (metavar "<TEXT>")++opts :: ParserInfo Options+opts =+ info+ (optionsParser <**> helper)+ $ fullDesc <> progDesc "Fantastic (maybe) CLI for translating between bottom and human-readable text" <> header ("Bottom translator " <> version)++main :: IO ()+main = do+ Options {mode, text} <- execParser opts+ case mode of+ Version -> putStrLn $ "Bottom translator " <> version+ Bottomify -> putStrLn $ T.unpack $ decodeUtf8 $ unBottom $ encode text+ Regress -> case decode' $ encodeUtf8 text of+ Right decoded -> putStrLn $ T.unpack decoded+ Left err -> putStrLn $ T.unpack err
+ src/Data/Encoding/Bottom.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE StrictData #-}++-- | Encodes and decodes 'Text's to 'Bottom's. For details, see the+-- [Bottom spec](https://github.com/bottom-software-foundation/spec).+module Data.Encoding.Bottom+ ( Bottom,+ unBottom,+ encode,+ decode,+ decode',+ )+where++import Control.DeepSeq (NFData)+import Control.Monad (void)+import Data.Bits (zeroBits)+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.Foldable as F+import Data.List (unfoldr)+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE+import Data.Text (Text)+import qualified Data.Text as T+import Data.Text.Encoding (decodeUtf8, encodeUtf8)+import Data.Void (Void)+import Data.Word (Word8)+import Text.Megaparsec (Parsec, Token, chunk, eof, manyTill, runParser, someTill, (<|>))+import Text.Megaparsec.Error (ErrorItem (..), ParseError (..), ParseErrorBundle (..), errorBundlePretty)++-- | A 'Bottom' is a wrapper around well-formed, Bottom-encoded 'ByteString'.+-- Its instances are derived from those of 'ByteString'.+newtype Bottom = Bottom ByteString+ deriving (Show, Eq, Ord, Semigroup, Monoid, NFData)++-- | 'unBottom' unwraps the underlying 'ByteString'.+unBottom :: Bottom -> ByteString+unBottom (Bottom bs) = bs++-- Value characters for encoding.+singleton :: Char -> ByteString+singleton = encodeUtf8 . T.singleton++twoHundred :: ByteString+twoHundred = singleton '\x1FAC2'++fifty :: ByteString+fifty = singleton '\x1F496'++ten :: ByteString+ten = singleton '\x2728'++five :: ByteString+five = singleton '\x1F97A'++one :: ByteString+one = singleton '\x002C'++zero :: ByteString+zero = encodeUtf8 $ T.pack ['\x2764', '\xFE0F']++separator :: ByteString+separator = encodeUtf8 $ T.pack ['\x1F449', '\x1F448']++-- Decoding functions.++-- | 'decode' decodes a 'Bottom' into its corresponding Unicode 'Text'.+decode :: Bottom -> Text+decode (Bottom bs) = case decode' bs of+ Right r -> r+ Left err -> error "Data.Encoding.Bottom.decode: malformed Bottom: " <> err++type Parser = Parsec Void ByteString++-- | 'decode'' decodes an arbitrary Bottom-encoded 'ByteString' into a 'Text',+-- or returns a parse error if the 'ByteString' is malformed.+decode' :: ByteString -> Either Text Text+decode' bs = case runParser bottomParser "" bs of+ Left err -> Left $ renderError err+ Right r -> Right r+ where+ bottomParser :: Parser Text+ bottomParser = decodeUtf8 . BS.pack <$> (fmap . fmap) toEnum (groupParser `manyTill` eof)++ groupParser :: Parser Int+ groupParser = parseNull <|> parseValues+ where+ parseNull = zeroParser >> separatorParser >> return 0+ parseValues = sum <$> (twoHundredParser <|> fiftyParser <|> tenParser <|> fiveParser <|> oneParser) `someTill` separatorParser++ twoHundredParser :: Parser Int+ twoHundredParser = chunk twoHundred >> return 200++ fiftyParser :: Parser Int+ fiftyParser = chunk fifty >> return 50++ tenParser :: Parser Int+ tenParser = chunk ten >> return 10++ fiveParser :: Parser Int+ fiveParser = chunk five >> return 5++ oneParser :: Parser Int+ oneParser = chunk one >> return 1++ zeroParser :: Parser Int+ zeroParser = chunk zero >> return 0++ separatorParser :: Parser ()+ separatorParser = void $ chunk separator++ -- Custom error messages, because the default error messages print raw+ -- ByteStrings and don't render correctly.+ renderError :: ParseErrorBundle ByteString Void -> Text+ renderError ParseErrorBundle {bundleErrors = (TrivialError offset unexpected expected) :| []} =+ unexpectedMessage <> expectedMessage <> " at offset " <> T.pack (show offset)+ where+ renderErrorItem :: ErrorItem (Token ByteString) -> Text+ renderErrorItem (Tokens tokens) = "\"" <> decodeUtf8 (BS.pack $ NE.toList tokens) <> "\""+ renderErrorItem (Label name) = T.pack $ NE.toList name+ renderErrorItem EndOfInput = "end of input"++ unexpectedMessage = case unexpected of+ Just unx -> "unexpected " <> renderErrorItem unx <> ": "+ Nothing -> ""++ expectedMessage = "expected " <> (if length expecteds >= 2 then "one of " else "") <> T.intercalate ", " expecteds+ where+ expecteds = renderErrorItem <$> F.toList expected+ renderError err = T.pack $ errorBundlePretty err++-- Encoding functions.++-- | 'encode' takes a 'Text', and encodes it into a 'Bottom'. To get at the+-- underlying 'ByteString', unwrap the returned value with 'unBottom'.+encode :: Text -> Bottom+encode = Bottom . BS.concatMap encodeByte . encodeUtf8++encodeByte :: Word8 -> ByteString+encodeByte b+ | b == zeroBits = zero <> separator+ | otherwise = BS.concat (unfoldr encodeByte' (fromEnum b)) <> separator+ where+ encodeByte' :: Int -> Maybe (ByteString, Int)+ encodeByte' n+ | n >= 200 = Just (twoHundred, n - 200)+ | n >= 50 = Just (fifty, n - 50)+ | n >= 10 = Just (ten, n - 10)+ | n >= 5 = Just (five, n - 5)+ | n >= 1 = Just (one, n - 1)+ | n == 0 = Nothing+ | otherwise = error "Data.Encoding.Bottom.encodeByte': impossible: unsigned byte is negative"
+ test/Main.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Data.Encoding.Bottom (decode, decode', encode, unBottom)+import Data.Text (Text)+import qualified Data.Text as T+import Data.Text.Encoding (encodeUtf8)+import Test.Hspec (Expectation, describe, hspec, it, shouldBe)+import Test.Hspec.QuickCheck (prop)++-- TODO: Why does using this character literally cause a GHC lexing error?+-- Related to https://gitlab.haskell.org/ghc/ghc/-/issues/5518 ?+twoHundred :: Text+twoHundred = T.singleton '\129730'++testEncode :: Text -> Text -> Expectation+testEncode input expected = unBottom (encode input) `shouldBe` encodeUtf8 expected++testDecode :: Text -> Text -> Expectation+testDecode input expected = case decode' $ encodeUtf8 input of+ Right r -> encodeUtf8 r `shouldBe` encodeUtf8 expected+ Left err -> error $ T.unpack err++testCases :: [(Text, Text)]+testCases =+ [ ("Test", "💖✨✨✨,,,,👉👈💖💖,👉👈💖💖✨🥺👉👈💖💖✨🥺,👉👈"),+ ("h", "💖💖,,,,👉👈"),+ ("🥺", twoHundred <> "✨✨✨✨👉👈💖💖💖🥺,,,,👉👈💖💖💖✨🥺👉👈💖💖💖✨✨✨🥺,👉👈"),+ ( "がんばれ",+ twoHundred+ <> "✨✨🥺,,👉👈💖💖✨✨🥺,,,,👉👈💖💖✨✨✨✨👉👈"+ <> twoHundred+ <> "✨✨🥺,,👉👈💖💖✨✨✨👉👈💖💖✨✨✨✨🥺,,👉👈"+ <> twoHundred+ <> "✨✨🥺,,👉👈💖💖✨✨🥺,,,,👉👈💖💖💖✨✨🥺,👉👈"+ <> twoHundred+ <> "✨✨🥺,,👉👈💖💖✨✨✨👉👈💖💖✨✨✨✨👉👈"+ )+ ]++main :: IO ()+main = hspec $ do+ -- Taken from https://github.com/bottom-software-foundation/bottom-rs/blob/need_top/src/bottom.rs+ describe "Data.Encoding.Bottom" $ do+ it "encodes strings" $ sequence_ $ uncurry testEncode <$> testCases+ it "decodes strings" $ sequence_ $ uncurry (flip testDecode) <$> testCases+ prop "decode is the inverse of encode" $ \s -> (T.unpack . decode . encode . T.pack) s `shouldBe` s