module Data.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)
quicksonParse :: BS.ByteString -> Either String Quickson
quicksonParse = 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 >=> mapM (drill q) >=> 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 <- quicksonParse structureSpec
quicksonExecute query val