{-# LANGUAGE CPP                 #-}
{-# LANGUAGE MultiWayIf          #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE RankNTypes #-}

module Data.GeoIP2 (
  -- * Library description
  -- |
  -- A haskell library for reading MaxMind's GeoIP version 2 files.
  -- It supports both IPv4 and IPv6 addresses. When a match is found, it
  -- is parsed and a simplified structure is returned. If you want to access
  -- other fields than those that are exposed, it is internally possible.
  --
  -- The database is mmapped upon opening, all querying can be later
  -- performed purely without IO monad.

  -- * Opening the database
    GeoDB
  , openGeoDB, openGeoDBBS
  , geoDbLanguages, geoDbType, geoDbDescription
  , geoDbAddrType, GeoIP(..)
  , DecodeException(..)
  -- * Querying the database
  , findGeoData
  , GeoResult(..)
  , Location(..)
  , AS(..)
  -- * Internals
  , GeoField, GeoFieldT(..)
  , rawGeoData
  -- * Lenses
  , _DataString, _DataDouble, _DataInt, _DataWord
  , _DataMap, _DataArray, _DataBool, _DataUnknown
  , key
  , geoNum
) where

#if !MIN_VERSION_base(4,8,0)
import           Control.Applicative    ((<$>), (<*>))
#endif

import           Control.Monad          (unless, when)
import qualified Data.ByteString        as BS
import           Data.Int
import           Data.IP                (IP (..), ipv4ToIPv6)
import           Data.Maybe             (mapMaybe)
import           Data.Serialize
import qualified Data.Text              as T
import           System.IO.MMap
import           Control.Lens           (ix, (^?), _Just, to, (^..), Traversal', Fold, prism')
import           Control.Exception      (throwIO, Exception)

import           Data.GeoIP2.Fields
import           Data.GeoIP2.SearchTree

-- | Address type stored in database
data GeoIP = GeoIPv6 | GeoIPv4 deriving (GeoIP -> GeoIP -> Bool
(GeoIP -> GeoIP -> Bool) -> (GeoIP -> GeoIP -> Bool) -> Eq GeoIP
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GeoIP -> GeoIP -> Bool
$c/= :: GeoIP -> GeoIP -> Bool
== :: GeoIP -> GeoIP -> Bool
$c== :: GeoIP -> GeoIP -> Bool
Eq, Int -> GeoIP -> ShowS
[GeoIP] -> ShowS
GeoIP -> String
(Int -> GeoIP -> ShowS)
-> (GeoIP -> String) -> ([GeoIP] -> ShowS) -> Show GeoIP
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GeoIP] -> ShowS
$cshowList :: [GeoIP] -> ShowS
show :: GeoIP -> String
$cshow :: GeoIP -> String
showsPrec :: Int -> GeoIP -> ShowS
$cshowsPrec :: Int -> GeoIP -> ShowS
Show)

data DecodeException = DecodeException String
  deriving (Int -> DecodeException -> ShowS
[DecodeException] -> ShowS
DecodeException -> String
(Int -> DecodeException -> ShowS)
-> (DecodeException -> String)
-> ([DecodeException] -> ShowS)
-> Show DecodeException
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DecodeException] -> ShowS
$cshowList :: [DecodeException] -> ShowS
show :: DecodeException -> String
$cshow :: DecodeException -> String
showsPrec :: Int -> DecodeException -> ShowS
$cshowsPrec :: Int -> DecodeException -> ShowS
Show)
instance Exception DecodeException

-- | Handle for search operations
data GeoDB = GeoDB {
   GeoDB -> ByteString
geoMem           :: BS.ByteString
 , GeoDB -> Text
geoDbType        :: T.Text         -- ^ String that indicates the structure of each data record associated with an IP address
 , GeoDB -> [Text]
geoDbLanguages   :: [T.Text]  -- ^ Languages supported in database
 , GeoDB -> Int64
geoDbNodeCount   :: Int64
 , GeoDB -> Int
geoDbRecordSize  :: Int
 , GeoDB -> GeoIP
geoDbAddrType    :: GeoIP -- ^ Type of address (IPv4/IPv6) stored in a database
 , GeoDB -> Maybe Text
geoDbDescription :: Maybe T.Text -- ^ Description of a database in english
}

getHeaderBytes :: BS.ByteString -> BS.ByteString
getHeaderBytes :: ByteString -> ByteString
getHeaderBytes = ByteString -> ByteString -> ByteString
lastsubstring ByteString
"\xab\xcd\xefMaxMind.com"
  where
    lastsubstring :: ByteString -> ByteString -> ByteString
lastsubstring ByteString
pattern ByteString
string =
        case ByteString -> ByteString -> (ByteString, ByteString)
BS.breakSubstring ByteString
pattern ByteString
string of
            (ByteString
res, ByteString
"") -> ByteString
res
            (ByteString
_, ByteString
rest) -> ByteString -> ByteString -> ByteString
lastsubstring ByteString
pattern (Int -> ByteString -> ByteString
BS.drop (ByteString -> Int
BS.length ByteString
pattern) ByteString
rest)

-- | Open database, mmap it into memory, parse header and return a handle for search operations
-- This function may throw DecodeException
openGeoDB :: FilePath -> IO GeoDB
openGeoDB :: String -> IO GeoDB
openGeoDB String
geoFile = do
    ByteString
bsmem <- String -> Maybe (Int64, Int) -> IO ByteString
mmapFileByteString String
geoFile Maybe (Int64, Int)
forall a. Maybe a
Nothing
    (String -> IO GeoDB)
-> (GeoDB -> IO GeoDB) -> Either String GeoDB -> IO GeoDB
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (DecodeException -> IO GeoDB
forall e a. Exception e => e -> IO a
throwIO (DecodeException -> IO GeoDB)
-> (String -> DecodeException) -> String -> IO GeoDB
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> DecodeException
DecodeException) GeoDB -> IO GeoDB
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Either String GeoDB
openGeoDBBS ByteString
bsmem)

-- | Open database from a bytestring
openGeoDBBS :: BS.ByteString -> Either String GeoDB
openGeoDBBS :: ByteString -> Either String GeoDB
openGeoDBBS ByteString
bsmem = do
    GeoField
hdr <- ByteString -> Either String GeoField
forall a. Serialize a => ByteString -> Either String a
decode (ByteString -> ByteString
getHeaderBytes ByteString
bsmem)
    Bool -> Either String () -> Either String ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (GeoField
hdr GeoField -> Getting (First Int) GeoField Int -> Maybe Int
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"binary_format_major_version" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Int) GeoField Int
forall b. Num b => Fold GeoField b
geoNum Maybe Int -> Maybe Int -> Bool
forall a. Eq a => a -> a -> Bool
/= (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
2 :: Maybe Int)) (Either String () -> Either String ())
-> Either String () -> Either String ()
forall a b. (a -> b) -> a -> b
$
      String -> Either String ()
forall a b. a -> Either a b
Left String
"Unsupported database version, only v2 supported."
    Bool -> Either String () -> Either String ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (GeoField
hdr GeoField -> Getting (First Int) GeoField Int -> Maybe Int
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"record_size" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Int) GeoField Int
forall b. Num b => Fold GeoField b
geoNum Maybe Int -> [Maybe Int] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> [Int] -> [Maybe Int]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Int
24, Int
28, Int
32 :: Int])) (Either String () -> Either String ())
-> Either String () -> Either String ()
forall a b. (a -> b) -> a -> b
$
      String -> Either String ()
forall a b. a -> Either a b
Left String
"Record size not supported."

    let res :: Maybe GeoDB
res = ByteString
-> Text -> [Text] -> Int64 -> Int -> GeoIP -> Maybe Text -> GeoDB
GeoDB ByteString
bsmem (Text -> [Text] -> Int64 -> Int -> GeoIP -> Maybe Text -> GeoDB)
-> Maybe Text
-> Maybe ([Text] -> Int64 -> Int -> GeoIP -> Maybe Text -> GeoDB)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GeoField
hdr GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"database_type" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
                          Maybe ([Text] -> Int64 -> Int -> GeoIP -> Maybe Text -> GeoDB)
-> Maybe [Text]
-> Maybe (Int64 -> Int -> GeoIP -> Maybe Text -> GeoDB)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [Text] -> Maybe [Text]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (GeoField
hdr GeoField -> Getting (Endo [Text]) GeoField Text -> [Text]
forall s a. s -> Getting (Endo [a]) s a -> [a]
^.. Text -> Traversal' GeoField GeoField
key Text
"languages" ((GeoField -> Const (Endo [Text]) GeoField)
 -> GeoField -> Const (Endo [Text]) GeoField)
-> Getting (Endo [Text]) GeoField Text
-> Getting (Endo [Text]) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([GeoField] -> Const (Endo [Text]) [GeoField])
-> GeoField -> Const (Endo [Text]) GeoField
forall a. Prism' (GeoFieldT a) [GeoFieldT a]
_DataArray (([GeoField] -> Const (Endo [Text]) [GeoField])
 -> GeoField -> Const (Endo [Text]) GeoField)
-> ((Text -> Const (Endo [Text]) Text)
    -> [GeoField] -> Const (Endo [Text]) [GeoField])
-> Getting (Endo [Text]) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GeoField -> Const (Endo [Text]) GeoField)
-> [GeoField] -> Const (Endo [Text]) [GeoField]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse ((GeoField -> Const (Endo [Text]) GeoField)
 -> [GeoField] -> Const (Endo [Text]) [GeoField])
-> Getting (Endo [Text]) GeoField Text
-> (Text -> Const (Endo [Text]) Text)
-> [GeoField]
-> Const (Endo [Text]) [GeoField]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (Endo [Text]) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString)
                          Maybe (Int64 -> Int -> GeoIP -> Maybe Text -> GeoDB)
-> Maybe Int64 -> Maybe (Int -> GeoIP -> Maybe Text -> GeoDB)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GeoField
hdr GeoField -> Getting (First Int64) GeoField Int64 -> Maybe Int64
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"node_count" ((GeoField -> Const (First Int64) GeoField)
 -> GeoField -> Const (First Int64) GeoField)
-> Getting (First Int64) GeoField Int64
-> Getting (First Int64) GeoField Int64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Int64) GeoField Int64
forall b. Num b => Fold GeoField b
geoNum
                          Maybe (Int -> GeoIP -> Maybe Text -> GeoDB)
-> Maybe Int -> Maybe (GeoIP -> Maybe Text -> GeoDB)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GeoField
hdr GeoField -> Getting (First Int) GeoField Int -> Maybe Int
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"record_size" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Int) GeoField Int
forall b. Num b => Fold GeoField b
geoNum
                          Maybe (GeoIP -> Maybe Text -> GeoDB)
-> Maybe GeoIP -> Maybe (Maybe Text -> GeoDB)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GeoField
hdr GeoField -> Getting (First GeoIP) GeoField GeoIP -> Maybe GeoIP
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"ip_version" ((GeoField -> Const (First GeoIP) GeoField)
 -> GeoField -> Const (First GeoIP) GeoField)
-> Getting (First GeoIP) GeoField GeoIP
-> Getting (First GeoIP) GeoField GeoIP
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First GeoIP) GeoField GeoIP
toVersion
                          Maybe (Maybe Text -> GeoDB) -> Maybe (Maybe Text) -> Maybe GeoDB
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe Text -> Maybe (Maybe Text)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (GeoField
hdr GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"description" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"en" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString)
    Either String GeoDB
-> (GeoDB -> Either String GeoDB)
-> Maybe GeoDB
-> Either String GeoDB
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> Either String GeoDB
forall a b. a -> Either a b
Left String
"Error decoding header") GeoDB -> Either String GeoDB
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe GeoDB
res
  where
    toVersion :: Getting (First GeoIP) GeoField GeoIP
toVersion = (Int -> Const (First GeoIP) Int)
-> GeoField -> Const (First GeoIP) GeoField
forall b. Num b => Fold GeoField b
geoNum ((Int -> Const (First GeoIP) Int)
 -> GeoField -> Const (First GeoIP) GeoField)
-> ((GeoIP -> Const (First GeoIP) GeoIP)
    -> Int -> Const (First GeoIP) Int)
-> Getting (First GeoIP) GeoField GeoIP
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GeoIP -> Int) -> (Int -> Maybe GeoIP) -> Prism Int Int GeoIP GeoIP
forall b s a. (b -> s) -> (s -> Maybe a) -> Prism s s a b
prism' GeoIP -> Int
pfrom Int -> Maybe GeoIP
forall a. (Eq a, Num a) => a -> Maybe GeoIP
pto
      where
        pfrom :: GeoIP -> Int
        pfrom :: GeoIP -> Int
pfrom GeoIP
GeoIPv4 = Int
4
        pfrom GeoIP
GeoIPv6 = Int
6
        pto :: a -> Maybe GeoIP
pto a
4 = GeoIP -> Maybe GeoIP
forall a. a -> Maybe a
Just GeoIP
GeoIPv4
        pto a
6 = GeoIP -> Maybe GeoIP
forall a. a -> Maybe a
Just GeoIP
GeoIPv6
        pto a
_ = Maybe GeoIP
forall a. Maybe a
Nothing

-- | Search GeoIP database and return complete unparsed data
rawGeoData :: GeoDB -> IP -> Either String GeoField
rawGeoData :: GeoDB -> IP -> Either String GeoField
rawGeoData GeoDB
geodb IP
addr = do
  [Bool]
bits <- Either String [Bool]
coerceAddr
  Int64
offset <- (ByteString, Int64, Int) -> [Bool] -> Either String Int64
getDataOffset (GeoDB -> ByteString
geoMem GeoDB
geodb, GeoDB -> Int64
geoDbNodeCount GeoDB
geodb, GeoDB -> Int
geoDbRecordSize GeoDB
geodb) [Bool]
bits
  Int64 -> Either String GeoField
strictDataAt Int64
offset
  where
    dataSectionStart :: Int
dataSectionStart = (GeoDB -> Int
geoDbRecordSize GeoDB
geodb Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
4) Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (GeoDB -> Int64
geoDbNodeCount GeoDB
geodb) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
16
    dataSection :: ByteString
dataSection = Int -> ByteString -> ByteString
BS.drop Int
dataSectionStart (GeoDB -> ByteString
geoMem GeoDB
geodb)

    strictDataAt :: Int64 -> Either String GeoField
    strictDataAt :: Int64 -> Either String GeoField
strictDataAt Int64
offset = do
      GeoFieldRaw
raw <- ByteString -> Either String GeoFieldRaw
forall a. Serialize a => ByteString -> Either String a
decode (Int -> ByteString -> ByteString
BS.drop (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
offset) ByteString
dataSection)
      (Int64 -> Either String GeoField)
-> GeoFieldRaw -> Either String GeoField
forall a (m :: * -> *).
(Ord a, Applicative m) =>
(Int64 -> m (GeoFieldT a)) -> GeoFieldRaw -> m (GeoFieldT a)
traversePtr (Int64 -> Either String GeoField
strictDataAt (Int64 -> Either String GeoField)
-> (Int64 -> Int64) -> Int64 -> Either String GeoField
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral) GeoFieldRaw
raw

    coerceAddr :: Either String [Bool]
coerceAddr
      | (IPv4 IPv4
_) <- IP
addr, GeoIP
GeoIPv4 <- GeoDB -> GeoIP
geoDbAddrType GeoDB
geodb = [Bool] -> Either String [Bool]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Bool] -> Either String [Bool]) -> [Bool] -> Either String [Bool]
forall a b. (a -> b) -> a -> b
$ IP -> [Bool]
ipToBits IP
addr
      | (IPv6 IPv6
_) <- IP
addr, GeoIP
GeoIPv6 <- GeoDB -> GeoIP
geoDbAddrType GeoDB
geodb = [Bool] -> Either String [Bool]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Bool] -> Either String [Bool]) -> [Bool] -> Either String [Bool]
forall a b. (a -> b) -> a -> b
$ IP -> [Bool]
ipToBits IP
addr
      | (IPv4 IPv4
addrv4) <- IP
addr, GeoIP
GeoIPv6 <- GeoDB -> GeoIP
geoDbAddrType GeoDB
geodb = [Bool] -> Either String [Bool]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Bool] -> Either String [Bool]) -> [Bool] -> Either String [Bool]
forall a b. (a -> b) -> a -> b
$ IP -> [Bool]
ipToBits (IP -> [Bool]) -> IP -> [Bool]
forall a b. (a -> b) -> a -> b
$ IPv6 -> IP
IPv6 (IPv4 -> IPv6
ipv4ToIPv6 IPv4
addrv4)
      | Bool
otherwise = String -> Either String [Bool]
forall a b. a -> Either a b
Left String
"Cannot search IPv6 address in IPv4 database"


data AS = AS {
    AS -> Int
asNumber       :: Int
  , AS -> Text
asOrganization :: T.Text
} deriving (Int -> AS -> ShowS
[AS] -> ShowS
AS -> String
(Int -> AS -> ShowS)
-> (AS -> String) -> ([AS] -> ShowS) -> Show AS
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [AS] -> ShowS
$cshowList :: [AS] -> ShowS
show :: AS -> String
$cshow :: AS -> String
showsPrec :: Int -> AS -> ShowS
$cshowsPrec :: Int -> AS -> ShowS
Show, AS -> AS -> Bool
(AS -> AS -> Bool) -> (AS -> AS -> Bool) -> Eq AS
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: AS -> AS -> Bool
$c/= :: AS -> AS -> Bool
== :: AS -> AS -> Bool
$c== :: AS -> AS -> Bool
Eq)

-- | Result of a search query
data GeoResult = GeoResult {
    GeoResult -> Maybe Text
geoContinent      :: Maybe T.Text
  , GeoResult -> Maybe Text
geoContinentCode  :: Maybe T.Text
  , GeoResult -> Maybe Text
geoCountryISO     :: Maybe T.Text
  , GeoResult -> Maybe Text
geoCountry        :: Maybe T.Text
  , GeoResult -> Maybe Location
geoLocation       :: Maybe Location
  , GeoResult -> Maybe Text
geoCity           :: Maybe T.Text
  , GeoResult -> Maybe Int
geoCityConfidence :: Maybe Int
  , GeoResult -> Maybe Text
geoPostalCode     :: Maybe T.Text
  , GeoResult -> Maybe AS
geoAS             :: Maybe AS
  , GeoResult -> Maybe Text
geoISP            :: Maybe T.Text
  , GeoResult -> Maybe Text
geoDomain         :: Maybe T.Text
  , GeoResult -> Maybe Text
geoOrganization   :: Maybe T.Text
  , GeoResult -> Maybe Text
geoUserType       :: Maybe T.Text
  , GeoResult -> [(Text, Text)]
geoSubdivisions   :: [(T.Text, T.Text)]

} deriving (Int -> GeoResult -> ShowS
[GeoResult] -> ShowS
GeoResult -> String
(Int -> GeoResult -> ShowS)
-> (GeoResult -> String)
-> ([GeoResult] -> ShowS)
-> Show GeoResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GeoResult] -> ShowS
$cshowList :: [GeoResult] -> ShowS
show :: GeoResult -> String
$cshow :: GeoResult -> String
showsPrec :: Int -> GeoResult -> ShowS
$cshowsPrec :: Int -> GeoResult -> ShowS
Show, GeoResult -> GeoResult -> Bool
(GeoResult -> GeoResult -> Bool)
-> (GeoResult -> GeoResult -> Bool) -> Eq GeoResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GeoResult -> GeoResult -> Bool
$c/= :: GeoResult -> GeoResult -> Bool
== :: GeoResult -> GeoResult -> Bool
$c== :: GeoResult -> GeoResult -> Bool
Eq)

-- | Location of the IP address
data Location = Location {
    Location -> Double
locationLatitude :: Double
  , Location -> Double
locationLongitude :: Double
  , Location -> Text
locationTimezone :: T.Text
  , Location -> Maybe Int
locationAccuracy :: Maybe Int
} deriving (Int -> Location -> ShowS
[Location] -> ShowS
Location -> String
(Int -> Location -> ShowS)
-> (Location -> String) -> ([Location] -> ShowS) -> Show Location
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Location] -> ShowS
$cshowList :: [Location] -> ShowS
show :: Location -> String
$cshow :: Location -> String
showsPrec :: Int -> Location -> ShowS
$cshowsPrec :: Int -> Location -> ShowS
Show, Location -> Location -> Bool
(Location -> Location -> Bool)
-> (Location -> Location -> Bool) -> Eq Location
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Location -> Location -> Bool
$c/= :: Location -> Location -> Bool
== :: Location -> Location -> Bool
$c== :: Location -> Location -> Bool
Eq)

-- | Search GeoIP database
findGeoData ::
     GeoDB   -- ^ Db handle
  -> T.Text  -- ^ Language code (e.g. "en")
  -> IP      -- ^ IP address to search
  -> Either String GeoResult -- ^ Result, if something is found
findGeoData :: GeoDB -> Text -> IP -> Either String GeoResult
findGeoData GeoDB
geodb Text
lang IP
ip = do
  GeoField
res <- GeoDB -> IP -> Either String GeoField
rawGeoData GeoDB
geodb IP
ip
  let subdivmap :: [GeoField]
subdivmap = GeoField
res GeoField
-> Getting (Endo [GeoField]) GeoField GeoField -> [GeoField]
forall s a. s -> Getting (Endo [a]) s a -> [a]
^.. Text -> Traversal' GeoField GeoField
key Text
"subdivisions" Getting (Endo [GeoField]) GeoField GeoField
-> Getting (Endo [GeoField]) GeoField GeoField
-> Getting (Endo [GeoField]) GeoField GeoField
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([GeoField] -> Const (Endo [GeoField]) [GeoField])
-> GeoField -> Const (Endo [GeoField]) GeoField
forall a. Prism' (GeoFieldT a) [GeoFieldT a]
_DataArray (([GeoField] -> Const (Endo [GeoField]) [GeoField])
 -> GeoField -> Const (Endo [GeoField]) GeoField)
-> ((GeoField -> Const (Endo [GeoField]) GeoField)
    -> [GeoField] -> Const (Endo [GeoField]) [GeoField])
-> Getting (Endo [GeoField]) GeoField GeoField
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GeoField -> Const (Endo [GeoField]) GeoField)
-> [GeoField] -> Const (Endo [GeoField]) [GeoField]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse
      subdivs :: [(Text, Text)]
subdivs = (GeoField -> Maybe (Text, Text)) -> [GeoField] -> [(Text, Text)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (\GeoField
s -> (,) (Text -> Text -> (Text, Text))
-> Maybe Text -> Maybe (Text -> (Text, Text))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GeoField
s GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"iso_code"  ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
                                    Maybe (Text -> (Text, Text)) -> Maybe Text -> Maybe (Text, Text)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GeoField
s GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"names" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
lang ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString) [GeoField]
subdivmap

  GeoResult -> Either String GeoResult
forall (m :: * -> *) a. Monad m => a -> m a
return (GeoResult -> Either String GeoResult)
-> GeoResult -> Either String GeoResult
forall a b. (a -> b) -> a -> b
$ GeoResult :: Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Location
-> Maybe Text
-> Maybe Int
-> Maybe Text
-> Maybe AS
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> [(Text, Text)]
-> GeoResult
GeoResult {
      geoContinent :: Maybe Text
geoContinent = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"continent" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"names" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
lang ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoContinentCode :: Maybe Text
geoContinentCode = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"continent" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"code" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoCountryISO :: Maybe Text
geoCountryISO = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"country" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"iso_code" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoCountry :: Maybe Text
geoCountry = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"country" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"names" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
lang ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoLocation :: Maybe Location
geoLocation = Double -> Double -> Text -> Maybe Int -> Location
Location (Double -> Double -> Text -> Maybe Int -> Location)
-> Maybe Double -> Maybe (Double -> Text -> Maybe Int -> Location)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GeoField
res GeoField -> Getting (First Double) GeoField Double -> Maybe Double
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"location" ((GeoField -> Const (First Double) GeoField)
 -> GeoField -> Const (First Double) GeoField)
-> Getting (First Double) GeoField Double
-> Getting (First Double) GeoField Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"latitude" ((GeoField -> Const (First Double) GeoField)
 -> GeoField -> Const (First Double) GeoField)
-> Getting (First Double) GeoField Double
-> Getting (First Double) GeoField Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Double) GeoField Double
forall a. Prism' (GeoFieldT a) Double
_DataDouble
                            Maybe (Double -> Text -> Maybe Int -> Location)
-> Maybe Double -> Maybe (Text -> Maybe Int -> Location)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GeoField
res GeoField -> Getting (First Double) GeoField Double -> Maybe Double
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"location" ((GeoField -> Const (First Double) GeoField)
 -> GeoField -> Const (First Double) GeoField)
-> Getting (First Double) GeoField Double
-> Getting (First Double) GeoField Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"longitude" ((GeoField -> Const (First Double) GeoField)
 -> GeoField -> Const (First Double) GeoField)
-> Getting (First Double) GeoField Double
-> Getting (First Double) GeoField Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Double) GeoField Double
forall a. Prism' (GeoFieldT a) Double
_DataDouble
                            Maybe (Text -> Maybe Int -> Location)
-> Maybe Text -> Maybe (Maybe Int -> Location)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"location" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"time_zone" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
                            Maybe (Maybe Int -> Location)
-> Maybe (Maybe Int) -> Maybe Location
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe Int -> Maybe (Maybe Int)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (GeoField
res GeoField -> Getting (First Int) GeoField Int -> Maybe Int
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"location" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"accuracy_radius" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Int) GeoField Int
forall b. Num b => Fold GeoField b
geoNum)
    , geoCity :: Maybe Text
geoCity = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"city" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"names" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
lang ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoCityConfidence :: Maybe Int
geoCityConfidence = GeoField
res GeoField -> Getting (First Int) GeoField Int -> Maybe Int
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"city" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"confidence" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Int) GeoField Int
forall b. Num b => Fold GeoField b
geoNum
    , geoPostalCode :: Maybe Text
geoPostalCode = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"postal" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"code" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoAS :: Maybe AS
geoAS = Int -> Text -> AS
AS (Int -> Text -> AS) -> Maybe Int -> Maybe (Text -> AS)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GeoField
res GeoField -> Getting (First Int) GeoField Int -> Maybe Int
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"traits" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"autonomous_system_number" ((GeoField -> Const (First Int) GeoField)
 -> GeoField -> Const (First Int) GeoField)
-> Getting (First Int) GeoField Int
-> Getting (First Int) GeoField Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Int) GeoField Int
forall b. Num b => Fold GeoField b
geoNum
                 Maybe (Text -> AS) -> Maybe Text -> Maybe AS
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"traits" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"autonomous_system_organization" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoISP :: Maybe Text
geoISP = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"traits" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"isp" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoDomain :: Maybe Text
geoDomain = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"traits" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"domain" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoOrganization :: Maybe Text
geoOrganization = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"traits" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"organization" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoUserType :: Maybe Text
geoUserType = GeoField
res GeoField -> Getting (First Text) GeoField Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' GeoField GeoField
key Text
"traits" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Traversal' GeoField GeoField
key Text
"user_type" ((GeoField -> Const (First Text) GeoField)
 -> GeoField -> Const (First Text) GeoField)
-> Getting (First Text) GeoField Text
-> Getting (First Text) GeoField Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (First Text) GeoField Text
forall a. Prism' (GeoFieldT a) Text
_DataString
    , geoSubdivisions :: [(Text, Text)]
geoSubdivisions = [(Text, Text)]
subdivs
  }

-- | Helper lens to access key in a DataMap
key :: T.Text -> Traversal' GeoField GeoField
key :: Text -> Traversal' GeoField GeoField
key Text
k = (Map GeoField GeoField -> f (Map GeoField GeoField))
-> GeoField -> f GeoField
forall a. Prism' (GeoFieldT a) (Map (GeoFieldT a) (GeoFieldT a))
_DataMap ((Map GeoField GeoField -> f (Map GeoField GeoField))
 -> GeoField -> f GeoField)
-> ((GeoField -> f GeoField)
    -> Map GeoField GeoField -> f (Map GeoField GeoField))
-> (GeoField -> f GeoField)
-> GeoField
-> f GeoField
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index (Map GeoField GeoField)
-> Traversal'
     (Map GeoField GeoField) (IxValue (Map GeoField GeoField))
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix (Text -> GeoField
forall a. Text -> GeoFieldT a
DataString Text
k)

-- | Helper lens to convert integer Word/Int to whatever number type is needed
geoNum :: Num b => Fold GeoField b
geoNum :: Fold GeoField b
geoNum = (GeoField -> Maybe b) -> Optic' (->) f GeoField (Maybe b)
forall (p :: * -> * -> *) (f :: * -> *) s a.
(Profunctor p, Contravariant f) =>
(s -> a) -> Optic' p f s a
to GeoField -> Maybe b
forall a a. Num a => GeoFieldT a -> Maybe a
fromNum Optic' (->) f GeoField (Maybe b)
-> ((b -> f b) -> Maybe b -> f (Maybe b))
-> (b -> f b)
-> GeoField
-> f GeoField
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (b -> f b) -> Maybe b -> f (Maybe b)
forall a b. Prism (Maybe a) (Maybe b) a b
_Just
  where
    fromNum :: GeoFieldT a -> Maybe a
fromNum (DataInt Int64
x) = a -> Maybe a
forall a. a -> Maybe a
Just (Int64 -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
x)
    fromNum (DataWord Word64
x) = a -> Maybe a
forall a. a -> Maybe a
Just (Word64 -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
x)
    fromNum GeoFieldT a
_ = Maybe a
forall a. Maybe a
Nothing