bson-lens-0.1.0: src/Data/Bson/Lens.hs
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
module Data.Bson.Lens
( _Float
, _String
, _Doc
, _Array
, _Bin
, _Fun
, _Uuid
, _Md5
, _UserDef
, _ObjId
, _Bool
, _UTC
, _Null
, _RegEx
, _JavaScr
, _Sym
, _Int32
, _Int64
, key
, values
, fields
, members
, nth
, labels
, AsValue(..)
, AsDocument(..)
)
where
import Data.Bson
import Control.Applicative
import Control.Lens
import Data.Text
-- import Data.Profunctor
$(makePrisms ''Value)
class AsDocument t where
-- |
-- A traversal for types that contain documents
--
-- >>> ["key" .= Bool True] ^? _Document
-- Just [ key: True]
--
-- >>> Doc ["key" .= Bool True] ^? _Document
-- Just [ key: True]
_Document :: Traversal' t Document
instance AsDocument Value where
_Document = _Doc
instance AsDocument Document where
_Document = id
instance AsDocument Javascript where
_Document = _JavascriptDoc
class AsValue t where
-- |
-- A traversal for types that contain 'Value's
--
-- >>> ("key" .= Bool True) ^? _Value
-- Just True
--
-- >>> Doc ["key" .= Bool True] ^? _Value
-- Just [ key: True]
_Value :: Traversal' t Value
instance AsValue Value where
_Value = id
instance AsValue Field where
_Value = lens (\(_ := v) -> v)
(\(k := _) v -> k := v)
_JavascriptDoc :: Lens' Javascript Document
_JavascriptDoc = lens (\(Javascript d _) -> d)
(\(Javascript _ v) d -> Javascript d v)
-- |
-- Traversal into an 'Val' over a 'Value'
--
-- >>> Array [String "hey"] ^? nth 0 . _Val :: Maybe Text
-- Just "hey"
--
-- >>> "[10.5]" ^? nth 0 . _Integer
-- Just 10
--
-- >>> "42" ^? _Integer
-- Just 42
_Val :: (AsValue t, Val v) => Traversal' t v
_Val = _Value.prism' val cast'
_IndexedDocument :: Iso' Document IndexedDocument
_IndexedDocument = iso IndexedDocument runIndexedDocument
_Symbol :: AsValue v => Traversal' v Text
_Symbol = _Value._Sym.iso (\(Symbol t) -> t) Symbol
-- |
-- A lens for a Label in a Field
--
-- >>> ("someKey" := Float 3.0) ^. _Label
-- "someKey"
--
-- >>> ("someKey" := Float 3.0) & _Label %~ Data.Text.toUpper
-- SOMEKEY: 3.0
_Label :: Lens' Field Label
_Label = lens (\(k := _) -> k)
(\(_ := v) k -> k := v)
-- |
-- Like 'ix', but for 'Document' values with Text indices
--
-- >>> ["someKey" := Float 3.0] ^? key "someKey"
-- Just 3.0
key :: AsDocument t => Label -> Traversal' t Value
key k = field k._Value
-- |
-- A traversal for a specific Document 'Field'.
-- This is similar to 'key' except it can target labels as well as values
--
-- >>> ["someKey" := Float 3.0] & field "someKey"._Label .~ "someOtherKey"
-- [ someOtherKey: 3.0 ]
field :: AsDocument t => Label -> Traversal' t Field
field k = _Document._IndexedDocument.ix k
-- | An indexed Traversal into Document fields
--
-- >>> Array [Doc ["doc1" := Float 3.0], Doc ["doc2" := Bool True]] ^.. values . fields
-- [ key: 3.0, key2: True ]
fields :: AsDocument v => IndexedTraversal' Int v Field
fields = _Document.traversed
-- | A Traversal into Document keys
--
-- >>> ["key" := Float 3.0, "key2" := Bool True] & labels %~ Data.Text.toUpper
-- ["KEY": 3.0, "KEY2": True]
labels :: AsDocument v => Traversal' v Label
labels = fields._Label
-- | A Traversal into Document values
--
-- >>> ["key" := Float 3.0, "key2" := Bool True] & members . _Float *~ 2
-- [ key: 6.0, key2: True ]
members :: AsDocument v => Traversal' v Value
members = fields._Value
-- | An indexed Traversal into Array elements
--
-- >>> Array [String "hey", Float 2.0] ^.. values
-- ["hey", 2.0]
--
-- >>> "Array [Float 2.0,Float 3.0,Float 4.0]" & values . _Float *~ 2.0
-- "[4.0,6.0,8.0]"
values :: AsValue v => IndexedTraversal' Int v Value
values = _Value._Array.traversed
-- | Like 'ix', but for Arrays with Int indexes
--
-- >>> "Array [Float 1.0, Float 2.0, Float 3.0]" ^? nth 1
-- Just 2.0
--
-- >>> "Array [Float 1.0, Float 2.0, Float 3.0]" & nth 1 .~ Float 20.0
-- "[1.0,20.0,3.0]"
nth :: AsValue v => Int -> Traversal' v Value
nth i = _Value._Array.ix i
newtype IndexedDocument = IndexedDocument { runIndexedDocument :: Document }
type instance IxValue IndexedDocument = Field
type instance Index IndexedDocument = Text
instance Ixed IndexedDocument where
ix i f xs0 = IndexedDocument <$> go (runIndexedDocument xs0) i
where
go [] _ = pure []
go (kv@(k' := v):as) k
| i == k' = f (k := v) <&> (:as)
| otherwise = (kv:) <$> go as i