{-# LANGUAGE CPP   #-}
{-# LANGUAGE RankNTypes   #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveGeneric #-}

-- | lenses to access sheets, cells and values of 'Xlsx'
module Codec.Xlsx.Lens
  ( ixSheet
  , atSheet
  , ixCell
  , ixCellRC
  , ixCellXY
  , atCell
  , atCellRC
  , atCellXY
  , cellValueAt
  , cellValueAtRC
  , cellValueAtXY
  ) where

import Codec.Xlsx.Types
#ifdef USE_MICROLENS
import Lens.Micro
import Lens.Micro.Internal
import Lens.Micro.GHC ()
#else
import Control.Lens
#endif
import Data.Function (on)
import Data.List (deleteBy)
import Data.Text
import Data.Tuple (swap)
import GHC.Generics (Generic)

newtype SheetList = SheetList{ SheetList -> [(Text, Worksheet)]
unSheetList :: [(Text, Worksheet)] }
    deriving (SheetList -> SheetList -> Bool
(SheetList -> SheetList -> Bool)
-> (SheetList -> SheetList -> Bool) -> Eq SheetList
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SheetList -> SheetList -> Bool
$c/= :: SheetList -> SheetList -> Bool
== :: SheetList -> SheetList -> Bool
$c== :: SheetList -> SheetList -> Bool
Eq, Int -> SheetList -> ShowS
[SheetList] -> ShowS
SheetList -> String
(Int -> SheetList -> ShowS)
-> (SheetList -> String)
-> ([SheetList] -> ShowS)
-> Show SheetList
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SheetList] -> ShowS
$cshowList :: [SheetList] -> ShowS
show :: SheetList -> String
$cshow :: SheetList -> String
showsPrec :: Int -> SheetList -> ShowS
$cshowsPrec :: Int -> SheetList -> ShowS
Show, (forall x. SheetList -> Rep SheetList x)
-> (forall x. Rep SheetList x -> SheetList) -> Generic SheetList
forall x. Rep SheetList x -> SheetList
forall x. SheetList -> Rep SheetList x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep SheetList x -> SheetList
$cfrom :: forall x. SheetList -> Rep SheetList x
Generic)

type instance IxValue (SheetList) = Worksheet
type instance Index (SheetList) = Text

instance Ixed SheetList where
    ix :: Index SheetList -> Traversal' SheetList (IxValue SheetList)
ix Index SheetList
k IxValue SheetList -> f (IxValue SheetList)
f sl :: SheetList
sl@(SheetList [(Text, Worksheet)]
l) = case Text -> [(Text, Worksheet)] -> Maybe Worksheet
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
Index SheetList
k [(Text, Worksheet)]
l of
        Just Worksheet
v  -> IxValue SheetList -> f (IxValue SheetList)
f IxValue SheetList
Worksheet
v f Worksheet -> (Worksheet -> SheetList) -> f SheetList
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \Worksheet
v' -> [(Text, Worksheet)] -> SheetList
SheetList (Text -> Worksheet -> [(Text, Worksheet)] -> [(Text, Worksheet)]
forall k v. Eq k => k -> v -> [(k, v)] -> [(k, v)]
upsert Text
Index SheetList
k Worksheet
v' [(Text, Worksheet)]
l)
        Maybe Worksheet
Nothing -> SheetList -> f SheetList
forall (f :: * -> *) a. Applicative f => a -> f a
pure SheetList
sl
    {-# INLINE ix #-}

instance At SheetList where
  at :: Index SheetList -> Lens' SheetList (Maybe (IxValue SheetList))
at Index SheetList
k Maybe (IxValue SheetList) -> f (Maybe (IxValue SheetList))
f (SheetList [(Text, Worksheet)]
l) = Maybe (IxValue SheetList) -> f (Maybe (IxValue SheetList))
f Maybe (IxValue SheetList)
Maybe Worksheet
mv f (Maybe Worksheet)
-> (Maybe Worksheet -> SheetList) -> f SheetList
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \Maybe Worksheet
r -> case Maybe Worksheet
r of
      Maybe Worksheet
Nothing -> [(Text, Worksheet)] -> SheetList
SheetList ([(Text, Worksheet)] -> SheetList)
-> [(Text, Worksheet)] -> SheetList
forall a b. (a -> b) -> a -> b
$ [(Text, Worksheet)]
-> (Worksheet -> [(Text, Worksheet)])
-> Maybe Worksheet
-> [(Text, Worksheet)]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [(Text, Worksheet)]
l (\Worksheet
v -> ((Text, Worksheet) -> (Text, Worksheet) -> Bool)
-> (Text, Worksheet) -> [(Text, Worksheet)] -> [(Text, Worksheet)]
forall a. (a -> a -> Bool) -> a -> [a] -> [a]
deleteBy (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
(==) (Text -> Text -> Bool)
-> ((Text, Worksheet) -> Text)
-> (Text, Worksheet)
-> (Text, Worksheet)
-> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Text, Worksheet) -> Text
forall a b. (a, b) -> a
fst) (Text
Index SheetList
k,Worksheet
v) [(Text, Worksheet)]
l) Maybe Worksheet
mv
      Just Worksheet
v' -> [(Text, Worksheet)] -> SheetList
SheetList ([(Text, Worksheet)] -> SheetList)
-> [(Text, Worksheet)] -> SheetList
forall a b. (a -> b) -> a -> b
$ Text -> Worksheet -> [(Text, Worksheet)] -> [(Text, Worksheet)]
forall k v. Eq k => k -> v -> [(k, v)] -> [(k, v)]
upsert Text
Index SheetList
k Worksheet
v' [(Text, Worksheet)]
l
    where
      mv :: Maybe Worksheet
mv = Text -> [(Text, Worksheet)] -> Maybe Worksheet
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
Index SheetList
k [(Text, Worksheet)]
l
  {-# INLINE at #-}

upsert :: (Eq k) => k -> v -> [(k,v)] -> [(k,v)]
upsert :: k -> v -> [(k, v)] -> [(k, v)]
upsert k
k v
v [] = [(k
k,v
v)]
upsert k
k v
v ((k
k1,v
v1):[(k, v)]
r) =
    if k
k k -> k -> Bool
forall a. Eq a => a -> a -> Bool
== k
k1
    then (k
k,v
v)(k, v) -> [(k, v)] -> [(k, v)]
forall a. a -> [a] -> [a]
:[(k, v)]
r
    else (k
k1,v
v1)(k, v) -> [(k, v)] -> [(k, v)]
forall a. a -> [a] -> [a]
:k -> v -> [(k, v)] -> [(k, v)]
forall k v. Eq k => k -> v -> [(k, v)] -> [(k, v)]
upsert k
k v
v [(k, v)]
r

-- | lens giving access to a worksheet from 'Xlsx' object
-- by its name
ixSheet :: Text -> Traversal' Xlsx Worksheet
ixSheet :: Text -> Traversal' Xlsx Worksheet
ixSheet Text
s = ([(Text, Worksheet)] -> f [(Text, Worksheet)]) -> Xlsx -> f Xlsx
Lens' Xlsx [(Text, Worksheet)]
xlSheets (([(Text, Worksheet)] -> f [(Text, Worksheet)]) -> Xlsx -> f Xlsx)
-> ((Worksheet -> f Worksheet)
    -> [(Text, Worksheet)] -> f [(Text, Worksheet)])
-> (Worksheet -> f Worksheet)
-> Xlsx
-> f Xlsx
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \Worksheet -> f Worksheet
f -> (SheetList -> [(Text, Worksheet)])
-> f SheetList -> f [(Text, Worksheet)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap SheetList -> [(Text, Worksheet)]
unSheetList (f SheetList -> f [(Text, Worksheet)])
-> ([(Text, Worksheet)] -> f SheetList)
-> [(Text, Worksheet)]
-> f [(Text, Worksheet)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index SheetList
-> (IxValue SheetList -> f (IxValue SheetList))
-> SheetList
-> f SheetList
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Text
Index SheetList
s IxValue SheetList -> f (IxValue SheetList)
Worksheet -> f Worksheet
f (SheetList -> f SheetList)
-> ([(Text, Worksheet)] -> SheetList)
-> [(Text, Worksheet)]
-> f SheetList
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Text, Worksheet)] -> SheetList
SheetList

-- | 'Control.Lens.At' variant of 'ixSheet' lens
--
-- /Note:/ if there is no such sheet in this workbook then new sheet will be
-- added as the last one to the sheet list
atSheet :: Text -> Lens' Xlsx (Maybe Worksheet)
atSheet :: Text -> Lens' Xlsx (Maybe Worksheet)
atSheet Text
s = ([(Text, Worksheet)] -> f [(Text, Worksheet)]) -> Xlsx -> f Xlsx
Lens' Xlsx [(Text, Worksheet)]
xlSheets (([(Text, Worksheet)] -> f [(Text, Worksheet)]) -> Xlsx -> f Xlsx)
-> ((Maybe Worksheet -> f (Maybe Worksheet))
    -> [(Text, Worksheet)] -> f [(Text, Worksheet)])
-> (Maybe Worksheet -> f (Maybe Worksheet))
-> Xlsx
-> f Xlsx
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \Maybe Worksheet -> f (Maybe Worksheet)
f -> (SheetList -> [(Text, Worksheet)])
-> f SheetList -> f [(Text, Worksheet)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap SheetList -> [(Text, Worksheet)]
unSheetList (f SheetList -> f [(Text, Worksheet)])
-> ([(Text, Worksheet)] -> f SheetList)
-> [(Text, Worksheet)]
-> f [(Text, Worksheet)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index SheetList
-> (Maybe (IxValue SheetList) -> f (Maybe (IxValue SheetList)))
-> SheetList
-> f SheetList
forall m. At m => Index m -> Lens' m (Maybe (IxValue m))
at Text
Index SheetList
s Maybe (IxValue SheetList) -> f (Maybe (IxValue SheetList))
Maybe Worksheet -> f (Maybe Worksheet)
f (SheetList -> f SheetList)
-> ([(Text, Worksheet)] -> SheetList)
-> [(Text, Worksheet)]
-> f SheetList
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Text, Worksheet)] -> SheetList
SheetList

-- | lens giving access to a cell in some worksheet
-- by its position, by default row+column index is used
-- so this lens is a synonym of 'ixCellRC'
ixCell :: (Int, Int) -> Traversal' Worksheet Cell
ixCell :: (Int, Int) -> Traversal' Worksheet Cell
ixCell = (Int, Int) -> (Cell -> f Cell) -> Worksheet -> f Worksheet
(Int, Int) -> Traversal' Worksheet Cell
ixCellRC

-- | lens to access cell in a worksheet
ixCellRC :: (Int, Int) -> Traversal' Worksheet Cell
ixCellRC :: (Int, Int) -> Traversal' Worksheet Cell
ixCellRC (Int, Int)
i = (CellMap -> f CellMap) -> Worksheet -> f Worksheet
Lens' Worksheet CellMap
wsCells ((CellMap -> f CellMap) -> Worksheet -> f Worksheet)
-> ((Cell -> f Cell) -> CellMap -> f CellMap)
-> (Cell -> f Cell)
-> Worksheet
-> f Worksheet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index CellMap -> Traversal' CellMap (IxValue CellMap)
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix (Int, Int)
Index CellMap
i

-- | lens to access cell in a worksheet using more traditional
-- x+y coordinates
ixCellXY :: (Int, Int) -> Traversal' Worksheet Cell
ixCellXY :: (Int, Int) -> Traversal' Worksheet Cell
ixCellXY (Int, Int)
i = (Int, Int) -> Traversal' Worksheet Cell
ixCellRC ((Int, Int) -> Traversal' Worksheet Cell)
-> (Int, Int) -> Traversal' Worksheet Cell
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> (Int, Int)
forall a b. (a, b) -> (b, a)
swap (Int, Int)
i

-- | accessor that can read, write or delete cell in a worksheet
-- synonym of 'atCellRC' so uses row+column index
atCell :: (Int, Int) -> Lens' Worksheet (Maybe Cell)
atCell :: (Int, Int) -> Lens' Worksheet (Maybe Cell)
atCell = (Int, Int)
-> (Maybe Cell -> f (Maybe Cell)) -> Worksheet -> f Worksheet
(Int, Int) -> Lens' Worksheet (Maybe Cell)
atCellRC

-- | lens to read, write or delete cell in a worksheet
atCellRC :: (Int, Int) -> Lens' Worksheet (Maybe Cell)
atCellRC :: (Int, Int) -> Lens' Worksheet (Maybe Cell)
atCellRC (Int, Int)
i = (CellMap -> f CellMap) -> Worksheet -> f Worksheet
Lens' Worksheet CellMap
wsCells ((CellMap -> f CellMap) -> Worksheet -> f Worksheet)
-> ((Maybe Cell -> f (Maybe Cell)) -> CellMap -> f CellMap)
-> (Maybe Cell -> f (Maybe Cell))
-> Worksheet
-> f Worksheet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index CellMap -> Lens' CellMap (Maybe (IxValue CellMap))
forall m. At m => Index m -> Lens' m (Maybe (IxValue m))
at (Int, Int)
Index CellMap
i

-- | lens to read, write or delete cell in a worksheet
-- using more traditional x+y or row+column index
atCellXY :: (Int, Int) -> Lens' Worksheet (Maybe Cell)
atCellXY :: (Int, Int) -> Lens' Worksheet (Maybe Cell)
atCellXY (Int, Int)
i = (Int, Int) -> Lens' Worksheet (Maybe Cell)
atCellRC ((Int, Int) -> Lens' Worksheet (Maybe Cell))
-> (Int, Int) -> Lens' Worksheet (Maybe Cell)
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> (Int, Int)
forall a b. (a, b) -> (b, a)
swap (Int, Int)
i

-- | lens to read, write or delete cell value in a worksheet
-- with row+column coordinates, synonym for 'cellValueRC'
cellValueAt :: (Int, Int) -> Lens' Worksheet (Maybe CellValue)
cellValueAt :: (Int, Int) -> Lens' Worksheet (Maybe CellValue)
cellValueAt = (Int, Int)
-> (Maybe CellValue -> f (Maybe CellValue))
-> Worksheet
-> f Worksheet
(Int, Int) -> Lens' Worksheet (Maybe CellValue)
cellValueAtRC

-- | lens to read, write or delete cell value in a worksheet
-- using row+column coordinates of that cell
cellValueAtRC :: (Int, Int) -> Lens' Worksheet (Maybe CellValue)
cellValueAtRC :: (Int, Int) -> Lens' Worksheet (Maybe CellValue)
cellValueAtRC (Int, Int)
i = (Int, Int) -> Lens' Worksheet (Maybe Cell)
atCell (Int, Int)
i ((Maybe Cell -> f (Maybe Cell)) -> Worksheet -> f Worksheet)
-> ((Maybe CellValue -> f (Maybe CellValue))
    -> Maybe Cell -> f (Maybe Cell))
-> (Maybe CellValue -> f (Maybe CellValue))
-> Worksheet
-> f Worksheet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Cell -> Iso' (Maybe Cell) Cell
forall a. Eq a => a -> Iso' (Maybe a) a
non Cell
forall a. Default a => a
def ((Cell -> f Cell) -> Maybe Cell -> f (Maybe Cell))
-> ((Maybe CellValue -> f (Maybe CellValue)) -> Cell -> f Cell)
-> (Maybe CellValue -> f (Maybe CellValue))
-> Maybe Cell
-> f (Maybe Cell)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe CellValue -> f (Maybe CellValue)) -> Cell -> f Cell
Lens' Cell (Maybe CellValue)
cellValue

-- | lens to read, write or delete cell value in a worksheet
-- using traditional x+y coordinates
cellValueAtXY :: (Int, Int) -> Lens' Worksheet (Maybe CellValue)
cellValueAtXY :: (Int, Int) -> Lens' Worksheet (Maybe CellValue)
cellValueAtXY (Int, Int)
i = (Int, Int) -> Lens' Worksheet (Maybe CellValue)
cellValueAtRC ((Int, Int) -> Lens' Worksheet (Maybe CellValue))
-> (Int, Int) -> Lens' Worksheet (Maybe CellValue)
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> (Int, Int)
forall a b. (a, b) -> (b, a)
swap (Int, Int)
i