module Data.Quickson
(
Quickson(..)
, parseQuickson
, quicksonExecute
, quickson
) where
import Control.Monad
import Control.Applicative
import Data.Aeson
import qualified Data.Aeson.Types as AT
import Data.Attoparsec.ByteString.Char8 as A8
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C8
import qualified Data.Text as T
data Quickson = Ob [(T.Text, Bool, Quickson)]
| Li Quickson
| Va deriving (Show)
parseQuickson :: BS.ByteString -> Either String Quickson
parseQuickson = parseOnly parseStructure
where
parseStructure = parseObject <|> parseArray
parseObject = Ob <$> ("{" *> sepBy1 parseLookups (char ',') <* "}")
parseArray = Li <$> ("[" *> parseStructure <* "]")
parseLookups = (,,) <$> (b2t <$> takeWhile1 (notInClass "?,:}"))
<*> ("?" *> pure True <|> pure False)
<*> (":" *> parseStructure <|> pure Va)
b2t = T.pack . C8.unpack
quicksonExecute :: FromJSON a => Quickson -> Value -> Either String a
quicksonExecute que = AT.parseEither (drill que >=> parseJSON)
where
drill :: Quickson -> Value -> AT.Parser Value
drill (Ob [l]) = parseJSON >=> flip look l
drill (Ob keys) = parseJSON >=> forM keys . look >=> return . toJSON
drill (Li q) = parseJSON >=> (\l -> mapM (drill q) (l::[Value]))
>=> return . toJSON
drill Va = return
look v (k,True,qq) = v.:?k >>= maybe (return Null) (drill qq)
look v (k,False,qq) = v.:k >>= drill qq
quickson :: FromJSON a => BS.ByteString -> BS.ByteString -> Either String a
quickson structureSpec bs = do
val <- eitherDecodeStrict bs
query <- parseQuickson structureSpec
quicksonExecute query val