From e1978f7941a21573af690b6645723f13a5e85537 Mon Sep 17 00:00:00 2001 From: Brian McKeon <135748266+brianjosephmckeon@users.noreply.github.com> Date: Thu, 16 May 2024 12:28:31 -0400 Subject: [PATCH] Prepare 0.2.4.2 release. (#37) Reformatted. Use new .github build workflow. Added custom release workflow. Updated package metadata. --------- Co-authored-by: Andrew Martin --- .github/CODEOWNERS | 1 + .github/workflows/build.yaml | 11 + .github/workflows/release.yaml | 10 + .gitignore | 1 + .travis.yml | 146 ----- README.md | 23 - cabal.project | 2 + code-generation/README.md | 1 - code-generation/Setup.hs | 2 - code-generation/app-countries/Main.hs | 170 +++--- code-generation/app-subdivisions/Main.hs | 27 +- code-generation/code-generation.cabal | 56 +- country-code-generation/Setup.hs | 2 - .../country-code-generation.cabal | 53 +- country/CHANGELOG.md | 6 + country/README.md | 20 + country/Setup.hs | 2 - country/bench/Main.hs | 32 +- country/bench/Size.hs | 19 +- country/country.cabal | 121 +++-- country/src/Continent.hs | 27 +- country/src/Continent/Unsafe.hs | 9 +- country/src/Country.hs | 86 +-- country/src/Country/Subdivision.hs | 78 +-- .../src/Country/Unexposed/Encode/English.hs | 506 +++++++++--------- country/src/Country/Unexposed/Names.hs | 165 +++--- country/src/Country/Unexposed/Trie.hs | 33 +- country/src/Country/Unexposed/TrieByte.hs | 32 +- country/src/Country/Unexposed/Util.hs | 23 +- country/src/Country/Unsafe.hs | 7 +- country/test/Spec.hs | 156 +++--- fourmolu.yaml | 51 ++ stack-8.6.yaml | 10 - stack.yaml.lock | 145 ----- 34 files changed, 930 insertions(+), 1103 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/build.yaml create mode 100644 .github/workflows/release.yaml delete mode 100644 .travis.yml delete mode 100644 README.md delete mode 100644 code-generation/README.md delete mode 100644 code-generation/Setup.hs delete mode 100644 country-code-generation/Setup.hs create mode 100644 country/CHANGELOG.md delete mode 100644 country/Setup.hs create mode 100644 fourmolu.yaml delete mode 100644 stack-8.6.yaml delete mode 100644 stack.yaml.lock diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..f6c0b22 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +@byteverse/l3c diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..0327d55 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,11 @@ +name: build +on: + pull_request: + branches: + - "*" + +jobs: + call-workflow: + uses: byteverse/.github/.github/workflows/build-matrix.yaml@main + with: + cabal-file: country/country.cabal diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..9411962 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,10 @@ +name: release +on: + push: + tags: + - "*" + +jobs: + call-workflow: + uses: byteverse/.github/.github/workflows/release.yaml@main + secrets: inherit diff --git a/.gitignore b/.gitignore index c8fd256..4f4d558 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.vscode/ *.aux cabal-dev .cabal-sandbox diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 07e06e9..0000000 --- a/.travis.yml +++ /dev/null @@ -1,146 +0,0 @@ -# This Travis job script has been generated by a script via -# -# haskell-ci 'travis' 'country/country.cabal' -# -# To regenerate the script (for example after adjusting tested-with) run -# -# haskell-ci regenerate -# -# For more information, see https://github.com/haskell-CI/haskell-ci -# -# version: 0.10.1 -# -version: ~> 1.0 -language: c -os: linux -dist: xenial -git: - # whether to recursively clone submodules - submodules: false -cache: - directories: - - $HOME/.cabal/packages - - $HOME/.cabal/store - - $HOME/.hlint -before_cache: - - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log - # remove files that are regenerated by 'cabal update' - - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.* - - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx - - rm -rfv $CABALHOME/packages/head.hackage -jobs: - include: - - compiler: ghc-8.10.1 - addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.10.1","cabal-install-3.2"]}} - os: linux - - compiler: ghc-8.8.3 - addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.8.3","cabal-install-3.2"]}} - os: linux - - compiler: ghc-8.6.5 - addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.6.5","cabal-install-3.2"]}} - os: linux - - compiler: ghc-8.4.4 - addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.4.4","cabal-install-3.2"]}} - os: linux -before_install: - - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//') - - WITHCOMPILER="-w $HC" - - HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//') - - HCPKG="$HC-pkg" - - unset CC - - CABAL=/opt/ghc/bin/cabal - - CABALHOME=$HOME/.cabal - - export PATH="$CABALHOME/bin:$PATH" - - TOP=$(pwd) - - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')" - - echo $HCNUMVER - - CABAL="$CABAL -vnormal+nowrap" - - set -o pipefail - - TEST=--enable-tests - - BENCH=--enable-benchmarks - - HEADHACKAGE=false - - rm -f $CABALHOME/config - - | - echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config - echo "remote-build-reporting: anonymous" >> $CABALHOME/config - echo "write-ghc-environment-files: always" >> $CABALHOME/config - echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config - echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config - echo "world-file: $CABALHOME/world" >> $CABALHOME/config - echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config - echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config - echo "installdir: $CABALHOME/bin" >> $CABALHOME/config - echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config - echo "store-dir: $CABALHOME/store" >> $CABALHOME/config - echo "install-dirs user" >> $CABALHOME/config - echo " prefix: $CABALHOME" >> $CABALHOME/config - echo "repository hackage.haskell.org" >> $CABALHOME/config - echo " url: http://hackage.haskell.org/" >> $CABALHOME/config -install: - - ${CABAL} --version - - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" - - | - echo "program-default-options" >> $CABALHOME/config - echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config - - cat $CABALHOME/config - - rm -fv cabal.project cabal.project.local cabal.project.freeze - - travis_retry ${CABAL} v2-update -v - # Generate cabal.project - - rm -rf cabal.project cabal.project.local cabal.project.freeze - - touch cabal.project - - | - echo "packages: country" >> cabal.project - - echo 'package country' >> cabal.project - - "echo ' ghc-options: -Werror=missing-methods' >> cabal.project" - - | - - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(country)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" - - cat cabal.project || true - - cat cabal.project.local || true - - if [ -f "country/configure.ac" ]; then (cd "country" && autoreconf -i); fi - - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} - - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'" - - rm cabal.project.freeze - - travis_wait 40 ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all - - travis_wait 40 ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all -script: - - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) - # Packaging... - - ${CABAL} v2-sdist all - # Unpacking... - - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ - - cd ${DISTDIR} || false - - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \; - - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \; - - PKGDIR_country="$(find . -maxdepth 1 -type d -regex '.*/country-[0-9.]*')" - # Generate cabal.project - - rm -rf cabal.project cabal.project.local cabal.project.freeze - - touch cabal.project - - | - echo "packages: ${PKGDIR_country}" >> cabal.project - - echo 'package country' >> cabal.project - - "echo ' ghc-options: -Werror=missing-methods' >> cabal.project" - - | - - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(country)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" - - cat cabal.project || true - - cat cabal.project.local || true - # Building... - # this builds all libraries and executables (without tests/benchmarks) - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all - # Building with tests and benchmarks... - # build & run tests, build benchmarks - - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all - # Testing... - - ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all - # cabal check... - - (cd ${PKGDIR_country} && ${CABAL} -vnormal check) - # haddock... - - ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all - # Building without installed constraints for packages in global-db... - - rm -f cabal.project.local - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all - -# REGENDATA ("0.10.1",["travis","country/country.cabal"]) -# EOF diff --git a/README.md b/README.md deleted file mode 100644 index d5637c3..0000000 --- a/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# country - -[![Build Status](https://travis-ci.org/andrewthad/country.svg?branch=master&status=started)](https://travis-ci.org/github/andrewthad/country) - -This is a mega-repo with two projects: - -- `country`: A library for dealing with countries, country codes, etc. -- `country-code-generation`: An executable that generates some of the - source code in `country`. It converts both `countries.csv` and - `aliases.txt` into lists of tuples. - -#### Build Instructions - -Some of source code in the `country` library is generated. To generate -this code, the `country-code-generation` application reads the top-level -`aliases.txt` and `countries.csv` file and outputs source Haskell. If -neither of these two files have been modified, then it is not necessary -to run `country-code-generation`. A `cabal.project` file is used to -build two targets. From the project root, run: - - cabal build country-code-generation - /home/jdoe/path/to/projects/country/.../country-code-generation - cabal build country diff --git a/cabal.project b/cabal.project index f3560ee..16bebd6 100644 --- a/cabal.project +++ b/cabal.project @@ -1 +1,3 @@ packages: ./country, ./code-generation +tests: True +benchmarks: True diff --git a/code-generation/README.md b/code-generation/README.md deleted file mode 100644 index 281a219..0000000 --- a/code-generation/README.md +++ /dev/null @@ -1 +0,0 @@ -# country diff --git a/code-generation/Setup.hs b/code-generation/Setup.hs deleted file mode 100644 index 9a994af..0000000 --- a/code-generation/Setup.hs +++ /dev/null @@ -1,2 +0,0 @@ -import Distribution.Simple -main = defaultMain diff --git a/code-generation/app-countries/Main.hs b/code-generation/app-countries/Main.hs index 098bac6..518d1e9 100644 --- a/code-generation/app-countries/Main.hs +++ b/code-generation/app-countries/Main.hs @@ -1,32 +1,32 @@ -{-# LANGUAGE RankNTypes #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} -import Streaming (Stream,Of(..)) -import Siphon (Siphon) import Colonnade (Headed) +import Control.Monad +import Control.Monad.IO.Class +import Control.Monad.Trans.Class import Data.ByteString (ByteString) -import Data.Text (Text) -import System.IO -import Data.Text.Encoding (encodeUtf8,decodeUtf8') -import Data.Char (isAlpha,toLower) +import qualified Data.ByteString.Char8 as BC +import Data.Char (isAlpha, toLower) import Data.DisjointSet (DisjointSet) -import Control.Monad.Trans.Class +import qualified Data.DisjointSet as DS import Data.Foldable (for_) -import Control.Monad -import Control.Monad.IO.Class -import Data.Primitive (readArray,writeArray,newArray) +import Data.Primitive (newArray, readArray, writeArray) import qualified Data.Set as Set -import qualified Data.DisjointSet as DS -import qualified Streaming as SM -import qualified Streaming.Prelude as SMP -import qualified Siphon as S +import Data.Text (Text) import qualified Data.Text as T +import Data.Text.Encoding (decodeUtf8', encodeUtf8) import qualified Data.Text.IO as TIO import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Builder as TB import qualified Data.Text.Lazy.Builder.Int as TBI -import qualified Data.ByteString.Char8 as BC -import qualified Data.ByteString.Streaming as BSM +import Siphon (Siphon) +import qualified Siphon as S +import Streaming (Of (..), Stream) +import qualified Streaming as SM +import qualified Streaming.ByteString as BSM +import qualified Streaming.Prelude as SMP +import System.IO main :: IO () main = do @@ -38,7 +38,7 @@ main = do alphaTwoPtrModule :: Stream (Of Country) IO r -> Stream (Of Text) IO r alphaTwoPtrModule s = do - aliasGroups <- lift buildAliasGroups + _aliasGroups <- lift buildAliasGroups SMP.yield "{-# language MagicHash #-}\n" SMP.yield "-- This module is autogenerated. Do not edit it by hand.\n" SMP.yield "module Country.Unexposed.AlphaTwoPtr\n" @@ -51,10 +51,10 @@ alphaTwoPtrModule s = do SMP.yield "alphaTwoPtr = Ptr \"\\\n" arr <- liftIO (newArray 2000 'A') r <- flip mapStreamM s $ \country -> do - let (c1,c2) = countryAlpha2 country + let (c1, c2) = countryAlpha2 country code = countryCode country if code >= 1000 - then error "Encountered country code over 1000" + then error "Encountered country code over 1000" else liftIO $ do writeArray arr (2 * code) c1 writeArray arr (2 * code + 1) c2 @@ -82,7 +82,7 @@ aliasModule s = do SMP.yield "\n" SMP.yield "aliases :: [(Word16,Text)]\n" SMP.yield "aliases =\n" - r <- flip mapStreamM (tagFirst s) $ \(isFirst,country) -> do + r <- flip mapStreamM (tagFirst s) $ \(isFirst, country) -> do let name = countryName country allNames = DS.equivalences name aliasGroups if isFirst @@ -105,7 +105,7 @@ aliasModule s = do buildAliasGroups :: IO (DisjointSet Text) buildAliasGroups = do t <- TIO.readFile "aliases.txt" - let countryGroups = map (filter (not . T.null)) $ (map.map) T.strip $ map T.lines $ T.splitOn "\n\n" t + let countryGroups = map (filter (not . T.null)) $ (map . map) T.strip $ map T.lines $ T.splitOn "\n\n" t let res = foldMap (DS.singletons . Set.fromList) countryGroups return res @@ -143,12 +143,12 @@ identifierModule s = do toIdentifier :: Text -> Text toIdentifier t = case (T.uncons . T.filter isAlpha . T.toTitle) t of Nothing -> T.empty - Just (b,bs) -> T.cons (toLower b) bs + Just (b, bs) -> T.cons (toLower b) bs toPatternName :: Text -> Text toPatternName = T.filter isAlpha . T.toTitle -englishEncoding :: Monad m => Stream (Of Country) m r -> Stream (Of Text) m r +englishEncoding :: (Monad m) => Stream (Of Country) m r -> Stream (Of Text) m r englishEncoding s = do SMP.yield "-- This module is autogenerated. Do not edit it by hand.\n" SMP.yield "module Country.Unexposed.Encode.English\n" @@ -162,9 +162,9 @@ englishEncoding s = do SMP.yield "-- third is two char code, fourth is three char code.\n" SMP.yield "countryNameQuads :: [(Word16,Text,(Char,Char),(Char,Char,Char))]\n" SMP.yield "countryNameQuads =\n" - r <- flip mapStreamM (tagFirst s) $ \(isFirst,country) -> do - let (a1,a2) = countryAlpha2 country - (b1,b2,b3) = countryAlpha3 country + r <- flip mapStreamM (tagFirst s) $ \(isFirst, country) -> do + let (a1, a2) = countryAlpha2 country + (b1, b2, b3) = countryAlpha3 country if isFirst then SMP.yield " [ (" else SMP.yield " , (" @@ -199,7 +199,7 @@ continents s = do SMP.yield "-- first value is country code, second is the continent constructor\n" SMP.yield "continentAList :: [(Word16,Continent)]\n" SMP.yield "continentAList =\n" - r <- flip mapStreamM (tagFirst s) $ \(isFirst,country) -> do + r <- flip mapStreamM (tagFirst s) $ \(isFirst, country) -> do if isFirst then SMP.yield " [ (" else SMP.yield " , (" @@ -233,87 +233,93 @@ continents s = do SMP.yield "{-# NOINLINE continentAList #-}\n" return r -yieldLazyText :: Monad m => LT.Text -> Stream (Of Text) m () +yieldLazyText :: (Monad m) => LT.Text -> Stream (Of Text) m () yieldLazyText = mapM_ SMP.yield . LT.toChunks - -tagFirst :: Monad m => Stream (Of a) m r -> Stream (Of (Bool,a)) m r + +tagFirst :: (Monad m) => Stream (Of a) m r -> Stream (Of (Bool, a)) m r tagFirst = SMP.zip (SMP.yield True >> SMP.repeat False) -mapStreamM :: Monad m - => (a -> Stream (Of b) m x) - -> Stream (Of a) m r - -> Stream (Of b) m r +mapStreamM :: + (Monad m) => + (a -> Stream (Of b) m x) -> + Stream (Of a) m r -> + Stream (Of b) m r mapStreamM f = SM.concats . SM.mapsM (\(a :> s) -> return (f a >> return s)) withCountries :: - String -- ^ file name - -> (forall r. Stream (Of Country) IO r -> Stream (Of Text) IO r) - -> IO () -withCountries fn g = + -- | file name + String -> + (forall r. Stream (Of Country) IO r -> Stream (Of Text) IO r) -> + IO () +withCountries fn g = withFile fn WriteMode $ \output -> - withFile "countries.csv" ReadMode $ \input -> do - m <- id - $ BSM.hPut output - $ BSM.fromChunks - $ SMP.map encodeUtf8 - $ g - $ S.decodeCsvUtf8 siphon - $ BSM.toChunks - $ BSM.fromHandle input - case m of - Nothing -> return () - Just err -> do - hPutStrLn stderr (S.humanizeSiphonError err) - fail "died" + withFile "countries.csv" ReadMode $ \input -> do + m <- + id $ + BSM.hPut output $ + BSM.fromChunks $ + SMP.map encodeUtf8 $ + g $ + S.decodeCsvUtf8 siphon $ + BSM.toChunks $ + BSM.fromHandle input + case m of + Nothing -> return () + Just err -> do + hPutStrLn stderr (S.humanizeSiphonError err) + fail "died" data Country = Country { countryName :: Text - , countryAlpha2 :: (Char,Char) - , countryAlpha3 :: (Char,Char,Char) + , countryAlpha2 :: (Char, Char) + , countryAlpha3 :: (Char, Char, Char) , countryCode :: Int , countryRegion :: Text , countrySubregion :: Text } siphon :: Siphon Headed ByteString Country -siphon = Country - <$> S.headed "name" decodeUtf8Maybe - <*> S.headed "alpha-2" decodeChar2 - <*> S.headed "alpha-3" decodeChar3 - <*> S.headed "country-code" decodeInt - <*> S.headed "region" decodeUtf8Maybe - <*> S.headed "sub-region" decodeUtf8Maybe +siphon = + Country + <$> S.headed "name" decodeUtf8Maybe + <*> S.headed "alpha-2" decodeChar2 + <*> S.headed "alpha-3" decodeChar3 + <*> S.headed "country-code" decodeInt + <*> S.headed "region" decodeUtf8Maybe + <*> S.headed "sub-region" decodeUtf8Maybe decodeUtf8Maybe :: ByteString -> Maybe Text decodeUtf8Maybe = either (\_ -> Nothing) Just . decodeUtf8' -decodeChar2 :: ByteString -> Maybe (Char,Char) -decodeChar2 bs = if BC.length bs == 2 - then - let b0 = BC.index bs 0 - b1 = BC.index bs 1 - in if isUpperAscii b0 && isUpperAscii b1 - then Just (b0,b1) - else Nothing - else Nothing +decodeChar2 :: ByteString -> Maybe (Char, Char) +decodeChar2 bs = + if BC.length bs == 2 + then + let b0 = BC.index bs 0 + b1 = BC.index bs 1 + in if isUpperAscii b0 && isUpperAscii b1 + then Just (b0, b1) + else Nothing + else Nothing isUpperAscii :: Char -> Bool isUpperAscii x = x >= 'A' && x <= 'Z' -decodeChar3 :: ByteString -> Maybe (Char,Char,Char) -decodeChar3 bs = if BC.length bs == 3 - then - let b0 = BC.index bs 0 - b1 = BC.index bs 1 - b2 = BC.index bs 2 - in if isUpperAscii b0 && isUpperAscii b1 && isUpperAscii b2 - then Just (b0,b1,b2) - else Nothing - else Nothing +decodeChar3 :: ByteString -> Maybe (Char, Char, Char) +decodeChar3 bs = + if BC.length bs == 3 + then + let b0 = BC.index bs 0 + b1 = BC.index bs 1 + b2 = BC.index bs 2 + in if isUpperAscii b0 && isUpperAscii b1 && isUpperAscii b2 + then Just (b0, b1, b2) + else Nothing + else Nothing decodeInt :: ByteString -> Maybe Int decodeInt b = do - (a,bsRem) <- BC.readInt b + (a, bsRem) <- BC.readInt b if BC.null bsRem then Just a else Nothing diff --git a/code-generation/app-subdivisions/Main.hs b/code-generation/app-subdivisions/Main.hs index 40c4e2f..9997956 100644 --- a/code-generation/app-subdivisions/Main.hs +++ b/code-generation/app-subdivisions/Main.hs @@ -2,19 +2,20 @@ module Main where -import Control.Monad (forM,forM_) +import Control.Monad (forM, forM_) import Data.List.Split (splitOn) import System.Environment (getArgs) import System.Exit (exitFailure) -import System.IO (stderr,Handle,withFile,hPutStrLn,IOMode(..)) +import System.IO (Handle, IOMode (..), hPutStrLn, stderr, withFile) main :: IO () main = do - (inFile,outFile) <- getArgs >>= \case - [a, b] -> pure (a, b) - _ -> die "usage: two required filepath arguments (in, out)" + (inFile, outFile) <- + getArgs >>= \case + [a, b] -> pure (a, b) + _ -> die "usage: two required filepath arguments (in, out)" rows <- parse inFile - withFile outFile WriteMode $ \fp -> do + withFile outFile WriteMode $ \fp -> do render fp rows parse :: FilePath -> IO [(String, String, String)] @@ -22,11 +23,11 @@ parse file = do content <- readFile file rawRows <- case lines content of [] -> die "no file contents" - (header:body) + (header : body) | header == expectedHeader -> pure body | otherwise -> die $ "unrecognized header: " ++ show header forM (splitOn "," <$> rawRows) $ \case - [x,y,z] -> pure (x,y,z) + [x, y, z] -> pure (x, y, z) _ -> die "bad data row" render :: Handle -> [(String, String, String)] -> IO () @@ -86,15 +87,15 @@ render out xs = do put "{-# NOINLINE categoryArray #-}" put "actualNumberOfSubdivisions :: Int" put $ "actualNumberOfSubdivisions = " ++ show len - where + where put = hPutStrLn out len = length xs topX = head xs restXs = tail xs - putThing c it = put $ " " ++ c:" " ++ show it - putCode c (code,_,_) = putThing c code - putName c (_,name,_) = putThing c name - putCategory c (_,_,category) = putThing c category + putThing c it = put $ " " ++ c : " " ++ show it + putCode c (code, _, _) = putThing c code + putName c (_, name, _) = putThing c name + putCategory c (_, _, category) = putThing c category expectedHeader :: String expectedHeader = "code,name,category" diff --git a/code-generation/code-generation.cabal b/code-generation/code-generation.cabal index 42c9dfe..e373e8b 100644 --- a/code-generation/code-generation.cabal +++ b/code-generation/code-generation.cabal @@ -1,43 +1,47 @@ cabal-version: 3.0 -name: country-code-generation -version: 0.1.0.0 --- synopsis: --- description: -homepage: https://github.com/andrewthad/country#readme -license: BSD-3-Clause -license-file: LICENSE -author: Andrew Martin -maintainer: andrew.thaddeus@gmail.com -copyright: 2017 Andrew Martin -category: Web -build-type: Simple -extra-source-files: README.md, ../aliases.txt, ../countries.csv +name: code-generation +version: 0.1.0.0 +homepage: https://github.com/byteverse/country +bug-reports: https://github.com/byteverse/country/issues +license: BSD-3-Clause +license-file: LICENSE +author: Andrew Martin +maintainer: amartin@layer3com.com +copyright: 2017 Andrew Martin +category: Web +build-type: Simple + +common build-settings + default-language: Haskell2010 + ghc-options: -Wall -Wunused-packages executable country-code-generation + import: build-settings + ghc-options: -O2 hs-source-dirs: app-countries - main-is: Main.hs + main-is: Main.hs build-depends: - , base - , bytestring >= 0.10 - , colonnade >= 1.2.0.1 + , base >=4.9 + , bytestring >=0.10 + , colonnade >=1.2.0.1 , containers - , disjoint-containers >= 0.2.3 + , disjoint-containers >=0.2.3 , primitive - , siphon >= 0.8.1 + , siphon >=0.8.1 , streaming , streaming-bytestring - , text >= 1.2 + , text >=1.2 , transformers - default-language: Haskell2010 executable subdivision-code-generation + import: build-settings + ghc-options: -O2 hs-source-dirs: app-subdivisions - main-is: Main.hs + main-is: Main.hs build-depends: - base - , split >=0.2 - default-language: Haskell2010 + , base >=4.9 + , split >=0.2 source-repository head type: git - location: https://github.com/andrewthad/country + location: git://github.com/byteverse/country.git diff --git a/country-code-generation/Setup.hs b/country-code-generation/Setup.hs deleted file mode 100644 index 9a994af..0000000 --- a/country-code-generation/Setup.hs +++ /dev/null @@ -1,2 +0,0 @@ -import Distribution.Simple -main = defaultMain diff --git a/country-code-generation/country-code-generation.cabal b/country-code-generation/country-code-generation.cabal index 9f37fdb..902b8ac 100644 --- a/country-code-generation/country-code-generation.cabal +++ b/country-code-generation/country-code-generation.cabal @@ -1,35 +1,38 @@ -name: country-code-generation -version: 0.1.0.0 --- synopsis: --- description: -homepage: https://github.com/andrewthad/country#readme -license: BSD3 -license-file: LICENSE -author: Andrew Martin -maintainer: andrew.thaddeus@gmail.com -copyright: 2017 Andrew Martin -category: Web -build-type: Simple -extra-source-files: README.md, ../aliases.txt, ../countries.csv -cabal-version: >=1.10 +cabal-version: 3.0 +name: country-code-generation +version: 0.1.0.0 +homepage: https://github.com/byteverse/country +bug-reports: https://github.com/byteverse/country/issues +license: BSD-3-Clause +license-file: LICENSE +author: Andrew Martin +maintainer: amartin@layer3com.com +copyright: 2017 Andrew Martin +category: Web +build-type: Simple + +common build-settings + default-language: Haskell2010 + ghc-options: -Wall -Wunused-packages executable country-code-generation + import: build-settings + ghc-options: -O2 hs-source-dirs: app - main-is: Main.hs + main-is: Main.hs build-depends: - base + , base <5.0 + , bytestring >=0.10 + , colonnade >=1.2.0.1 + , containers + , disjoint-containers >=0.3 + , primitive + , siphon >=0.8.1 , streaming , streaming-bytestring - , bytestring >= 0.10 - , text >= 1.2 && < 2.1 - , siphon >= 0.8.1 - , colonnade >= 1.2.0.1 - , disjoint-containers >= 0.3 - , containers + , text >=1.2 && <2.1 , transformers - , primitive - default-language: Haskell2010 source-repository head type: git - location: https://github.com/andrewthad/country + location: git://github.com/byteverse/country.git diff --git a/country/CHANGELOG.md b/country/CHANGELOG.md new file mode 100644 index 0000000..93cde28 --- /dev/null +++ b/country/CHANGELOG.md @@ -0,0 +1,6 @@ +# Revision history for country + +## 0.2.4.2 -- 2024-02-15 + +* Update package metadata. +* Bumped `deepseq` upper bound to <1.6. diff --git a/country/README.md b/country/README.md index 281a219..db01f34 100644 --- a/country/README.md +++ b/country/README.md @@ -1 +1,21 @@ # country + +This is a mega-repo with two projects: + +- `country`: A library for dealing with countries, country codes, etc. +- `country-code-generation`: An executable that generates some of the + source code in `country`. It converts both `countries.csv` and + `aliases.txt` into lists of tuples. + +#### Build Instructions + +Some of source code in the `country` library is generated. To generate +this code, the `country-code-generation` application reads the top-level +`aliases.txt` and `countries.csv` file and outputs source Haskell. If +neither of these two files have been modified, then it is not necessary +to run `country-code-generation`. A `cabal.project` file is used to +build two targets. From the project root, run: + + cabal build country-code-generation + /home/jdoe/path/to/projects/country/.../country-code-generation + cabal build country diff --git a/country/Setup.hs b/country/Setup.hs deleted file mode 100644 index 9a994af..0000000 --- a/country/Setup.hs +++ /dev/null @@ -1,2 +0,0 @@ -import Distribution.Simple -main = defaultMain diff --git a/country/bench/Main.hs b/country/bench/Main.hs index 80d8b2e..483eb32 100644 --- a/country/bench/Main.hs +++ b/country/bench/Main.hs @@ -1,28 +1,30 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE TypeApplications #-} -import Gauge.Main (defaultMain,bgroup,bench,whnf) import Data.Text (Text) +import Gauge.Main (bench, defaultMain, whnf) -import qualified Data.Bytes as Bytes +import qualified Country import qualified Data.List as List import qualified Data.Text as Text -import qualified Country main :: IO () -main = defaultMain - [ bench "decode" (whnf decodeManyCountries ex1) - ] +main = + defaultMain + [ bench "decode" (whnf decodeManyCountries ex1) + ] ex1 :: Text -{-# noinline ex1 #-} -ex1 = Text.intercalate - (Text.singleton '\n') - (List.map Country.encodeEnglish [minBound..maxBound]) +{-# NOINLINE ex1 #-} +ex1 = + Text.intercalate + (Text.singleton '\n') + (List.map Country.encodeEnglish [minBound .. maxBound]) decodeManyCountries :: Text -> Word -{-# noinline decodeManyCountries #-} -decodeManyCountries txt = List.foldl' - (\acc t -> acc + maybe 0 (fromIntegral . Country.encodeNumeric) (Country.decode t)) - (0 :: Word) - (Text.split (=='\n') txt) +{-# NOINLINE decodeManyCountries #-} +decodeManyCountries txt = + List.foldl' + (\acc t -> acc + maybe 0 (fromIntegral . Country.encodeNumeric) (Country.decode t)) + (0 :: Word) + (Text.split (== '\n') txt) diff --git a/country/bench/Size.hs b/country/bench/Size.hs index d45152d..d708bf4 100644 --- a/country/bench/Size.hs +++ b/country/bench/Size.hs @@ -1,13 +1,13 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE TypeApplications #-} -import Country (hashMapUtf8,hashMapUtf16) +import Country (hashMapUtf16, hashMapUtf8) import Data.Compact import Data.Foldable -import qualified Data.List as List import qualified Country import qualified Data.Bytes.HashMap.Word as Map +import qualified Data.List as List main :: IO () main = do @@ -16,14 +16,17 @@ main = do szUtf16 <- estimateHeapUse hashMapUtf16 putStrLn ("UTF-16 HashMap Size: " ++ show szUtf16) putStrLn "UTF-8 HashMap Distribution:" - forM_ (Map.distribution hashMapUtf8) $ \(bktSz,ct) -> + forM_ (Map.distribution hashMapUtf8) $ \(bktSz, ct) -> putStrLn (show bktSz ++ "," ++ show ct) putStrLn ("UTF-8 HashMap Distinct Entropies: " ++ show (Map.distinctEntropies hashMapUtf8)) -- I think this rounds to the nearest 4KB. estimateHeapUse :: a -> IO Word -estimateHeapUse a = foldlM - ( \lo i -> do - w <- compactSized (i * 2000) True a >>= compactSize - pure (min lo w) - ) maxBound [1..50] +estimateHeapUse a = + foldlM + ( \lo i -> do + w <- compactSized (i * 2000) True a >>= compactSize + pure (min lo w) + ) + maxBound + [1 .. 50] diff --git a/country/country.cabal b/country/country.cabal index 85ff19f..febb538 100644 --- a/country/country.cabal +++ b/country/country.cabal @@ -1,7 +1,7 @@ -cabal-version: 2.2 -name: country -version: 0.2.4.1 -synopsis: Country data type and functions +cabal-version: 3.0 +name: country +version: 0.2.4.2 +synopsis: Country data type and functions description: The `country` library provides a data type for dealing with the set of countries as defined by ISO 3166. The representation @@ -30,19 +30,29 @@ description: . Please open up an issue on github if there is anything you would like to see added. -homepage: https://github.com/andrewthad/country#readme -license: BSD-3-Clause -license-file: LICENSE -author: Andrew Martin -maintainer: andrew.thaddeus@gmail.com -copyright: 2017 Andrew Martin -category: Web -build-type: Simple -extra-source-files: README.md -tested-with: GHC==8.10.1, GHC==8.8.3, GHC==8.6.5, GHC==8.4.4 + +homepage: https://github.com/byteverse/country +bug-reports: https://github.com/byteverse/country/issues +license: BSD-3-Clause +license-file: LICENSE +author: Andrew Martin +maintainer: amartin@layer3com.com +copyright: 2017 Andrew Martin +category: Web +build-type: Simple +extra-doc-files: + CHANGELOG.md + README.md + +tested-with: GHC ==9.4.8 || ==9.6.3 || ==9.8.1 + +common build-settings + default-language: Haskell2010 + ghc-options: -Wall -Wunused-packages library - hs-source-dirs: src + import: build-settings + hs-source-dirs: src exposed-modules: Continent Continent.Unsafe @@ -50,6 +60,7 @@ library Country.Identifier Country.Subdivision Country.Unsafe + other-modules: Country.Unexposed.Alias Country.Unexposed.AlphaTwoPtr @@ -60,73 +71,71 @@ library Country.Unexposed.Trie Country.Unexposed.TrieByte Country.Unexposed.Util + build-depends: - , aeson >=1.2 && <2.3 - , attoparsec >=0.13 && <0.15 - , base >=4.12 && <5 - , bytebuild >=0.3.4 && <0.4 - , bytehash >=0.1.1 && <0.2 - , byteslice >=0.2.3 && <0.3 - , bytestring >= 0.10 && <0.12 - , contiguous >=0.6.1 - , deepseq >= 1.3.0.2 && <1.5 - , entropy >=0.4.1.5 && <0.5 - , hashable >=1.2 && <1.5 - , primitive >= 0.6.4 && <0.10 - , primitive-unlifted >= 2.1 - , scientific >=0.3 && <0.4 - , text >= 2.0 && <2.2 - , text-short >=0.1.3 - , unordered-containers >=0.2 && <0.3 - default-language: Haskell2010 - ghc-options: -Wall -O2 + , aeson >=1.2 && <2.3 + , attoparsec >=0.13 && <0.15 + , base >=4.9 && <5 + , bytebuild >=0.3.4 && <0.4 + , bytehash >=0.1.1 && <0.2 + , byteslice >=0.2.3 && <0.3 + , bytestring >=0.10 + , contiguous >=0.6.1 + , deepseq >=1.3.0.2 && <1.6 + , hashable >=1.2 && <1.5 + , primitive >=0.6.4 && <0.10 + , primitive-unlifted >=2.1 + , scientific >=0.3 && <0.4 + , text >=2.0 && <2.2 + , text-short >=0.1.3 + , unordered-containers >=0.2 && <0.3 + + ghc-options: -O2 test-suite test - type: exitcode-stdio-1.0 + import: build-settings + type: exitcode-stdio-1.0 hs-source-dirs: test - main-is: Spec.hs + main-is: Spec.hs build-depends: - , QuickCheck , base , byteslice , country , primitive - , quickcheck-classes >=0.6.4 + , QuickCheck + , quickcheck-classes >=0.6.4 , tasty , tasty-quickcheck , text , text-short - ghc-options: -O2 - default-language: Haskell2010 benchmark bench - type: exitcode-stdio-1.0 + import: build-settings + type: exitcode-stdio-1.0 build-depends: - , country , base - , byteslice - , bytestring + , country , gauge - , primitive - , text >=1.2 - ghc-options: -Wall -O2 - default-language: Haskell2010 + , text >=1.2 + + ghc-options: -O2 hs-source-dirs: bench - main-is: Main.hs + main-is: Main.hs benchmark bench-size - type: exitcode-stdio-1.0 + import: build-settings + type: exitcode-stdio-1.0 build-depends: - , country , base + , bytehash , bytestring , compact - , bytehash - ghc-options: -Wall -O2 - default-language: Haskell2010 + , country + + ghc-options: -O2 hs-source-dirs: bench - main-is: Size.hs + main-is: Size.hs source-repository head type: git - location: https://github.com/andrewthad/country + location: git://github.com/byteverse/country.git diff --git a/country/src/Continent.hs b/country/src/Continent.hs index c0a9add..e2176ce 100644 --- a/country/src/Continent.hs +++ b/country/src/Continent.hs @@ -10,12 +10,15 @@ module Continent , pattern NorthAmerica , pattern Oceania , pattern SouthAmerica - -- * Continent Mapping + + -- * Continent Mapping , continent - -- * Name + + -- * Name , encodeEnglish , decodeEnglish - -- * Two-letter Codes + + -- * Two-letter Codes , alphaUpper , alphaLower , decodeAlpha @@ -26,8 +29,8 @@ import Continent.Unsafe import Control.Monad (forM_) import Control.Monad.ST (runST) import Country.Unexposed.Continents (continentAList) -import Country.Unexposed.Util (mapTextArray,charToWord8,word16ToInt,timesTwo) -import Country.Unsafe (Country(Country)) +import Country.Unexposed.Util (charToWord8, mapTextArray, timesTwo, word16ToInt) +import Country.Unsafe (Country (Country)) import Data.Char (toLower) import Data.Text (Text) import Data.Word (Word8) @@ -37,19 +40,17 @@ import qualified Data.Text as T import qualified Data.Text.Array as TA import qualified Data.Text.Internal as TI - numberOfContinents :: Int numberOfContinents = length continentNameDb {-# NOINLINE numberOfContinents #-} - alphaUpper :: Continent -> Text alphaUpper (Continent n) = TI.text allAlphaUpper (timesTwo (fromIntegral n)) 2 allAlphaUpper :: TA.Array allAlphaUpper = TA.run $ do m <- TA.new (timesTwo numberOfContinents) - forM_ continentNameDb $ \(n,_,(a1,a2)) -> do + forM_ continentNameDb $ \(n, _, (a1, a2)) -> do let ix = timesTwo (fromIntegral n) TA.unsafeWrite m ix (charToWord8 a1) TA.unsafeWrite m (ix + 1) (charToWord8 a2) @@ -65,7 +66,8 @@ allAlphaLower = mapTextArray toLower allAlphaUpper decodeAlpha :: Text -> Maybe Continent decodeAlpha = fmap Continent . flip lookup tbl . T.toUpper - where tbl = flip map continentNameDb $ \(n,_,(a,b)) -> ((T.toUpper . T.pack) [a,b], n) + where + tbl = flip map continentNameDb $ \(n, _, (a, b)) -> ((T.toUpper . T.pack) [a, b], n) encodeEnglish :: Continent -> Text encodeEnglish (Continent n) = Prim.indexArray englishContinentNamesText (fromIntegral n) @@ -73,13 +75,14 @@ encodeEnglish (Continent n) = Prim.indexArray englishContinentNamesText (fromInt englishContinentNamesText :: Prim.Array Text englishContinentNamesText = runST $ do m <- Prim.newArray numberOfContinents unnamed - mapM_ (\(ix,name,_) -> Prim.writeArray m (fromIntegral ix) name) continentNameDb + mapM_ (\(ix, name, _) -> Prim.writeArray m (fromIntegral ix) name) continentNameDb Prim.unsafeFreezeArray m {-# NOINLINE englishContinentNamesText #-} decodeEnglish :: Text -> Maybe Continent decodeEnglish = fmap Continent . flip lookup tbl - where tbl = flip map continentNameDb $ \(n,name,_) -> (name, n) + where + tbl = flip map continentNameDb $ \(n, name, _) -> (name, n) continent :: Country -> Continent continent (Country n) = Continent $ Prim.indexArray allContinents (word16ToInt n) @@ -87,7 +90,7 @@ continent (Country n) = Continent $ Prim.indexArray allContinents (word16ToInt n allContinents :: Prim.Array Word8 allContinents = runST $ do m <- Prim.newArray numberOfPossibleCodes 255 - forM_ continentAList $ \(ix,Continent n) -> + forM_ continentAList $ \(ix, Continent n) -> Prim.writeArray m (word16ToInt ix) n Prim.unsafeFreezeArray m {-# NOINLINE allContinents #-} diff --git a/country/src/Continent/Unsafe.hs b/country/src/Continent/Unsafe.hs index a1b7a27..6c8d65d 100644 --- a/country/src/Continent/Unsafe.hs +++ b/country/src/Continent/Unsafe.hs @@ -3,7 +3,7 @@ {-# LANGUAGE PatternSynonyms #-} module Continent.Unsafe - ( Continent(..) + ( Continent (..) , pattern Africa , pattern Asia , pattern Antarctica @@ -17,10 +17,8 @@ module Continent.Unsafe import Data.Text (Text) import Data.Word (Word8) - newtype Continent = Continent Word8 - deriving(Eq,Ord,Enum) - + deriving (Eq, Ord, Enum) {-# COMPLETE Africa, Asia, Antarctica, Europe, NorthAmerica, Oceania, SouthAmerica #-} pattern Africa, Asia, Antarctica, Europe, NorthAmerica, Oceania, SouthAmerica :: Continent @@ -32,8 +30,7 @@ pattern NorthAmerica = Continent 4 pattern Oceania = Continent 5 pattern SouthAmerica = Continent 6 - -continentNameDb :: [(Word8,Text,(Char,Char))] +continentNameDb :: [(Word8, Text, (Char, Char))] continentNameDb = [ (0, "Africa", ('A', 'F')) , (1, "Asia", ('A', 'N')) diff --git a/country/src/Country.hs b/country/src/Country.hs index 8371207..10b5970 100644 --- a/country/src/Country.hs +++ b/country/src/Country.hs @@ -2,14 +2,14 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE MagicHash #-} -{-# OPTIONS_GHC -Wall #-} - -- | Country type and helpers. module Country ( Country + -- * Three digit code , encodeNumeric , decodeNumeric + -- * Name , encodeEnglish , encodeEnglishShort @@ -18,6 +18,7 @@ module Country , decodeUtf8Bytes , parser , parserUtf8 + -- * Alpha-2 and Alpha-3 , alphaTwoUpper , alphaTwoUpperUtf8Ptr @@ -27,6 +28,7 @@ module Country , alphaTwoLower , decodeAlphaTwo , decodeAlphaThree + -- * Hash Maps for Decoding , hashMapUtf8 , hashMapUtf16 @@ -36,23 +38,21 @@ import Control.Monad (forM_) import Control.Monad.ST (runST) import Country.Unexposed.AlphaTwoPtr (alphaTwoPtr) import Country.Unexposed.Encode.English (countryNameQuads) -import Country.Unexposed.Names (hashMapUtf16,hashMapUtf8) -import Country.Unexposed.Names (numberOfPossibleCodes,alphaTwoHashMap,alphaThreeHashMap,decodeMap,decodeMapUtf8,decodeNumeric,encodeEnglish,encodeEnglishShort) -import Country.Unexposed.Trie (Trie,trieFromList,trieParser) -import Country.Unexposed.TrieByte (TrieByte,trieByteFromList,trieByteParser) -import Country.Unexposed.Util (newZeroedByteArray,mapTextArray,charToWord8,word16ToInt,timesTwo,timesThree) -import Country.Unsafe (Country(..)) -import Data.Bytes.Types (Bytes(Bytes)) +import Country.Unexposed.Names (alphaThreeHashMap, alphaTwoHashMap, decodeMap, decodeMapUtf8, decodeNumeric, encodeEnglish, encodeEnglishShort, hashMapUtf16, hashMapUtf8, numberOfPossibleCodes) +import Country.Unexposed.Trie (Trie, trieFromList, trieParser) +import Country.Unexposed.TrieByte (TrieByte, trieByteFromList, trieByteParser) +import Country.Unexposed.Util (charToWord8, mapTextArray, newZeroedByteArray, timesThree, timesTwo, word16ToInt) +import Country.Unsafe (Country (..)) import Data.ByteString (ByteString) +import Data.Bytes.Types (Bytes (Bytes)) import Data.Char (toLower) import Data.Coerce (coerce) -import Data.Primitive (writeByteArray,indexByteArray,unsafeFreezeByteArray) -import Data.Primitive.ByteArray (ByteArray(..)) +import Data.Primitive (indexByteArray, unsafeFreezeByteArray, writeByteArray) +import Data.Primitive.ByteArray (ByteArray (..)) import Data.Primitive.Ptr (indexOffPtr) import Data.Text (Text) -import Data.Word (Word16) -import Data.Word (Word8) -import Foreign.Ptr (Ptr,plusPtr) +import Data.Word (Word16, Word8) +import Foreign.Ptr (Ptr, plusPtr) import qualified Data.Attoparsec.ByteString as AB import qualified Data.Attoparsec.Text as AT @@ -63,8 +63,9 @@ import qualified Data.Text.Array as TA import qualified Data.Text.Encoding as TE import qualified Data.Text.Internal as TI --- | Convert a country to its numeric code. This is a --- three-digit number and will consequently be less than 1000. +{- | Convert a country to its numeric code. This is a + three-digit number and will consequently be less than 1000. +-} encodeNumeric :: Country -> Word16 encodeNumeric (Country n) = n @@ -72,20 +73,22 @@ encodeNumeric (Country n) = n alphaTwoUpper :: Country -> Text alphaTwoUpper c = TI.text allAlphaTwoUpper (timesTwo (indexOfCountry c)) 2 --- | The alpha-2 country code, uppercase. The resulting address always --- has two bytes at it. +{- | The alpha-2 country code, uppercase. The resulting address always +has two bytes at it. +-} alphaTwoUpperUtf8Ptr :: Country -> Ptr Word8 alphaTwoUpperUtf8Ptr (Country c) = plusPtr alphaTwoPtr (2 * fromIntegral c) alphaTwoUpperUtf8BoundedBuilder :: Country -> BBU.Builder 2 -alphaTwoUpperUtf8BoundedBuilder !c = BBU.construct - (\arr ix -> do - let ptr = alphaTwoUpperUtf8Ptr c - writeByteArray arr ix (indexOffPtr ptr 0) - writeByteArray arr (ix + 1) (indexOffPtr ptr 1) - pure (ix + 2) - ) +alphaTwoUpperUtf8BoundedBuilder !c = + BBU.construct + ( \arr ix -> do + let ptr = alphaTwoUpperUtf8Ptr c + writeByteArray arr ix (indexOffPtr ptr 0) + writeByteArray arr (ix + 1) (indexOffPtr ptr 1) + pure (ix + 2) + ) -- | The alpha-3 country code, uppercase alphaThreeUpper :: Country -> Text @@ -107,12 +110,12 @@ decodeAlphaTwo = flip HM.lookup alphaTwoHashMap decodeAlphaThree :: Text -> Maybe Country decodeAlphaThree = flip HM.lookup alphaThreeHashMap - --- | Parse a country from its name. This function is language-agnostic --- and is very generous with what it accepts. It handles official --- names, colloquial names, acroynms, and obsolete names for many --- countries. It strives to handle any source language. Open an --- issue on the issue tracker if there are names that are missing. +{- | Parse a country from its name. This function is language-agnostic + and is very generous with what it accepts. It handles official + names, colloquial names, acroynms, and obsolete names for many + countries. It strives to handle any source language. Open an + issue on the issue tracker if there are names that are missing. +-} decode :: Text -> Maybe Country decode (TI.Text (TA.ByteArray arr) off8 len8) = case (BytesHashMap.lookup (Bytes (ByteArray arr) off8 len8) hashMapUtf8) of @@ -128,11 +131,12 @@ decodeUtf8Bytes !bs = case (BytesHashMap.lookup bs hashMapUtf8) of Nothing -> Nothing Just w -> Just (Country (fromIntegral w)) --- | Parse a country from its name using an attoparsec text parser. This --- function is language-agnostic and can handle any source language. --- In the case that one possible country name is a prefix of another --- possible name (for example, United States vs United States of America), --- the longest possible will be parsed. +{- | Parse a country from its name using an attoparsec text parser. This + function is language-agnostic and can handle any source language. + In the case that one possible country name is a prefix of another + possible name (for example, United States vs United States of America), + the longest possible will be parsed. +-} parser :: AT.Parser Country parser = coerce (trieParser decodeTrie) @@ -147,7 +151,7 @@ numberOfCountries = length countryNameQuads positions :: ByteArray positions = runST $ do m <- newZeroedByteArray (timesTwo numberOfPossibleCodes) - forM_ (zip (enumFrom (0 :: Word16)) countryNameQuads) $ \(ix,(n,_,_,_)) -> do + forM_ (zip (enumFrom (0 :: Word16)) countryNameQuads) $ \(ix, (n, _, _, _)) -> do writeByteArray m (word16ToInt n) ix unsafeFreezeByteArray m {-# NOINLINE positions #-} @@ -162,7 +166,7 @@ indexOfCountry (Country n) = allAlphaTwoUpper :: TA.Array allAlphaTwoUpper = TA.run $ do m <- TA.new (timesTwo numberOfCountries) - forM_ countryNameQuads $ \(n,_,(a1,a2),_) -> do + forM_ countryNameQuads $ \(n, _, (a1, a2), _) -> do let ix = timesTwo (indexOfCountry (Country n)) TA.unsafeWrite m ix (charToWord8 a1) TA.unsafeWrite m (ix + 1) (charToWord8 a2) @@ -172,7 +176,7 @@ allAlphaTwoUpper = TA.run $ do allAlphaThreeUpper :: TA.Array allAlphaThreeUpper = TA.run $ do m <- TA.new (timesThree numberOfCountries) - forM_ countryNameQuads $ \(n,_,_,(a1,a2,a3)) -> do + forM_ countryNameQuads $ \(n, _, _, (a1, a2, a3)) -> do let ix = timesThree (indexOfCountry (Country n)) TA.unsafeWrite m ix (charToWord8 a1) TA.unsafeWrite m (ix + 1) (charToWord8 a2) @@ -189,9 +193,9 @@ allAlphaTwoLower = mapTextArray toLower allAlphaTwoUpper {-# NOINLINE allAlphaTwoLower #-} decodeTrie :: Trie -decodeTrie = trieFromList (map (\(a,Country x) -> (a,x)) (HM.toList decodeMap)) +decodeTrie = trieFromList (map (\(a, Country x) -> (a, x)) (HM.toList decodeMap)) {-# NOINLINE decodeTrie #-} decodeTrieUtf8 :: TrieByte -decodeTrieUtf8 = trieByteFromList (map (\(a,Country x) -> (TE.encodeUtf8 a,x)) (HM.toList decodeMap)) +decodeTrieUtf8 = trieByteFromList (map (\(a, Country x) -> (TE.encodeUtf8 a, x)) (HM.toList decodeMap)) {-# NOINLINE decodeTrieUtf8 #-} diff --git a/country/src/Country/Subdivision.hs b/country/src/Country/Subdivision.hs index 315f60d..e648245 100644 --- a/country/src/Country/Subdivision.hs +++ b/country/src/Country/Subdivision.hs @@ -6,53 +6,55 @@ module Country.Subdivision ( Subdivision - -- * Accessors + + -- * Accessors , encodeAlpha , encodeAlphaShort , encodeEnglish , encodeEnglishShort , category - -- * Decoding + + -- * Decoding , decodeAlpha , decodeEnglish , decodeEnglishUtf8Bytes ) where import Data.Bytes (Bytes) -import Data.Hashable (Hashable) import Data.HashMap.Strict (HashMap) +import Data.Hashable (Hashable) import Data.Primitive.Contiguous (index) import Data.Primitive.Types (Prim) import Data.Text (Text) -import Data.Text.Short (ShortText) import Data.Text.Encoding (encodeUtf8) +import Data.Text.Short (ShortText) import Data.Word (Word16) import Foreign.Storable (Storable) import qualified Country.Unexposed.Subdivision as Arrays -import qualified Data.Bytes.HashMap.Word as BytesHashMap import qualified Data.ByteString as ByteString +import qualified Data.Bytes.HashMap.Word as BytesHashMap import qualified Data.HashMap.Strict as HM import qualified Data.Primitive.Contiguous as Arr import qualified Data.Text as T import qualified GHC.Exts as Exts newtype Subdivision = Subdivision Word16 - deriving (Eq,Ord,Prim,Hashable,Storable) + deriving (Eq, Ord, Prim, Hashable, Storable) instance Show Subdivision where show = show . encodeAlpha instance Enum Subdivision where fromEnum (Subdivision w) = fromIntegral w - toEnum number = if number >= 0 && number < Arrays.actualNumberOfSubdivisions - then Subdivision (fromIntegral number) - else error ("toEnum: cannot convert " ++ show number ++ " to Subdivision") + toEnum number = + if number >= 0 && number < Arrays.actualNumberOfSubdivisions + then Subdivision (fromIntegral number) + else error ("toEnum: cannot convert " ++ show number ++ " to Subdivision") instance Bounded Subdivision where minBound = Subdivision 0 maxBound = Subdivision (fromIntegral $ Arrays.actualNumberOfSubdivisions - 1) - -- country :: Subdivision -> Country -- country (Subdivision i) = index Arrays.countryArray i @@ -71,43 +73,49 @@ encodeEnglishShort (Subdivision i) = index Arrays.nameArrayShort (fromIntegral @ category :: Subdivision -> Text category (Subdivision i) = index Arrays.categoryArray (fromIntegral @Word16 @Int i) - -- | Decode a 'Subdivision' using its ISO subdivision code. decodeAlpha :: Text -> Maybe Subdivision decodeAlpha = flip HM.lookup alphaHashMap alphaHashMap :: HashMap Text Subdivision -alphaHashMap = Arr.ifoldl' - (\hm i x -> - HM.insert x (Subdivision $ fromIntegral i) - $ hm - ) - HM.empty Arrays.codeArray +alphaHashMap = + Arr.ifoldl' + ( \hm i x -> + HM.insert x (Subdivision $ fromIntegral i) $ + hm + ) + HM.empty + Arrays.codeArray {-# NOINLINE alphaHashMap #-} --- | Decode a 'Subdivision' using its ISO subdivision English name --- It's not terribly forgiving, accepting only the official(?) names I found on wiki. +{- | Decode a 'Subdivision' using its ISO subdivision English name +It's not terribly forgiving, accepting only the official(?) names I found on wiki. +-} decodeEnglish :: Text -> Maybe Subdivision decodeEnglish = flip HM.lookup englishHashMap englishHashMap :: HashMap Text Subdivision -englishHashMap = Arr.ifoldl' - (\hm i x -> - let place = Subdivision (fromIntegral i) - in HM.insert x place - $ HM.insert (T.toLower $ x) place - $ hm - ) - HM.empty Arrays.nameArray +englishHashMap = + Arr.ifoldl' + ( \hm i x -> + let place = Subdivision (fromIntegral i) + in HM.insert x place $ + HM.insert (T.toLower $ x) place $ + hm + ) + HM.empty + Arrays.nameArray {-# NOINLINE englishHashMap #-} englishHashMapUtf8Bytes :: BytesHashMap.Map {-# NOINLINE englishHashMapUtf8Bytes #-} -englishHashMapUtf8Bytes = BytesHashMap.fromTrustedList - ( imap - (\i t -> (Exts.fromList (ByteString.unpack (encodeUtf8 t)),fromIntegral i) - ) (Exts.toList Arrays.nameArray) - ) +englishHashMapUtf8Bytes = + BytesHashMap.fromTrustedList + ( imap + ( \i t -> (Exts.fromList (ByteString.unpack (encodeUtf8 t)), fromIntegral i) + ) + (Exts.toList Arrays.nameArray) + ) decodeEnglishUtf8Bytes :: Bytes -> Maybe Subdivision decodeEnglishUtf8Bytes !bs = case BytesHashMap.lookup bs englishHashMapUtf8Bytes of @@ -116,6 +124,6 @@ decodeEnglishUtf8Bytes !bs = case BytesHashMap.lookup bs englishHashMapUtf8Bytes imap :: (Int -> a -> b) -> [a] -> [b] imap f ls = go 0 ls - where - go !i (x:xs) = f i x : go (i + 1) xs - go !_ [] = [] + where + go !i (x : xs) = f i x : go (i + 1) xs + go !_ [] = [] diff --git a/country/src/Country/Unexposed/Encode/English.hs b/country/src/Country/Unexposed/Encode/English.hs index 9e6a703..645e2f6 100644 --- a/country/src/Country/Unexposed/Encode/English.hs +++ b/country/src/Country/Unexposed/Encode/English.hs @@ -4,262 +4,262 @@ module Country.Unexposed.Encode.English ) where import Data.Text (Text) -import Data.Word (Word16) import qualified Data.Text as T +import Data.Word (Word16) --- first value is country code, second is english name, +-- first value is country code, second is english name, -- third is two char code, fourth is three char code. -countryNameQuads :: [(Word16,Text,(Char,Char),(Char,Char,Char))] +countryNameQuads :: [(Word16, Text, (Char, Char), (Char, Char, Char))] countryNameQuads = - [ (4, T.pack "Afghanistan",('A','F'),('A','F','G')) - , (248, T.pack "Åland Islands",('A','X'),('A','L','A')) - , (8, T.pack "Albania",('A','L'),('A','L','B')) - , (12, T.pack "Algeria",('D','Z'),('D','Z','A')) - , (16, T.pack "American Samoa",('A','S'),('A','S','M')) - , (20, T.pack "Andorra",('A','D'),('A','N','D')) - , (24, T.pack "Angola",('A','O'),('A','G','O')) - , (660, T.pack "Anguilla",('A','I'),('A','I','A')) - , (10, T.pack "Antarctica",('A','Q'),('A','T','A')) - , (28, T.pack "Antigua and Barbuda",('A','G'),('A','T','G')) - , (32, T.pack "Argentina",('A','R'),('A','R','G')) - , (51, T.pack "Armenia",('A','M'),('A','R','M')) - , (533, T.pack "Aruba",('A','W'),('A','B','W')) - , (36, T.pack "Australia",('A','U'),('A','U','S')) - , (40, T.pack "Austria",('A','T'),('A','U','T')) - , (31, T.pack "Azerbaijan",('A','Z'),('A','Z','E')) - , (44, T.pack "Bahamas",('B','S'),('B','H','S')) - , (48, T.pack "Bahrain",('B','H'),('B','H','R')) - , (50, T.pack "Bangladesh",('B','D'),('B','G','D')) - , (52, T.pack "Barbados",('B','B'),('B','R','B')) - , (112, T.pack "Belarus",('B','Y'),('B','L','R')) - , (56, T.pack "Belgium",('B','E'),('B','E','L')) - , (84, T.pack "Belize",('B','Z'),('B','L','Z')) - , (204, T.pack "Benin",('B','J'),('B','E','N')) - , (60, T.pack "Bermuda",('B','M'),('B','M','U')) - , (64, T.pack "Bhutan",('B','T'),('B','T','N')) - , (68, T.pack "Bolivia (Plurinational State of)",('B','O'),('B','O','L')) - , (535, T.pack "Bonaire, Sint Eustatius and Saba",('B','Q'),('B','E','S')) - , (70, T.pack "Bosnia and Herzegovina",('B','A'),('B','I','H')) - , (72, T.pack "Botswana",('B','W'),('B','W','A')) - , (74, T.pack "Bouvet Island",('B','V'),('B','V','T')) - , (76, T.pack "Brazil",('B','R'),('B','R','A')) - , (86, T.pack "British Indian Ocean Territory",('I','O'),('I','O','T')) - , (96, T.pack "Brunei Darussalam",('B','N'),('B','R','N')) - , (100, T.pack "Bulgaria",('B','G'),('B','G','R')) - , (854, T.pack "Burkina Faso",('B','F'),('B','F','A')) - , (108, T.pack "Burundi",('B','I'),('B','D','I')) - , (116, T.pack "Cambodia",('K','H'),('K','H','M')) - , (120, T.pack "Cameroon",('C','M'),('C','M','R')) - , (124, T.pack "Canada",('C','A'),('C','A','N')) - , (132, T.pack "Cabo Verde",('C','V'),('C','P','V')) - , (136, T.pack "Cayman Islands",('K','Y'),('C','Y','M')) - , (140, T.pack "Central African Republic",('C','F'),('C','A','F')) - , (148, T.pack "Chad",('T','D'),('T','C','D')) - , (152, T.pack "Chile",('C','L'),('C','H','L')) - , (156, T.pack "China",('C','N'),('C','H','N')) - , (162, T.pack "Christmas Island",('C','X'),('C','X','R')) - , (166, T.pack "Cocos (Keeling) Islands",('C','C'),('C','C','K')) - , (170, T.pack "Colombia",('C','O'),('C','O','L')) - , (174, T.pack "Comoros",('K','M'),('C','O','M')) - , (178, T.pack "Congo",('C','G'),('C','O','G')) - , (180, T.pack "Congo (Democratic Republic of the)",('C','D'),('C','O','D')) - , (184, T.pack "Cook Islands",('C','K'),('C','O','K')) - , (188, T.pack "Costa Rica",('C','R'),('C','R','I')) - , (384, T.pack "Côte d'Ivoire",('C','I'),('C','I','V')) - , (191, T.pack "Croatia",('H','R'),('H','R','V')) - , (192, T.pack "Cuba",('C','U'),('C','U','B')) - , (531, T.pack "Curaçao",('C','W'),('C','U','W')) - , (196, T.pack "Cyprus",('C','Y'),('C','Y','P')) - , (203, T.pack "Czech Republic",('C','Z'),('C','Z','E')) - , (208, T.pack "Denmark",('D','K'),('D','N','K')) - , (262, T.pack "Djibouti",('D','J'),('D','J','I')) - , (212, T.pack "Dominica",('D','M'),('D','M','A')) - , (214, T.pack "Dominican Republic",('D','O'),('D','O','M')) - , (218, T.pack "Ecuador",('E','C'),('E','C','U')) - , (818, T.pack "Egypt",('E','G'),('E','G','Y')) - , (222, T.pack "El Salvador",('S','V'),('S','L','V')) - , (226, T.pack "Equatorial Guinea",('G','Q'),('G','N','Q')) - , (232, T.pack "Eritrea",('E','R'),('E','R','I')) - , (233, T.pack "Estonia",('E','E'),('E','S','T')) - , (231, T.pack "Ethiopia",('E','T'),('E','T','H')) - , (238, T.pack "Falkland Islands (Malvinas)",('F','K'),('F','L','K')) - , (234, T.pack "Faroe Islands",('F','O'),('F','R','O')) - , (242, T.pack "Fiji",('F','J'),('F','J','I')) - , (246, T.pack "Finland",('F','I'),('F','I','N')) - , (250, T.pack "France",('F','R'),('F','R','A')) - , (254, T.pack "French Guiana",('G','F'),('G','U','F')) - , (258, T.pack "French Polynesia",('P','F'),('P','Y','F')) - , (260, T.pack "French Southern Territories",('T','F'),('A','T','F')) - , (266, T.pack "Gabon",('G','A'),('G','A','B')) - , (270, T.pack "Gambia",('G','M'),('G','M','B')) - , (268, T.pack "Georgia",('G','E'),('G','E','O')) - , (276, T.pack "Germany",('D','E'),('D','E','U')) - , (288, T.pack "Ghana",('G','H'),('G','H','A')) - , (292, T.pack "Gibraltar",('G','I'),('G','I','B')) - , (300, T.pack "Greece",('G','R'),('G','R','C')) - , (304, T.pack "Greenland",('G','L'),('G','R','L')) - , (308, T.pack "Grenada",('G','D'),('G','R','D')) - , (312, T.pack "Guadeloupe",('G','P'),('G','L','P')) - , (316, T.pack "Guam",('G','U'),('G','U','M')) - , (320, T.pack "Guatemala",('G','T'),('G','T','M')) - , (831, T.pack "Guernsey",('G','G'),('G','G','Y')) - , (324, T.pack "Guinea",('G','N'),('G','I','N')) - , (624, T.pack "Guinea-Bissau",('G','W'),('G','N','B')) - , (328, T.pack "Guyana",('G','Y'),('G','U','Y')) - , (332, T.pack "Haiti",('H','T'),('H','T','I')) - , (334, T.pack "Heard Island and McDonald Islands",('H','M'),('H','M','D')) - , (336, T.pack "Holy See",('V','A'),('V','A','T')) - , (340, T.pack "Honduras",('H','N'),('H','N','D')) - , (344, T.pack "Hong Kong",('H','K'),('H','K','G')) - , (348, T.pack "Hungary",('H','U'),('H','U','N')) - , (352, T.pack "Iceland",('I','S'),('I','S','L')) - , (356, T.pack "India",('I','N'),('I','N','D')) - , (360, T.pack "Indonesia",('I','D'),('I','D','N')) - , (364, T.pack "Iran (Islamic Republic of)",('I','R'),('I','R','N')) - , (368, T.pack "Iraq",('I','Q'),('I','R','Q')) - , (372, T.pack "Ireland",('I','E'),('I','R','L')) - , (833, T.pack "Isle of Man",('I','M'),('I','M','N')) - , (376, T.pack "Israel",('I','L'),('I','S','R')) - , (380, T.pack "Italy",('I','T'),('I','T','A')) - , (388, T.pack "Jamaica",('J','M'),('J','A','M')) - , (392, T.pack "Japan",('J','P'),('J','P','N')) - , (832, T.pack "Jersey",('J','E'),('J','E','Y')) - , (400, T.pack "Jordan",('J','O'),('J','O','R')) - , (398, T.pack "Kazakhstan",('K','Z'),('K','A','Z')) - , (404, T.pack "Kenya",('K','E'),('K','E','N')) - , (296, T.pack "Kiribati",('K','I'),('K','I','R')) - , (408, T.pack "Korea (Democratic People's Republic of)",('K','P'),('P','R','K')) - , (410, T.pack "Korea (Republic of)",('K','R'),('K','O','R')) - , (414, T.pack "Kuwait",('K','W'),('K','W','T')) - , (417, T.pack "Kyrgyzstan",('K','G'),('K','G','Z')) - , (418, T.pack "Lao People's Democratic Republic",('L','A'),('L','A','O')) - , (428, T.pack "Latvia",('L','V'),('L','V','A')) - , (422, T.pack "Lebanon",('L','B'),('L','B','N')) - , (426, T.pack "Lesotho",('L','S'),('L','S','O')) - , (430, T.pack "Liberia",('L','R'),('L','B','R')) - , (434, T.pack "Libya",('L','Y'),('L','B','Y')) - , (438, T.pack "Liechtenstein",('L','I'),('L','I','E')) - , (440, T.pack "Lithuania",('L','T'),('L','T','U')) - , (442, T.pack "Luxembourg",('L','U'),('L','U','X')) - , (446, T.pack "Macao",('M','O'),('M','A','C')) - , (807, T.pack "Macedonia (the former Yugoslav Republic of)",('M','K'),('M','K','D')) - , (450, T.pack "Madagascar",('M','G'),('M','D','G')) - , (454, T.pack "Malawi",('M','W'),('M','W','I')) - , (458, T.pack "Malaysia",('M','Y'),('M','Y','S')) - , (462, T.pack "Maldives",('M','V'),('M','D','V')) - , (466, T.pack "Mali",('M','L'),('M','L','I')) - , (470, T.pack "Malta",('M','T'),('M','L','T')) - , (584, T.pack "Marshall Islands",('M','H'),('M','H','L')) - , (474, T.pack "Martinique",('M','Q'),('M','T','Q')) - , (478, T.pack "Mauritania",('M','R'),('M','R','T')) - , (480, T.pack "Mauritius",('M','U'),('M','U','S')) - , (175, T.pack "Mayotte",('Y','T'),('M','Y','T')) - , (484, T.pack "Mexico",('M','X'),('M','E','X')) - , (583, T.pack "Micronesia (Federated States of)",('F','M'),('F','S','M')) - , (498, T.pack "Moldova (Republic of)",('M','D'),('M','D','A')) - , (492, T.pack "Monaco",('M','C'),('M','C','O')) - , (496, T.pack "Mongolia",('M','N'),('M','N','G')) - , (499, T.pack "Montenegro",('M','E'),('M','N','E')) - , (500, T.pack "Montserrat",('M','S'),('M','S','R')) - , (504, T.pack "Morocco",('M','A'),('M','A','R')) - , (508, T.pack "Mozambique",('M','Z'),('M','O','Z')) - , (104, T.pack "Myanmar",('M','M'),('M','M','R')) - , (516, T.pack "Namibia",('N','A'),('N','A','M')) - , (520, T.pack "Nauru",('N','R'),('N','R','U')) - , (524, T.pack "Nepal",('N','P'),('N','P','L')) - , (528, T.pack "Netherlands",('N','L'),('N','L','D')) - , (540, T.pack "New Caledonia",('N','C'),('N','C','L')) - , (554, T.pack "New Zealand",('N','Z'),('N','Z','L')) - , (558, T.pack "Nicaragua",('N','I'),('N','I','C')) - , (562, T.pack "Niger",('N','E'),('N','E','R')) - , (566, T.pack "Nigeria",('N','G'),('N','G','A')) - , (570, T.pack "Niue",('N','U'),('N','I','U')) - , (574, T.pack "Norfolk Island",('N','F'),('N','F','K')) - , (580, T.pack "Northern Mariana Islands",('M','P'),('M','N','P')) - , (578, T.pack "Norway",('N','O'),('N','O','R')) - , (512, T.pack "Oman",('O','M'),('O','M','N')) - , (586, T.pack "Pakistan",('P','K'),('P','A','K')) - , (585, T.pack "Palau",('P','W'),('P','L','W')) - , (275, T.pack "Palestine, State of",('P','S'),('P','S','E')) - , (591, T.pack "Panama",('P','A'),('P','A','N')) - , (598, T.pack "Papua New Guinea",('P','G'),('P','N','G')) - , (600, T.pack "Paraguay",('P','Y'),('P','R','Y')) - , (604, T.pack "Peru",('P','E'),('P','E','R')) - , (608, T.pack "Philippines",('P','H'),('P','H','L')) - , (612, T.pack "Pitcairn",('P','N'),('P','C','N')) - , (616, T.pack "Poland",('P','L'),('P','O','L')) - , (620, T.pack "Portugal",('P','T'),('P','R','T')) - , (630, T.pack "Puerto Rico",('P','R'),('P','R','I')) - , (634, T.pack "Qatar",('Q','A'),('Q','A','T')) - , (638, T.pack "Réunion",('R','E'),('R','E','U')) - , (642, T.pack "Romania",('R','O'),('R','O','U')) - , (643, T.pack "Russian Federation",('R','U'),('R','U','S')) - , (646, T.pack "Rwanda",('R','W'),('R','W','A')) - , (652, T.pack "Saint Barthélemy",('B','L'),('B','L','M')) - , (654, T.pack "Saint Helena, Ascension and Tristan da Cunha",('S','H'),('S','H','N')) - , (659, T.pack "Saint Kitts and Nevis",('K','N'),('K','N','A')) - , (662, T.pack "Saint Lucia",('L','C'),('L','C','A')) - , (663, T.pack "Saint Martin (French part)",('M','F'),('M','A','F')) - , (666, T.pack "Saint Pierre and Miquelon",('P','M'),('S','P','M')) - , (670, T.pack "Saint Vincent and the Grenadines",('V','C'),('V','C','T')) - , (882, T.pack "Samoa",('W','S'),('W','S','M')) - , (674, T.pack "San Marino",('S','M'),('S','M','R')) - , (678, T.pack "Sao Tome and Principe",('S','T'),('S','T','P')) - , (682, T.pack "Saudi Arabia",('S','A'),('S','A','U')) - , (686, T.pack "Senegal",('S','N'),('S','E','N')) - , (688, T.pack "Serbia",('R','S'),('S','R','B')) - , (690, T.pack "Seychelles",('S','C'),('S','Y','C')) - , (694, T.pack "Sierra Leone",('S','L'),('S','L','E')) - , (702, T.pack "Singapore",('S','G'),('S','G','P')) - , (534, T.pack "Sint Maarten (Dutch part)",('S','X'),('S','X','M')) - , (703, T.pack "Slovakia",('S','K'),('S','V','K')) - , (705, T.pack "Slovenia",('S','I'),('S','V','N')) - , (90, T.pack "Solomon Islands",('S','B'),('S','L','B')) - , (706, T.pack "Somalia",('S','O'),('S','O','M')) - , (710, T.pack "South Africa",('Z','A'),('Z','A','F')) - , (239, T.pack "South Georgia and the South Sandwich Islands",('G','S'),('S','G','S')) - , (728, T.pack "South Sudan",('S','S'),('S','S','D')) - , (724, T.pack "Spain",('E','S'),('E','S','P')) - , (144, T.pack "Sri Lanka",('L','K'),('L','K','A')) - , (729, T.pack "Sudan",('S','D'),('S','D','N')) - , (740, T.pack "Suriname",('S','R'),('S','U','R')) - , (744, T.pack "Svalbard and Jan Mayen",('S','J'),('S','J','M')) - , (748, T.pack "Swaziland",('S','Z'),('S','W','Z')) - , (752, T.pack "Sweden",('S','E'),('S','W','E')) - , (756, T.pack "Switzerland",('C','H'),('C','H','E')) - , (760, T.pack "Syrian Arab Republic",('S','Y'),('S','Y','R')) - , (158, T.pack "Taiwan, Province of China",('T','W'),('T','W','N')) - , (762, T.pack "Tajikistan",('T','J'),('T','J','K')) - , (834, T.pack "Tanzania, United Republic of",('T','Z'),('T','Z','A')) - , (764, T.pack "Thailand",('T','H'),('T','H','A')) - , (626, T.pack "Timor-Leste",('T','L'),('T','L','S')) - , (768, T.pack "Togo",('T','G'),('T','G','O')) - , (772, T.pack "Tokelau",('T','K'),('T','K','L')) - , (776, T.pack "Tonga",('T','O'),('T','O','N')) - , (780, T.pack "Trinidad and Tobago",('T','T'),('T','T','O')) - , (788, T.pack "Tunisia",('T','N'),('T','U','N')) - , (792, T.pack "Turkey",('T','R'),('T','U','R')) - , (795, T.pack "Turkmenistan",('T','M'),('T','K','M')) - , (796, T.pack "Turks and Caicos Islands",('T','C'),('T','C','A')) - , (798, T.pack "Tuvalu",('T','V'),('T','U','V')) - , (800, T.pack "Uganda",('U','G'),('U','G','A')) - , (804, T.pack "Ukraine",('U','A'),('U','K','R')) - , (784, T.pack "United Arab Emirates",('A','E'),('A','R','E')) - , (826, T.pack "United Kingdom of Great Britain and Northern Ireland",('G','B'),('G','B','R')) - , (840, T.pack "United States of America",('U','S'),('U','S','A')) - , (581, T.pack "United States Minor Outlying Islands",('U','M'),('U','M','I')) - , (858, T.pack "Uruguay",('U','Y'),('U','R','Y')) - , (860, T.pack "Uzbekistan",('U','Z'),('U','Z','B')) - , (548, T.pack "Vanuatu",('V','U'),('V','U','T')) - , (862, T.pack "Venezuela (Bolivarian Republic of)",('V','E'),('V','E','N')) - , (704, T.pack "Viet Nam",('V','N'),('V','N','M')) - , (92, T.pack "Virgin Islands (British)",('V','G'),('V','G','B')) - , (850, T.pack "Virgin Islands (U.S.)",('V','I'),('V','I','R')) - , (876, T.pack "Wallis and Futuna",('W','F'),('W','L','F')) - , (732, T.pack "Western Sahara",('E','H'),('E','S','H')) - , (887, T.pack "Yemen",('Y','E'),('Y','E','M')) - , (894, T.pack "Zambia",('Z','M'),('Z','M','B')) - , (716, T.pack "Zimbabwe",('Z','W'),('Z','W','E')) - , (383, T.pack "Kosovo",('X','K'),('X','K','X')) + [ (4, T.pack "Afghanistan", ('A', 'F'), ('A', 'F', 'G')) + , (248, T.pack "Åland Islands", ('A', 'X'), ('A', 'L', 'A')) + , (8, T.pack "Albania", ('A', 'L'), ('A', 'L', 'B')) + , (12, T.pack "Algeria", ('D', 'Z'), ('D', 'Z', 'A')) + , (16, T.pack "American Samoa", ('A', 'S'), ('A', 'S', 'M')) + , (20, T.pack "Andorra", ('A', 'D'), ('A', 'N', 'D')) + , (24, T.pack "Angola", ('A', 'O'), ('A', 'G', 'O')) + , (660, T.pack "Anguilla", ('A', 'I'), ('A', 'I', 'A')) + , (10, T.pack "Antarctica", ('A', 'Q'), ('A', 'T', 'A')) + , (28, T.pack "Antigua and Barbuda", ('A', 'G'), ('A', 'T', 'G')) + , (32, T.pack "Argentina", ('A', 'R'), ('A', 'R', 'G')) + , (51, T.pack "Armenia", ('A', 'M'), ('A', 'R', 'M')) + , (533, T.pack "Aruba", ('A', 'W'), ('A', 'B', 'W')) + , (36, T.pack "Australia", ('A', 'U'), ('A', 'U', 'S')) + , (40, T.pack "Austria", ('A', 'T'), ('A', 'U', 'T')) + , (31, T.pack "Azerbaijan", ('A', 'Z'), ('A', 'Z', 'E')) + , (44, T.pack "Bahamas", ('B', 'S'), ('B', 'H', 'S')) + , (48, T.pack "Bahrain", ('B', 'H'), ('B', 'H', 'R')) + , (50, T.pack "Bangladesh", ('B', 'D'), ('B', 'G', 'D')) + , (52, T.pack "Barbados", ('B', 'B'), ('B', 'R', 'B')) + , (112, T.pack "Belarus", ('B', 'Y'), ('B', 'L', 'R')) + , (56, T.pack "Belgium", ('B', 'E'), ('B', 'E', 'L')) + , (84, T.pack "Belize", ('B', 'Z'), ('B', 'L', 'Z')) + , (204, T.pack "Benin", ('B', 'J'), ('B', 'E', 'N')) + , (60, T.pack "Bermuda", ('B', 'M'), ('B', 'M', 'U')) + , (64, T.pack "Bhutan", ('B', 'T'), ('B', 'T', 'N')) + , (68, T.pack "Bolivia (Plurinational State of)", ('B', 'O'), ('B', 'O', 'L')) + , (535, T.pack "Bonaire, Sint Eustatius and Saba", ('B', 'Q'), ('B', 'E', 'S')) + , (70, T.pack "Bosnia and Herzegovina", ('B', 'A'), ('B', 'I', 'H')) + , (72, T.pack "Botswana", ('B', 'W'), ('B', 'W', 'A')) + , (74, T.pack "Bouvet Island", ('B', 'V'), ('B', 'V', 'T')) + , (76, T.pack "Brazil", ('B', 'R'), ('B', 'R', 'A')) + , (86, T.pack "British Indian Ocean Territory", ('I', 'O'), ('I', 'O', 'T')) + , (96, T.pack "Brunei Darussalam", ('B', 'N'), ('B', 'R', 'N')) + , (100, T.pack "Bulgaria", ('B', 'G'), ('B', 'G', 'R')) + , (854, T.pack "Burkina Faso", ('B', 'F'), ('B', 'F', 'A')) + , (108, T.pack "Burundi", ('B', 'I'), ('B', 'D', 'I')) + , (116, T.pack "Cambodia", ('K', 'H'), ('K', 'H', 'M')) + , (120, T.pack "Cameroon", ('C', 'M'), ('C', 'M', 'R')) + , (124, T.pack "Canada", ('C', 'A'), ('C', 'A', 'N')) + , (132, T.pack "Cabo Verde", ('C', 'V'), ('C', 'P', 'V')) + , (136, T.pack "Cayman Islands", ('K', 'Y'), ('C', 'Y', 'M')) + , (140, T.pack "Central African Republic", ('C', 'F'), ('C', 'A', 'F')) + , (148, T.pack "Chad", ('T', 'D'), ('T', 'C', 'D')) + , (152, T.pack "Chile", ('C', 'L'), ('C', 'H', 'L')) + , (156, T.pack "China", ('C', 'N'), ('C', 'H', 'N')) + , (162, T.pack "Christmas Island", ('C', 'X'), ('C', 'X', 'R')) + , (166, T.pack "Cocos (Keeling) Islands", ('C', 'C'), ('C', 'C', 'K')) + , (170, T.pack "Colombia", ('C', 'O'), ('C', 'O', 'L')) + , (174, T.pack "Comoros", ('K', 'M'), ('C', 'O', 'M')) + , (178, T.pack "Congo", ('C', 'G'), ('C', 'O', 'G')) + , (180, T.pack "Congo (Democratic Republic of the)", ('C', 'D'), ('C', 'O', 'D')) + , (184, T.pack "Cook Islands", ('C', 'K'), ('C', 'O', 'K')) + , (188, T.pack "Costa Rica", ('C', 'R'), ('C', 'R', 'I')) + , (384, T.pack "Côte d'Ivoire", ('C', 'I'), ('C', 'I', 'V')) + , (191, T.pack "Croatia", ('H', 'R'), ('H', 'R', 'V')) + , (192, T.pack "Cuba", ('C', 'U'), ('C', 'U', 'B')) + , (531, T.pack "Curaçao", ('C', 'W'), ('C', 'U', 'W')) + , (196, T.pack "Cyprus", ('C', 'Y'), ('C', 'Y', 'P')) + , (203, T.pack "Czech Republic", ('C', 'Z'), ('C', 'Z', 'E')) + , (208, T.pack "Denmark", ('D', 'K'), ('D', 'N', 'K')) + , (262, T.pack "Djibouti", ('D', 'J'), ('D', 'J', 'I')) + , (212, T.pack "Dominica", ('D', 'M'), ('D', 'M', 'A')) + , (214, T.pack "Dominican Republic", ('D', 'O'), ('D', 'O', 'M')) + , (218, T.pack "Ecuador", ('E', 'C'), ('E', 'C', 'U')) + , (818, T.pack "Egypt", ('E', 'G'), ('E', 'G', 'Y')) + , (222, T.pack "El Salvador", ('S', 'V'), ('S', 'L', 'V')) + , (226, T.pack "Equatorial Guinea", ('G', 'Q'), ('G', 'N', 'Q')) + , (232, T.pack "Eritrea", ('E', 'R'), ('E', 'R', 'I')) + , (233, T.pack "Estonia", ('E', 'E'), ('E', 'S', 'T')) + , (231, T.pack "Ethiopia", ('E', 'T'), ('E', 'T', 'H')) + , (238, T.pack "Falkland Islands (Malvinas)", ('F', 'K'), ('F', 'L', 'K')) + , (234, T.pack "Faroe Islands", ('F', 'O'), ('F', 'R', 'O')) + , (242, T.pack "Fiji", ('F', 'J'), ('F', 'J', 'I')) + , (246, T.pack "Finland", ('F', 'I'), ('F', 'I', 'N')) + , (250, T.pack "France", ('F', 'R'), ('F', 'R', 'A')) + , (254, T.pack "French Guiana", ('G', 'F'), ('G', 'U', 'F')) + , (258, T.pack "French Polynesia", ('P', 'F'), ('P', 'Y', 'F')) + , (260, T.pack "French Southern Territories", ('T', 'F'), ('A', 'T', 'F')) + , (266, T.pack "Gabon", ('G', 'A'), ('G', 'A', 'B')) + , (270, T.pack "Gambia", ('G', 'M'), ('G', 'M', 'B')) + , (268, T.pack "Georgia", ('G', 'E'), ('G', 'E', 'O')) + , (276, T.pack "Germany", ('D', 'E'), ('D', 'E', 'U')) + , (288, T.pack "Ghana", ('G', 'H'), ('G', 'H', 'A')) + , (292, T.pack "Gibraltar", ('G', 'I'), ('G', 'I', 'B')) + , (300, T.pack "Greece", ('G', 'R'), ('G', 'R', 'C')) + , (304, T.pack "Greenland", ('G', 'L'), ('G', 'R', 'L')) + , (308, T.pack "Grenada", ('G', 'D'), ('G', 'R', 'D')) + , (312, T.pack "Guadeloupe", ('G', 'P'), ('G', 'L', 'P')) + , (316, T.pack "Guam", ('G', 'U'), ('G', 'U', 'M')) + , (320, T.pack "Guatemala", ('G', 'T'), ('G', 'T', 'M')) + , (831, T.pack "Guernsey", ('G', 'G'), ('G', 'G', 'Y')) + , (324, T.pack "Guinea", ('G', 'N'), ('G', 'I', 'N')) + , (624, T.pack "Guinea-Bissau", ('G', 'W'), ('G', 'N', 'B')) + , (328, T.pack "Guyana", ('G', 'Y'), ('G', 'U', 'Y')) + , (332, T.pack "Haiti", ('H', 'T'), ('H', 'T', 'I')) + , (334, T.pack "Heard Island and McDonald Islands", ('H', 'M'), ('H', 'M', 'D')) + , (336, T.pack "Holy See", ('V', 'A'), ('V', 'A', 'T')) + , (340, T.pack "Honduras", ('H', 'N'), ('H', 'N', 'D')) + , (344, T.pack "Hong Kong", ('H', 'K'), ('H', 'K', 'G')) + , (348, T.pack "Hungary", ('H', 'U'), ('H', 'U', 'N')) + , (352, T.pack "Iceland", ('I', 'S'), ('I', 'S', 'L')) + , (356, T.pack "India", ('I', 'N'), ('I', 'N', 'D')) + , (360, T.pack "Indonesia", ('I', 'D'), ('I', 'D', 'N')) + , (364, T.pack "Iran (Islamic Republic of)", ('I', 'R'), ('I', 'R', 'N')) + , (368, T.pack "Iraq", ('I', 'Q'), ('I', 'R', 'Q')) + , (372, T.pack "Ireland", ('I', 'E'), ('I', 'R', 'L')) + , (833, T.pack "Isle of Man", ('I', 'M'), ('I', 'M', 'N')) + , (376, T.pack "Israel", ('I', 'L'), ('I', 'S', 'R')) + , (380, T.pack "Italy", ('I', 'T'), ('I', 'T', 'A')) + , (388, T.pack "Jamaica", ('J', 'M'), ('J', 'A', 'M')) + , (392, T.pack "Japan", ('J', 'P'), ('J', 'P', 'N')) + , (832, T.pack "Jersey", ('J', 'E'), ('J', 'E', 'Y')) + , (400, T.pack "Jordan", ('J', 'O'), ('J', 'O', 'R')) + , (398, T.pack "Kazakhstan", ('K', 'Z'), ('K', 'A', 'Z')) + , (404, T.pack "Kenya", ('K', 'E'), ('K', 'E', 'N')) + , (296, T.pack "Kiribati", ('K', 'I'), ('K', 'I', 'R')) + , (408, T.pack "Korea (Democratic People's Republic of)", ('K', 'P'), ('P', 'R', 'K')) + , (410, T.pack "Korea (Republic of)", ('K', 'R'), ('K', 'O', 'R')) + , (414, T.pack "Kuwait", ('K', 'W'), ('K', 'W', 'T')) + , (417, T.pack "Kyrgyzstan", ('K', 'G'), ('K', 'G', 'Z')) + , (418, T.pack "Lao People's Democratic Republic", ('L', 'A'), ('L', 'A', 'O')) + , (428, T.pack "Latvia", ('L', 'V'), ('L', 'V', 'A')) + , (422, T.pack "Lebanon", ('L', 'B'), ('L', 'B', 'N')) + , (426, T.pack "Lesotho", ('L', 'S'), ('L', 'S', 'O')) + , (430, T.pack "Liberia", ('L', 'R'), ('L', 'B', 'R')) + , (434, T.pack "Libya", ('L', 'Y'), ('L', 'B', 'Y')) + , (438, T.pack "Liechtenstein", ('L', 'I'), ('L', 'I', 'E')) + , (440, T.pack "Lithuania", ('L', 'T'), ('L', 'T', 'U')) + , (442, T.pack "Luxembourg", ('L', 'U'), ('L', 'U', 'X')) + , (446, T.pack "Macao", ('M', 'O'), ('M', 'A', 'C')) + , (807, T.pack "Macedonia (the former Yugoslav Republic of)", ('M', 'K'), ('M', 'K', 'D')) + , (450, T.pack "Madagascar", ('M', 'G'), ('M', 'D', 'G')) + , (454, T.pack "Malawi", ('M', 'W'), ('M', 'W', 'I')) + , (458, T.pack "Malaysia", ('M', 'Y'), ('M', 'Y', 'S')) + , (462, T.pack "Maldives", ('M', 'V'), ('M', 'D', 'V')) + , (466, T.pack "Mali", ('M', 'L'), ('M', 'L', 'I')) + , (470, T.pack "Malta", ('M', 'T'), ('M', 'L', 'T')) + , (584, T.pack "Marshall Islands", ('M', 'H'), ('M', 'H', 'L')) + , (474, T.pack "Martinique", ('M', 'Q'), ('M', 'T', 'Q')) + , (478, T.pack "Mauritania", ('M', 'R'), ('M', 'R', 'T')) + , (480, T.pack "Mauritius", ('M', 'U'), ('M', 'U', 'S')) + , (175, T.pack "Mayotte", ('Y', 'T'), ('M', 'Y', 'T')) + , (484, T.pack "Mexico", ('M', 'X'), ('M', 'E', 'X')) + , (583, T.pack "Micronesia (Federated States of)", ('F', 'M'), ('F', 'S', 'M')) + , (498, T.pack "Moldova (Republic of)", ('M', 'D'), ('M', 'D', 'A')) + , (492, T.pack "Monaco", ('M', 'C'), ('M', 'C', 'O')) + , (496, T.pack "Mongolia", ('M', 'N'), ('M', 'N', 'G')) + , (499, T.pack "Montenegro", ('M', 'E'), ('M', 'N', 'E')) + , (500, T.pack "Montserrat", ('M', 'S'), ('M', 'S', 'R')) + , (504, T.pack "Morocco", ('M', 'A'), ('M', 'A', 'R')) + , (508, T.pack "Mozambique", ('M', 'Z'), ('M', 'O', 'Z')) + , (104, T.pack "Myanmar", ('M', 'M'), ('M', 'M', 'R')) + , (516, T.pack "Namibia", ('N', 'A'), ('N', 'A', 'M')) + , (520, T.pack "Nauru", ('N', 'R'), ('N', 'R', 'U')) + , (524, T.pack "Nepal", ('N', 'P'), ('N', 'P', 'L')) + , (528, T.pack "Netherlands", ('N', 'L'), ('N', 'L', 'D')) + , (540, T.pack "New Caledonia", ('N', 'C'), ('N', 'C', 'L')) + , (554, T.pack "New Zealand", ('N', 'Z'), ('N', 'Z', 'L')) + , (558, T.pack "Nicaragua", ('N', 'I'), ('N', 'I', 'C')) + , (562, T.pack "Niger", ('N', 'E'), ('N', 'E', 'R')) + , (566, T.pack "Nigeria", ('N', 'G'), ('N', 'G', 'A')) + , (570, T.pack "Niue", ('N', 'U'), ('N', 'I', 'U')) + , (574, T.pack "Norfolk Island", ('N', 'F'), ('N', 'F', 'K')) + , (580, T.pack "Northern Mariana Islands", ('M', 'P'), ('M', 'N', 'P')) + , (578, T.pack "Norway", ('N', 'O'), ('N', 'O', 'R')) + , (512, T.pack "Oman", ('O', 'M'), ('O', 'M', 'N')) + , (586, T.pack "Pakistan", ('P', 'K'), ('P', 'A', 'K')) + , (585, T.pack "Palau", ('P', 'W'), ('P', 'L', 'W')) + , (275, T.pack "Palestine, State of", ('P', 'S'), ('P', 'S', 'E')) + , (591, T.pack "Panama", ('P', 'A'), ('P', 'A', 'N')) + , (598, T.pack "Papua New Guinea", ('P', 'G'), ('P', 'N', 'G')) + , (600, T.pack "Paraguay", ('P', 'Y'), ('P', 'R', 'Y')) + , (604, T.pack "Peru", ('P', 'E'), ('P', 'E', 'R')) + , (608, T.pack "Philippines", ('P', 'H'), ('P', 'H', 'L')) + , (612, T.pack "Pitcairn", ('P', 'N'), ('P', 'C', 'N')) + , (616, T.pack "Poland", ('P', 'L'), ('P', 'O', 'L')) + , (620, T.pack "Portugal", ('P', 'T'), ('P', 'R', 'T')) + , (630, T.pack "Puerto Rico", ('P', 'R'), ('P', 'R', 'I')) + , (634, T.pack "Qatar", ('Q', 'A'), ('Q', 'A', 'T')) + , (638, T.pack "Réunion", ('R', 'E'), ('R', 'E', 'U')) + , (642, T.pack "Romania", ('R', 'O'), ('R', 'O', 'U')) + , (643, T.pack "Russian Federation", ('R', 'U'), ('R', 'U', 'S')) + , (646, T.pack "Rwanda", ('R', 'W'), ('R', 'W', 'A')) + , (652, T.pack "Saint Barthélemy", ('B', 'L'), ('B', 'L', 'M')) + , (654, T.pack "Saint Helena, Ascension and Tristan da Cunha", ('S', 'H'), ('S', 'H', 'N')) + , (659, T.pack "Saint Kitts and Nevis", ('K', 'N'), ('K', 'N', 'A')) + , (662, T.pack "Saint Lucia", ('L', 'C'), ('L', 'C', 'A')) + , (663, T.pack "Saint Martin (French part)", ('M', 'F'), ('M', 'A', 'F')) + , (666, T.pack "Saint Pierre and Miquelon", ('P', 'M'), ('S', 'P', 'M')) + , (670, T.pack "Saint Vincent and the Grenadines", ('V', 'C'), ('V', 'C', 'T')) + , (882, T.pack "Samoa", ('W', 'S'), ('W', 'S', 'M')) + , (674, T.pack "San Marino", ('S', 'M'), ('S', 'M', 'R')) + , (678, T.pack "Sao Tome and Principe", ('S', 'T'), ('S', 'T', 'P')) + , (682, T.pack "Saudi Arabia", ('S', 'A'), ('S', 'A', 'U')) + , (686, T.pack "Senegal", ('S', 'N'), ('S', 'E', 'N')) + , (688, T.pack "Serbia", ('R', 'S'), ('S', 'R', 'B')) + , (690, T.pack "Seychelles", ('S', 'C'), ('S', 'Y', 'C')) + , (694, T.pack "Sierra Leone", ('S', 'L'), ('S', 'L', 'E')) + , (702, T.pack "Singapore", ('S', 'G'), ('S', 'G', 'P')) + , (534, T.pack "Sint Maarten (Dutch part)", ('S', 'X'), ('S', 'X', 'M')) + , (703, T.pack "Slovakia", ('S', 'K'), ('S', 'V', 'K')) + , (705, T.pack "Slovenia", ('S', 'I'), ('S', 'V', 'N')) + , (90, T.pack "Solomon Islands", ('S', 'B'), ('S', 'L', 'B')) + , (706, T.pack "Somalia", ('S', 'O'), ('S', 'O', 'M')) + , (710, T.pack "South Africa", ('Z', 'A'), ('Z', 'A', 'F')) + , (239, T.pack "South Georgia and the South Sandwich Islands", ('G', 'S'), ('S', 'G', 'S')) + , (728, T.pack "South Sudan", ('S', 'S'), ('S', 'S', 'D')) + , (724, T.pack "Spain", ('E', 'S'), ('E', 'S', 'P')) + , (144, T.pack "Sri Lanka", ('L', 'K'), ('L', 'K', 'A')) + , (729, T.pack "Sudan", ('S', 'D'), ('S', 'D', 'N')) + , (740, T.pack "Suriname", ('S', 'R'), ('S', 'U', 'R')) + , (744, T.pack "Svalbard and Jan Mayen", ('S', 'J'), ('S', 'J', 'M')) + , (748, T.pack "Swaziland", ('S', 'Z'), ('S', 'W', 'Z')) + , (752, T.pack "Sweden", ('S', 'E'), ('S', 'W', 'E')) + , (756, T.pack "Switzerland", ('C', 'H'), ('C', 'H', 'E')) + , (760, T.pack "Syrian Arab Republic", ('S', 'Y'), ('S', 'Y', 'R')) + , (158, T.pack "Taiwan, Province of China", ('T', 'W'), ('T', 'W', 'N')) + , (762, T.pack "Tajikistan", ('T', 'J'), ('T', 'J', 'K')) + , (834, T.pack "Tanzania, United Republic of", ('T', 'Z'), ('T', 'Z', 'A')) + , (764, T.pack "Thailand", ('T', 'H'), ('T', 'H', 'A')) + , (626, T.pack "Timor-Leste", ('T', 'L'), ('T', 'L', 'S')) + , (768, T.pack "Togo", ('T', 'G'), ('T', 'G', 'O')) + , (772, T.pack "Tokelau", ('T', 'K'), ('T', 'K', 'L')) + , (776, T.pack "Tonga", ('T', 'O'), ('T', 'O', 'N')) + , (780, T.pack "Trinidad and Tobago", ('T', 'T'), ('T', 'T', 'O')) + , (788, T.pack "Tunisia", ('T', 'N'), ('T', 'U', 'N')) + , (792, T.pack "Turkey", ('T', 'R'), ('T', 'U', 'R')) + , (795, T.pack "Turkmenistan", ('T', 'M'), ('T', 'K', 'M')) + , (796, T.pack "Turks and Caicos Islands", ('T', 'C'), ('T', 'C', 'A')) + , (798, T.pack "Tuvalu", ('T', 'V'), ('T', 'U', 'V')) + , (800, T.pack "Uganda", ('U', 'G'), ('U', 'G', 'A')) + , (804, T.pack "Ukraine", ('U', 'A'), ('U', 'K', 'R')) + , (784, T.pack "United Arab Emirates", ('A', 'E'), ('A', 'R', 'E')) + , (826, T.pack "United Kingdom of Great Britain and Northern Ireland", ('G', 'B'), ('G', 'B', 'R')) + , (840, T.pack "United States of America", ('U', 'S'), ('U', 'S', 'A')) + , (581, T.pack "United States Minor Outlying Islands", ('U', 'M'), ('U', 'M', 'I')) + , (858, T.pack "Uruguay", ('U', 'Y'), ('U', 'R', 'Y')) + , (860, T.pack "Uzbekistan", ('U', 'Z'), ('U', 'Z', 'B')) + , (548, T.pack "Vanuatu", ('V', 'U'), ('V', 'U', 'T')) + , (862, T.pack "Venezuela (Bolivarian Republic of)", ('V', 'E'), ('V', 'E', 'N')) + , (704, T.pack "Viet Nam", ('V', 'N'), ('V', 'N', 'M')) + , (92, T.pack "Virgin Islands (British)", ('V', 'G'), ('V', 'G', 'B')) + , (850, T.pack "Virgin Islands (U.S.)", ('V', 'I'), ('V', 'I', 'R')) + , (876, T.pack "Wallis and Futuna", ('W', 'F'), ('W', 'L', 'F')) + , (732, T.pack "Western Sahara", ('E', 'H'), ('E', 'S', 'H')) + , (887, T.pack "Yemen", ('Y', 'E'), ('Y', 'E', 'M')) + , (894, T.pack "Zambia", ('Z', 'M'), ('Z', 'M', 'B')) + , (716, T.pack "Zimbabwe", ('Z', 'W'), ('Z', 'W', 'E')) + , (383, T.pack "Kosovo", ('X', 'K'), ('X', 'K', 'X')) ] {-# NOINLINE countryNameQuads #-} diff --git a/country/src/Country/Unexposed/Names.hs b/country/src/Country/Unexposed/Names.hs index dbcc13b..6668b99 100644 --- a/country/src/Country/Unexposed/Names.hs +++ b/country/src/Country/Unexposed/Names.hs @@ -1,12 +1,11 @@ -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE BangPatterns #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MagicHash #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE UnboxedTuples #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE TypeInType #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DeriveDataTypeable #-} - {-# OPTIONS_HADDOCK not-home #-} module Country.Unexposed.Names @@ -22,7 +21,7 @@ module Country.Unexposed.Names , decodeNumeric , encodeEnglish , encodeEnglishShort - , Country(..) + , Country (..) ) where import Control.Monad @@ -33,75 +32,69 @@ import Control.DeepSeq (NFData) import Country.Unexposed.Alias (aliases) import Country.Unexposed.Encode.English (countryNameQuads) import Country.Unexposed.Util (newZeroedByteArray) -import Data.Bytes.Types (Bytes(Bytes)) import Data.ByteString (ByteString) -import Data.Char (toLower,isAlpha,toUpper) +import Data.Char (isAlpha, toLower, toUpper) import Data.Data -import Data.Hashable (Hashable) import Data.HashMap.Strict (HashMap) -import Data.Primitive (Array,indexArray,newArray,unsafeFreezeArray,writeArray) -import Data.Primitive (sizeOf) -import Data.Primitive (writeByteArray,indexByteArray,unsafeFreezeByteArray) -import Data.Primitive.ByteArray (ByteArray(..)) +import Data.Hashable (Hashable) +import Data.Primitive (Array, indexArray, indexByteArray, newArray, sizeOf, unsafeFreezeArray, unsafeFreezeByteArray, writeArray, writeByteArray) +import Data.Primitive.ByteArray (ByteArray (..)) import Data.Primitive.Types (Prim) import Data.Text (Text) -import Data.Text.Encoding (encodeUtf8,encodeUtf16BE) +import Data.Text.Encoding (encodeUtf16BE, encodeUtf8) import Data.Text.Short (ShortText) import Foreign.Storable (Storable) import GHC.Generics (Generic) import qualified Data.Aeson as AE import qualified Data.Aeson.Types as AET +import qualified Data.ByteString as ByteString import qualified Data.Bytes as Bytes import qualified Data.Bytes.HashMap.Word as BytesHashMap -import qualified Data.ByteString as ByteString import qualified Data.HashMap.Strict as HM import qualified Data.List as L import qualified Data.Primitive.Unlifted.Array as PM import qualified Data.Scientific as SCI import qualified Data.Text as T -import qualified Data.Text.Array as Text -import qualified Data.Text.Internal as Text import qualified Data.Text.Short as TS import qualified GHC.Exts as Exts -- | The name of a country given in English encodeEnglish :: Country -> Text -{-# inline encodeEnglish #-} +{-# INLINE encodeEnglish #-} encodeEnglish (Country n) = indexArray englishCountryNamesText (word16ToInt n) -- | The name of a country given in English encodeEnglishShort :: Country -> ShortText -{-# inline encodeEnglishShort #-} +{-# INLINE encodeEnglishShort #-} encodeEnglishShort (Country n) = PM.indexUnliftedArray englishCountryNamesShortText (word16ToInt n) englishCountryNamesShortText :: PM.UnliftedArray ShortText englishCountryNamesShortText = runST $ do m <- PM.newUnliftedArray numberOfPossibleCodes unnamedShort - mapM_ (\(ix,name,_,_) -> PM.writeUnliftedArray m (word16ToInt ix) (TS.fromText name)) countryNameQuads + mapM_ (\(ix, name, _, _) -> PM.writeUnliftedArray m (word16ToInt ix) (TS.fromText name)) countryNameQuads PM.unsafeFreezeUnliftedArray m {-# NOINLINE englishCountryNamesShortText #-} englishCountryNamesText :: Array Text englishCountryNamesText = runST $ do m <- newArray numberOfPossibleCodes unnamed - mapM_ (\(ix,name,_,_) -> writeArray m (word16ToInt ix) name) countryNameQuads + mapM_ (\(ix, name, _, _) -> writeArray m (word16ToInt ix) name) countryNameQuads unsafeFreezeArray m {-# NOINLINE englishCountryNamesText #-} englishIdentifierNamesText :: Array Text englishIdentifierNamesText = runST $ do m <- newArray numberOfPossibleCodes unnamed - mapM_ (\(ix,name,_,_) -> writeArray m (word16ToInt ix) (toIdentifier name)) countryNameQuads + mapM_ (\(ix, name, _, _) -> writeArray m (word16ToInt ix) (toIdentifier name)) countryNameQuads unsafeFreezeArray m {-# NOINLINE englishIdentifierNamesText #-} toIdentifier :: Text -> Text toIdentifier t = case (T.uncons . T.filter isAlpha . slowToTitle) t of Nothing -> T.empty - Just (b,bs) -> T.cons (toLower b) bs - + Just (b, bs) -> T.cons (toLower b) bs unnamed :: Text unnamed = T.pack "Invalid Country" @@ -119,38 +112,45 @@ word16ToInt = fromIntegral decodeMap :: HashMap Text Country {-# NOINLINE decodeMap #-} -decodeMap = HM.fromList (map (\(a,b) -> (b,Country a)) countryPairs) +decodeMap = HM.fromList (map (\(a, b) -> (b, Country a)) countryPairs) hashMapUtf8 :: BytesHashMap.Map -hashMapUtf8 = BytesHashMap.fromTrustedList - ( map - (\(a,t) -> (Exts.fromList (ByteString.unpack (encodeUtf8 t)),fromIntegral a) - ) countryPairs - ) +hashMapUtf8 = + BytesHashMap.fromTrustedList + ( map + ( \(a, t) -> (Exts.fromList (ByteString.unpack (encodeUtf8 t)), fromIntegral a) + ) + countryPairs + ) -- It is a hack to pull from a source of randomness in here, but whatever. -- Maybe I can get rid of this if GHC ever supports casing on values of -- type ByteArray# along with good codegen for it. hashMapUtf16 :: BytesHashMap.Map -hashMapUtf16 = BytesHashMap.fromTrustedList - ( map - (\(a, str) -> - (Bytes.fromByteString $ encodeUtf16BE str, fromIntegral a) - ) countryPairs - ) -countryPairs :: [(Word16,Text)] +hashMapUtf16 = + BytesHashMap.fromTrustedList + ( map + ( \(a, str) -> + (Bytes.fromByteString $ encodeUtf16BE str, fromIntegral a) + ) + countryPairs + ) +countryPairs :: [(Word16, Text)] {-# NOINLINE countryPairs #-} countryPairs = - let x = aliases ++ concatMap - (\(num,name,(c2a,c2b),(c3a,c3b,c3c)) -> - [ (num,name) - , (num,T.pack [c2a,c2b]) - , (num,T.pack [c3a,c3b,c3c]) - , (num,T.pack [toLower c2a,toLower c2b]) - , (num,T.pack [toLower c3a,toLower c3b,toLower c3c]) - ] - ) countryNameQuads - in x ++ map (\(a,b) -> (a,slowToTitle b)) x + let x = + aliases + ++ concatMap + ( \(num, name, (c2a, c2b), (c3a, c3b, c3c)) -> + [ (num, name) + , (num, T.pack [c2a, c2b]) + , (num, T.pack [c3a, c3b, c3c]) + , (num, T.pack [toLower c2a, toLower c2b]) + , (num, T.pack [toLower c3a, toLower c3b, toLower c3c]) + ] + ) + countryNameQuads + in x ++ map (\(a, b) -> (a, slowToTitle b)) x -- This is only needed to support the reflex-platform fork of text. Fortunately, -- in all the places this is needed, it is only called to build CAFs. @@ -160,7 +160,7 @@ slowToTitle = T.intercalate (T.singleton ' ') . map upperFirst . T.splitOn (T.si upperFirst :: Text -> Text upperFirst t = case T.uncons t of Nothing -> T.empty - Just (c,cs) -> T.cons (toUpper c) cs + Just (c, cs) -> T.cons (toUpper c) cs decodeMapUtf8 :: HashMap ByteString Country decodeMapUtf8 = HM.foldlWithKey' (\hm k v -> HM.insert (encodeUtf8 k) v hm) HM.empty decodeMap @@ -168,27 +168,28 @@ decodeMapUtf8 = HM.foldlWithKey' (\hm k v -> HM.insert (encodeUtf8 k) v hm) HM.e -- | A country recognized by ISO 3166. newtype Country = Country Word16 - deriving (Eq,Ord,Prim,Hashable,Storable,NFData,Generic,Data,Typeable) + deriving (Eq, Ord, Prim, Hashable, Storable, NFData, Generic, Data, Typeable) instance Show Country where show (Country n) = T.unpack (indexArray englishIdentifierNamesText (word16ToInt n)) instance Enum Country where fromEnum (Country w) = indexByteArray countryCodeToSequentialMapping (fromIntegral w) - toEnum number = if number >= 0 && number < actualNumberOfCountries - then Country (indexByteArray sequentialToCountryCodeMapping number) - else error ("toEnum: cannot convert " ++ show number ++ " to Country") + toEnum number = + if number >= 0 && number < actualNumberOfCountries + then Country (indexByteArray sequentialToCountryCodeMapping number) + else error ("toEnum: cannot convert " ++ show number ++ " to Country") instance Bounded Country where minBound = Country (indexByteArray sequentialToCountryCodeMapping 0) maxBound = Country (indexByteArray sequentialToCountryCodeMapping (actualNumberOfCountries - 1)) orderedCountryCodes :: [Word16] -orderedCountryCodes = L.sort $ map (\(a,_,_,_) -> a) countryNameQuads +orderedCountryCodes = L.sort $ map (\(a, _, _, _) -> a) countryNameQuads countryCodeToSequentialMapping :: ByteArray countryCodeToSequentialMapping = runST $ do numbers <- newZeroedByteArray (numberOfPossibleCodes * sizeOf (undefined :: Int)) - forM_ (zip [0 :: Int,1..] orderedCountryCodes) $ \(number,code) -> do + forM_ (zip [0 :: Int, 1 ..] orderedCountryCodes) $ \(number, code) -> do writeByteArray numbers (word16ToInt code) number unsafeFreezeByteArray numbers {-# NOINLINE countryCodeToSequentialMapping #-} @@ -196,7 +197,7 @@ countryCodeToSequentialMapping = runST $ do sequentialToCountryCodeMapping :: ByteArray sequentialToCountryCodeMapping = runST $ do codes <- newZeroedByteArray (actualNumberOfCountries * sizeOf (undefined :: Word16)) - forM_ (zip [0 :: Int,1..] orderedCountryCodes) $ \(number,code) -> do + forM_ (zip [0 :: Int, 1 ..] orderedCountryCodes) $ \(number, code) -> do writeByteArray codes number (code :: Word16) unsafeFreezeByteArray codes {-# NOINLINE sequentialToCountryCodeMapping #-} @@ -205,7 +206,6 @@ actualNumberOfCountries :: Int actualNumberOfCountries = length countryNameQuads {-# NOINLINE actualNumberOfCountries #-} - -- todo: add support for encoding directly to bytestring. -- Also, add suport for ToJSONKey and FromJSONKey once everything -- finally gets off of aeson-0.11 (looking at you, reflex-platform) @@ -222,42 +222,49 @@ instance AE.FromJSON Country where Just w -> case decodeNumeric w of Just c -> return c Nothing -> fail errMsg - where errMsg = fail $ "invalid country code " ++ show n + where + errMsg = fail $ "invalid country code " ++ show n _ -> AET.typeMismatch "Country" x --- | Get a country from a numeric code. Any code greater than --- 999 will not have a country associated with it. Additionally, --- many codes are unassigned. +{- | Get a country from a numeric code. Any code greater than + 999 will not have a country associated with it. Additionally, + many codes are unassigned. +-} decodeNumeric :: Word16 -> Maybe Country -decodeNumeric n = if n < 1000 && indexByteArray numericValidities (word16ToInt n) == (1 :: Word8) - then Just (Country n) - else Nothing +decodeNumeric n = + if n < 1000 && indexByteArray numericValidities (word16ToInt n) == (1 :: Word8) + then Just (Country n) + else Nothing -- | The elements in this array are Word8 (basically boolean) numericValidities :: ByteArray numericValidities = runST $ do m <- newZeroedByteArray numberOfPossibleCodes - forM_ countryNameQuads $ \(n,_,_,_) -> do + forM_ countryNameQuads $ \(n, _, _, _) -> do writeByteArray m (word16ToInt n) (1 :: Word8) unsafeFreezeByteArray m {-# NOINLINE numericValidities #-} alphaTwoHashMap :: HashMap Text Country -alphaTwoHashMap = L.foldl' - (\hm (countryNum,_,(c1,c2),_) -> - HM.insert (T.pack [c1,c2]) (Country countryNum) - $ HM.insert (T.pack [toLower c1, toLower c2]) (Country countryNum) - $ hm - ) - HM.empty countryNameQuads +alphaTwoHashMap = + L.foldl' + ( \hm (countryNum, _, (c1, c2), _) -> + HM.insert (T.pack [c1, c2]) (Country countryNum) $ + HM.insert (T.pack [toLower c1, toLower c2]) (Country countryNum) $ + hm + ) + HM.empty + countryNameQuads {-# NOINLINE alphaTwoHashMap #-} alphaThreeHashMap :: HashMap Text Country -alphaThreeHashMap = L.foldl' - (\hm (countryNum,_,_,(c1,c2,c3)) -> - HM.insert (T.pack [c1,c2,c3]) (Country countryNum) - $ HM.insert (T.pack [toLower c1, toLower c2, toLower c3]) (Country countryNum) - $ hm - ) - HM.empty countryNameQuads +alphaThreeHashMap = + L.foldl' + ( \hm (countryNum, _, _, (c1, c2, c3)) -> + HM.insert (T.pack [c1, c2, c3]) (Country countryNum) $ + HM.insert (T.pack [toLower c1, toLower c2, toLower c3]) (Country countryNum) $ + hm + ) + HM.empty + countryNameQuads {-# NOINLINE alphaThreeHashMap #-} diff --git a/country/src/Country/Unexposed/Trie.hs b/country/src/Country/Unexposed/Trie.hs index 5166e50..27986fe 100644 --- a/country/src/Country/Unexposed/Trie.hs +++ b/country/src/Country/Unexposed/Trie.hs @@ -4,22 +4,22 @@ module Country.Unexposed.Trie , trieParser ) where -import Data.HashMap.Strict (HashMap) -import Data.Word (Word16) -import Data.Text (Text) -import Data.Semigroup (Semigroup) import Control.Applicative ((<|>)) -import qualified Data.Text as T -import qualified Data.HashMap.Strict as HM import qualified Data.Attoparsec.Text as AT +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HM import qualified Data.Semigroup as SG +import Data.Text (Text) +import qualified Data.Text as T +import Data.Word (Word16) --- | If the value is not the max Word16 (65535), there --- is a match. This means that 65535 cannot be used, which --- is fine for this since 65535 is not used as a country code. +{- | If the value is not the max Word16 (65535), there + is a match. This means that 65535 cannot be used, which + is fine for this since 65535 is not used as a country code. +-} data Trie = Trie - { trieValue :: {-# UNPACK #-} !Word16 - , trieChildren :: !(HashMap Char Trie) + { _trieValue :: {-# UNPACK #-} !Word16 + , _trieChildren :: !(HashMap Char Trie) } empty :: Trie @@ -32,10 +32,11 @@ placeholder :: Word16 placeholder = 0xFFFF singleton :: Text -> Word16 -> Trie -singleton fullName code = go fullName where +singleton fullName code = go fullName + where go :: Text -> Trie go name = case T.uncons name of - Just (char,nameNext) -> Trie placeholder (HM.singleton char (go nameNext)) + Just (char, nameNext) -> Trie placeholder (HM.singleton char (go nameNext)) Nothing -> Trie code HM.empty instance Semigroup Trie where @@ -45,7 +46,7 @@ instance Monoid Trie where mempty = empty mappend = (SG.<>) -trieFromList :: [(Text,Word16)] -> Trie +trieFromList :: [(Text, Word16)] -> Trie trieFromList = foldMap (uncurry singleton) -- it seems like attoparsec should have some kind of convenience @@ -53,7 +54,8 @@ trieFromList = foldMap (uncurry singleton) -- input once your certain that it will be consumed, but I cannot -- find a way to use the api to do this. trieParser :: Trie -> AT.Parser Word16 -trieParser = go where +trieParser = go + where go :: Trie -> AT.Parser Word16 go (Trie value children) = do let keepGoing = do @@ -64,4 +66,3 @@ trieParser = go where if value == placeholder then keepGoing else keepGoing <|> return value - diff --git a/country/src/Country/Unexposed/TrieByte.hs b/country/src/Country/Unexposed/TrieByte.hs index e00125a..74bdd13 100644 --- a/country/src/Country/Unexposed/TrieByte.hs +++ b/country/src/Country/Unexposed/TrieByte.hs @@ -4,22 +4,22 @@ module Country.Unexposed.TrieByte , trieByteParser ) where -import Data.HashMap.Strict (HashMap) -import Data.Word (Word16,Word8) -import Data.ByteString (ByteString) -import Data.Semigroup (Semigroup) import Control.Applicative ((<|>)) +import qualified Data.Attoparsec.ByteString as AB +import Data.ByteString (ByteString) import qualified Data.ByteString as B +import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM -import qualified Data.Attoparsec.ByteString as AB import qualified Data.Semigroup as SG +import Data.Word (Word16, Word8) --- | If the value is not the max Word16 (65535), there --- is a match. This means that 65535 cannot be used, which --- is fine for this since 65535 is not used as a country code. +{- | If the value is not the max Word16 (65535), there + is a match. This means that 65535 cannot be used, which + is fine for this since 65535 is not used as a country code. +-} data TrieByte = TrieByte - { trieValue :: {-# UNPACK #-} !Word16 - , trieChildren :: !(HashMap Word8 TrieByte) + { _trieValue :: {-# UNPACK #-} !Word16 + , _trieChildren :: !(HashMap Word8 TrieByte) } empty :: TrieByte @@ -32,10 +32,11 @@ placeholder :: Word16 placeholder = 0xFFFF singleton :: ByteString -> Word16 -> TrieByte -singleton fullName code = go fullName where +singleton fullName code = go fullName + where go :: ByteString -> TrieByte go name = case B.uncons name of - Just (char,nameNext) -> TrieByte placeholder (HM.singleton char (go nameNext)) + Just (char, nameNext) -> TrieByte placeholder (HM.singleton char (go nameNext)) Nothing -> TrieByte code HM.empty instance Semigroup TrieByte where @@ -45,7 +46,7 @@ instance Monoid TrieByte where mempty = empty mappend = (SG.<>) -trieByteFromList :: [(ByteString,Word16)] -> TrieByte +trieByteFromList :: [(ByteString, Word16)] -> TrieByte trieByteFromList = foldMap (uncurry singleton) -- it seems like attoparsec should have some kind of convenience @@ -53,7 +54,8 @@ trieByteFromList = foldMap (uncurry singleton) -- input once your certain that it will be consumed, but I cannot -- find a way to use the api to do this. trieByteParser :: TrieByte -> AB.Parser Word16 -trieByteParser = go where +trieByteParser = go + where go :: TrieByte -> AB.Parser Word16 go (TrieByte value children) = do let keepGoing = do @@ -64,5 +66,3 @@ trieByteParser = go where if value == placeholder then keepGoing else keepGoing <|> return value - - diff --git a/country/src/Country/Unexposed/Util.hs b/country/src/Country/Unexposed/Util.hs index ff630e7..249c2ce 100644 --- a/country/src/Country/Unexposed/Util.hs +++ b/country/src/Country/Unexposed/Util.hs @@ -13,16 +13,16 @@ module Country.Unexposed.Util ) where import Control.Monad.Primitive (PrimMonad, PrimState) -import Data.Bits (unsafeShiftL,unsafeShiftR) -import Data.Char (chr,ord) -import Data.Primitive.ByteArray (MutableByteArray,newByteArray,fillByteArray) -import Data.Word (Word8,Word16) +import Data.Bits (unsafeShiftL, unsafeShiftR) +import Data.Char (chr, ord) +import Data.Primitive.ByteArray (MutableByteArray, fillByteArray, newByteArray) +import Data.Word (Word16, Word8) import GHC.Exts (sizeofByteArray#) -import GHC.Int (Int(I#)) +import GHC.Int (Int (I#)) import qualified Data.Text.Array as TA -newZeroedByteArray :: PrimMonad m => Int -> m (MutableByteArray (PrimState m)) +newZeroedByteArray :: (PrimMonad m) => Int -> m (MutableByteArray (PrimState m)) newZeroedByteArray len = do arr <- newByteArray len let arrStart = 0 @@ -36,11 +36,12 @@ mapTextArray f a@(TA.ByteArray inner) = TA.run $ do let len = I# (sizeofByteArray# inner) m <- TA.new len TA.copyI len m 0 a 0 - let go !ix = if ix < len - then do - TA.unsafeWrite m ix (charToWord8 (f (word8ToChar (TA.unsafeIndex a ix)))) - go (ix + 1) - else return () + let go !ix = + if ix < len + then do + TA.unsafeWrite m ix (charToWord8 (f (word8ToChar (TA.unsafeIndex a ix)))) + go (ix + 1) + else return () go 0 return m {-# INLINE mapTextArray #-} diff --git a/country/src/Country/Unsafe.hs b/country/src/Country/Unsafe.hs index 822ee6b..080d91a 100644 --- a/country/src/Country/Unsafe.hs +++ b/country/src/Country/Unsafe.hs @@ -2,7 +2,7 @@ {-# LANGUAGE UnboxedTuples #-} {-# OPTIONS_HADDOCK not-home #-} -{-| This module provides the data constructor for a 'Country'. +{- | This module provides the data constructor for a 'Country'. While pattern matching on a country is perfectly safe, constructing one is not. There is an invariant the type system does not capture that the country number, as defined @@ -11,8 +11,7 @@ in this library to segfault. -} module Country.Unsafe - ( Country(..) + ( Country (..) ) where -import Country.Unexposed.Names (Country(..)) - +import Country.Unexposed.Names (Country (..)) diff --git a/country/test/Spec.hs b/country/test/Spec.hs index 59f6cd8..f95689f 100644 --- a/country/test/Spec.hs +++ b/country/test/Spec.hs @@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -Wno-orphans #-} import Continent (Continent) import Country (Country) @@ -7,10 +8,10 @@ import Country.Subdivision (Subdivision) import Data.Char (ord) import Data.Maybe (fromJust) import Data.Primitive.Ptr (indexOffPtr) -import Data.Proxy (Proxy(..)) +import Data.Proxy (Proxy (..)) import Data.Word (Word8) -import Test.Tasty (defaultMain,testGroup,TestTree) -import Test.Tasty.QuickCheck (testProperty,(===)) +import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty.QuickCheck (testProperty, (===)) import qualified Continent import qualified Country @@ -21,81 +22,88 @@ import qualified Data.Text.Encoding as Text import qualified Data.Text.Short as TS import qualified Test.QuickCheck as QC import qualified Test.QuickCheck.Classes as QCC -import qualified Test.QuickCheck.Classes.IsList as QCCL import qualified Test.Tasty.QuickCheck as TQC main :: IO () -main = defaultMain $ testGroup "Country" $ - ( map lawsToTest - $ map ($ proxy) - $ [ QCC.boundedEnumLaws - , QCC.eqLaws - , QCC.ordLaws - , QCC.primLaws - , QCC.showLaws - , QCC.storableLaws - ] - ) ++ - ( map lawsToTest - $ map ($ Proxy @Continent) - $ [ -- QCC.boundedEnumLaws - QCC.eqLaws - , QCC.ordLaws - -- , QCC.primLaws - -- , QCC.showLaws - -- , QCC.storableLaws - ] - ) ++ - [ testProperty "encode-decode-english" - (\x -> Just x === Country.decode (Country.encodeEnglish x)) - , testProperty "encode-decode-alpha-2-upper" - (\x -> Just x === Country.decode (Country.alphaTwoUpper x)) - , testProperty "encode-decode-alpha-3-upper" - (\x -> Just x === Country.decode (Country.alphaThreeUpper x)) - , testProperty "encode-decode-alpha-2-lower" - (\x -> Just x === Country.decode (Country.alphaTwoLower x)) - , testProperty "encode-decode-alpha-3-lower" - (\x -> Just x === Country.decode (Country.alphaThreeLower x)) - , testProperty "encode-alpha-two-upper" - (\x -> - let t = Country.alphaTwoUpper x - ptr = Country.alphaTwoUpperUtf8Ptr x - in - (c2w (Text.index t 0), c2w (Text.index t 1)) - === - (indexOffPtr ptr 0 :: Word8, indexOffPtr ptr 1 :: Word8) +main = + defaultMain $ + testGroup "Country" $ + ( map lawsToTest $ + map ($ proxy) $ + [ QCC.boundedEnumLaws + , QCC.eqLaws + , QCC.ordLaws + , QCC.primLaws + , QCC.showLaws + , QCC.storableLaws + ] ) - , testProperty "encode-decode-numeric" - (\x -> Just x === Country.decodeNumeric (Country.encodeNumeric x)) - , testGroup "Continent" - [ testProperty "encode-decode-alpha-upper" $ \x -> - Just x === Continent.decodeAlpha (Continent.alphaUpper x) - , testProperty "encode-decode-english" $ \x -> - Just x === Continent.decodeEnglish (Continent.encodeEnglish x) - , testProperty "country-continent-smoke-usa" $ - Continent.continent (fromJust $ Country.decodeAlphaTwo "US") === Continent.NorthAmerica - , testProperty "country-continent-smoke-china" $ - Continent.continent (fromJust $ Country.decodeAlphaTwo "CN") === Continent.Asia - ] - , testGroup "Subdivision" $ - ( map lawsToTest - $ map ($ Proxy @Subdivision) - $ [ QCC.boundedEnumLaws - , QCC.eqLaws - , QCC.ordLaws - , QCC.primLaws - , QCC.showLaws - , QCC.storableLaws - ] - ) ++ - [ testProperty "encode-decode-alpha" $ \x -> - Just x === Subdivision.decodeAlpha (Subdivision.encodeAlpha x) - , testProperty "encode-short" $ \x -> - Just x === Subdivision.decodeAlpha (TS.toText (Subdivision.encodeAlphaShort x)) - , testProperty "decode-utf8-bytes" $ \x -> - Just x === Subdivision.decodeEnglishUtf8Bytes (Bytes.fromByteString (Text.encodeUtf8 (Subdivision.encodeEnglish x))) - ] - ] + ++ ( map lawsToTest $ + map ($ Proxy @Continent) $ + [ -- QCC.boundedEnumLaws + QCC.eqLaws + , QCC.ordLaws + -- , QCC.primLaws + -- , QCC.showLaws + -- , QCC.storableLaws + ] + ) + ++ [ testProperty + "encode-decode-english" + (\x -> Just x === Country.decode (Country.encodeEnglish x)) + , testProperty + "encode-decode-alpha-2-upper" + (\x -> Just x === Country.decode (Country.alphaTwoUpper x)) + , testProperty + "encode-decode-alpha-3-upper" + (\x -> Just x === Country.decode (Country.alphaThreeUpper x)) + , testProperty + "encode-decode-alpha-2-lower" + (\x -> Just x === Country.decode (Country.alphaTwoLower x)) + , testProperty + "encode-decode-alpha-3-lower" + (\x -> Just x === Country.decode (Country.alphaThreeLower x)) + , testProperty + "encode-alpha-two-upper" + ( \x -> + let t = Country.alphaTwoUpper x + ptr = Country.alphaTwoUpperUtf8Ptr x + in (c2w (Text.index t 0), c2w (Text.index t 1)) + === (indexOffPtr ptr 0 :: Word8, indexOffPtr ptr 1 :: Word8) + ) + , testProperty + "encode-decode-numeric" + (\x -> Just x === Country.decodeNumeric (Country.encodeNumeric x)) + , testGroup + "Continent" + [ testProperty "encode-decode-alpha-upper" $ \x -> + Just x === Continent.decodeAlpha (Continent.alphaUpper x) + , testProperty "encode-decode-english" $ \x -> + Just x === Continent.decodeEnglish (Continent.encodeEnglish x) + , testProperty "country-continent-smoke-usa" $ + Continent.continent (fromJust $ Country.decodeAlphaTwo "US") === Continent.NorthAmerica + , testProperty "country-continent-smoke-china" $ + Continent.continent (fromJust $ Country.decodeAlphaTwo "CN") === Continent.Asia + ] + , testGroup "Subdivision" $ + ( map lawsToTest $ + map ($ Proxy @Subdivision) $ + [ QCC.boundedEnumLaws + , QCC.eqLaws + , QCC.ordLaws + , QCC.primLaws + , QCC.showLaws + , QCC.storableLaws + ] + ) + ++ [ testProperty "encode-decode-alpha" $ \x -> + Just x === Subdivision.decodeAlpha (Subdivision.encodeAlpha x) + , testProperty "encode-short" $ \x -> + Just x === Subdivision.decodeAlpha (TS.toText (Subdivision.encodeAlphaShort x)) + , testProperty "decode-utf8-bytes" $ \x -> + Just x === Subdivision.decodeEnglishUtf8Bytes (Bytes.fromByteString (Text.encodeUtf8 (Subdivision.encodeEnglish x))) + ] + ] c2w :: Char -> Word8 c2w = fromIntegral . ord diff --git a/fourmolu.yaml b/fourmolu.yaml new file mode 100644 index 0000000..40cd005 --- /dev/null +++ b/fourmolu.yaml @@ -0,0 +1,51 @@ +# Number of spaces per indentation step +indentation: 2 + +# Max line length for automatic line breaking +column-limit: 200 + +# Styling of arrows in type signatures (choices: trailing, leading, or leading-args) +function-arrows: trailing + +# How to place commas in multi-line lists, records, etc. (choices: leading or trailing) +comma-style: leading + +# Styling of import/export lists (choices: leading, trailing, or diff-friendly) +import-export-style: leading + +# Whether to full-indent or half-indent 'where' bindings past the preceding body +indent-wheres: false + +# Whether to leave a space before an opening record brace +record-brace-space: true + +# Number of spaces between top-level declarations +newlines-between-decls: 1 + +# How to print Haddock comments (choices: single-line, multi-line, or multi-line-compact) +haddock-style: multi-line + +# How to print module docstring +haddock-style-module: null + +# Styling of let blocks (choices: auto, inline, newline, or mixed) +let-style: auto + +# How to align the 'in' keyword with respect to the 'let' keyword (choices: left-align, right-align, or no-space) +in-style: right-align + +# Whether to put parentheses around a single constraint (choices: auto, always, or never) +single-constraint-parens: always + +# Output Unicode syntax (choices: detect, always, or never) +unicode: never + +# Give the programmer more choice on where to insert blank lines +respectful: true + +# Fixity information for operators +fixities: [] + +# Module reexports Fourmolu should know about +reexports: [] + diff --git a/stack-8.6.yaml b/stack-8.6.yaml deleted file mode 100644 index c54d5e9..0000000 --- a/stack-8.6.yaml +++ /dev/null @@ -1,10 +0,0 @@ -resolver: lts-14.20 -packages: -- './country' -- './country-code-generation' -extra-deps: -- disjoint-containers-0.2.3 -- siphon-0.8.1 -- base-compat-0.9.3 -flags: {} -extra-package-dbs: [] diff --git a/stack.yaml.lock b/stack.yaml.lock deleted file mode 100644 index 4f2e481..0000000 --- a/stack.yaml.lock +++ /dev/null @@ -1,145 +0,0 @@ -# This file was autogenerated by Stack. -# You should not edit this file by hand. -# For more information, please see the documentation at: -# https://docs.haskellstack.org/en/stable/lock_files - -packages: -- completed: - hackage: base-compat-0.9.3@sha256:66691ca16b7ceb8c38bd2bac6ffe8e0eee25f408020cb8150a0444f8824e7458,4508 - pantry-tree: - size: 4363 - sha256: f84476f8e203dc456200c11d18ded408d61fdfdd52833c117106dd776432eb7a - original: - hackage: base-compat-0.9.3 -- completed: - hackage: bytebuild-0.3.7.0@sha256:dd3cd96f44670f6c38539d6a6b210afd19a1ff4883f92c96862c33de277f308e,2982 - pantry-tree: - size: 844 - sha256: 7445e3458b19692565380ea63ffdb4f24f7619e35cff14ed9d6b73707fee4cee - original: - hackage: bytebuild-0.3.7.0 -- completed: - hackage: byteslice-0.2.3.0@sha256:3ebcc77f8ac9fec3ca1a8304e66cfe0a1590c9272b768f2b19637e06de00bf6d,2014 - pantry-tree: - size: 1095 - sha256: 9ada4e1c418e8d9029edefdf664c64ff419ed1f02564e5a0dd28dd03e1e716a6 - original: - hackage: byteslice-0.2.3.0 -- completed: - hackage: disjoint-containers-0.2.4@sha256:8eb77b55d221f6b29f7f82a822839c7e13d63788dac92da7f32859ee0f91cf1e,1331 - pantry-tree: - size: 449 - sha256: f7d47588baf259d8e6465295c5f4a5c079149370cbfbe58dcceebf52fd019949 - original: - hackage: disjoint-containers-0.2.4 -- completed: - hackage: primitive-0.7.0.0@sha256:c45abc68bec080e3f1ab347dd331617d43fded94a473086bf21aeda69a6e20bc,3552 - pantry-tree: - size: 3221 - sha256: 2d85b8e01790534e666f519787454bab613c21d609f6707cb56c3301fafcc8cb - original: - hackage: primitive-0.7.0.0 -- completed: - hackage: primitive-addr-0.1.0.2@sha256:97eff091d1f89705635017b5459013bb19537ebb9fc630560116abf3e9e50f93,720 - pantry-tree: - size: 276 - sha256: 775df7896fd477c56671505db97aff519ac76bce9f04a6fc55965e189057bd79 - original: - hackage: primitive-addr-0.1.0.2 -- completed: - hackage: primitive-unaligned-0.1.1.0@sha256:a99337e10cb89ee16e9087d854cd90388aeffec3f8e6d7bbf348c137b87c63c0,1277 - pantry-tree: - size: 457 - sha256: 08c6daf62f33dd7de4019ec190aebc7ce0f2f2f064cc9bc7e4b0700af7f90935 - original: - hackage: primitive-unaligned-0.1.1.0 -- completed: - hackage: primitive-unlifted-0.1.2.0@sha256:9c3df73af54ed19fb3f4874da19334863cc414b22e578e27b5f52beeac4a60dd,1360 - pantry-tree: - size: 420 - sha256: cc6ffb1b48aa3f514c107f5188d5d5beb4e61a4232a81be7025b54f9be66a837 - original: - hackage: primitive-unlifted-0.1.2.0 -- completed: - hackage: quickcheck-classes-0.6.4.0@sha256:140a7c9f8c2ce98fffeb4efcb919584ef4fc837b45c2990c5e23802a12448e26,5991 - pantry-tree: - size: 1320 - sha256: a7f40c731f41ed8e02e6c7c09dfe59002b7851a19f3d0d72b935d79bc5f014f3 - original: - hackage: quickcheck-classes-0.6.4.0 -- completed: - hackage: quickcheck-classes-base-0.6.0.0@sha256:a6f9d0c0acdd6ad79571261cf58c415983992f613982f30db26c7afee947312d,3680 - pantry-tree: - size: 2557 - sha256: 496cdf2e4a65a6a29db3e12041f315f31cc0fe51ffd927a3f3dc4683f8fa4c8f - original: - hackage: quickcheck-classes-base-0.6.0.0 -- completed: - hackage: run-st-0.1.1.0@sha256:a43245bb23984089016772481bf52bfe63eaff0c5040303f69c9b15e80872fdc,883 - pantry-tree: - size: 269 - sha256: 06d5d7ecf185a26c15e48cda6c30e8865dae715c528a31466701272fae36d822 - original: - hackage: run-st-0.1.1.0 -- completed: - hackage: siphon-0.8.1@sha256:6114a9b9732f22beaf5722537f6d42548e9e1d38b5a19ea00dd0c387cb03824f,1432 - pantry-tree: - size: 372 - sha256: 37bee2a2e8f949d9eaac179eb59e094e9f1d3c1cd831b42f8abcf44e315e6dec - original: - hackage: siphon-0.8.1 -- completed: - hackage: byte-order-0.1.2.0@sha256:e0a5de48cfcf8d01535c43a1c1657c2589c1848b9b209288538d1f0f20474336,1207 - pantry-tree: - size: 560 - sha256: bbe6e9d48ce1d96f9dd342437f504c29feee7e2bb7522bda55b7402715233258 - original: - hackage: byte-order-0.1.2.0 -- completed: - hackage: small-bytearray-builder-0.3.2.0@sha256:1d1d364ecd6a9ef402ae588f4a6f80351840552cf659f8a92c459fb12d54ac3f,2991 - pantry-tree: - size: 873 - sha256: 6594410c783e7934bedefd8e80d37315f8d7ba4988a337d2069cc836ec7681ed - original: - hackage: small-bytearray-builder-0.3.2.0 -- completed: - hackage: natural-arithmetic-0.1.1.0@sha256:5c7cd0b0e6941b14ee1034fefafb29e6380d4c6a57b219983b8402f19ef98548,3411 - pantry-tree: - size: 714 - sha256: ab450e4956e128b253bab8ad86c734b55275615aa2ddd2899884c6408ada46ca - original: - hackage: natural-arithmetic-0.1.1.0 -- completed: - hackage: primitive-offset-0.2.0.0@sha256:f8006927d5c0a3e83707610bbc5514aabe8f84a907ecb07edd2c815f58299dea,843 - pantry-tree: - size: 368 - sha256: 6dbc2fbfd70920a1de5a76d3715506edc0895c81a2f7b856d3abb027865d4605 - original: - hackage: primitive-offset-0.2.0.0 -- completed: - hackage: bytehash-0.1.0.0@sha256:31d9018c896eff6170329774ec1c1b247712448871f2400a541e92e64286c610,2017 - pantry-tree: - size: 587 - sha256: 48315bcf32ca2188401b7c1c2a27a6ad07158f836fe01ab2cd7f70ae8d49bf62 - original: - hackage: bytehash-0.1.0.0 -- completed: - hackage: tuples-0.1.0.0@sha256:7006c1cab721ad3e39cdbf1ccb07ec050b94d654cc6e39277d46241eee6ac7c9,1088 - pantry-tree: - size: 320 - sha256: 57009cc671ed8e43738be3bf7b1392461ad086083df633a2f4f9c7206a14a79c - original: - hackage: tuples-0.1.0.0 -- completed: - hackage: wide-word-0.1.1.1@sha256:d3465109036cbbe3fc3266944f5a9defcb85b506afcbd2cd525baf8e0908d837,2998 - pantry-tree: - size: 801 - sha256: 62d8502c4698bdf60239f5f13637d0dcc4c946970bd9f7f6a54c47f8f8c605da - original: - hackage: wide-word-0.1.1.1 -snapshots: -- completed: - size: 524154 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/14/20.yaml - sha256: 2f5099f69ddb6abfe64400fe1e6a604e8e628f55e6837211cd70a81eb0a8fa4d - original: lts-14.20