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

upsert :: (Eq k) => k -> v -> [(k,v)] -> [(k,v)]
upsert :: forall k v. Eq k => 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 forall a. Eq a => a -> a -> Bool
== k
k1
    then (k
k,v
v)forall a. a -> [a] -> [a]
:[(k, v)]
r
    else (k
k1,v
v1)forall a. a -> [a] -> [a]
: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 = Lens' Xlsx [(Text, Worksheet)]
xlSheets forall b c a. (b -> c) -> (a -> b) -> a -> c
. \Worksheet -> f Worksheet
f -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap SheetList -> [(Text, Worksheet)]
unSheetList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Text
s Worksheet -> f Worksheet
f 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 = Lens' Xlsx [(Text, Worksheet)]
xlSheets forall b c a. (b -> c) -> (a -> b) -> a -> c
. \Maybe Worksheet -> f (Maybe Worksheet)
f -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap SheetList -> [(Text, Worksheet)]
unSheetList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall m. At m => Index m -> Lens' m (Maybe (IxValue m))
at Text
s Maybe Worksheet -> f (Maybe Worksheet)
f 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 :: (RowIndex, ColumnIndex) -> Traversal' Worksheet Cell
ixCell :: (RowIndex, ColumnIndex) -> Traversal' Worksheet Cell
ixCell = (RowIndex, ColumnIndex) -> Traversal' Worksheet Cell
ixCellRC

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

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

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

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

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

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

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

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