packages feed

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 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++[![Build and test](https://github.com/bottom-software-foundation/bottom-hs/actions/workflows/main.yml/badge.svg)](https://github.com/bottom-software-foundation/bottom-hs/actions/workflows/main.yml)+[![Hackage](https://img.shields.io/hackage/v/bottom)](https://hackage.haskell.org/package/bottom)+[![Hackage-Deps](https://img.shields.io/hackage-deps/v/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).++![,,,,,,,,,](https://cdn.discordapp.com/attachments/644479051918082050/799905088541425664/bottom.jpg)++## 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