{-# 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{ unSheetList :: [(Text, Worksheet)] } deriving (Eq, Show, Generic) type instance IxValue (SheetList) = Worksheet type instance Index (SheetList) = Text instance Ixed SheetList where ix k f sl@(SheetList l) = case lookup k l of Just v -> f v <&> \v' -> SheetList (upsert k v' l) Nothing -> pure sl {-# INLINE ix #-} instance At SheetList where at k f (SheetList l) = f mv <&> \r -> case r of Nothing -> SheetList $ maybe l (\v -> deleteBy ((==) `on` fst) (k,v) l) mv Just v' -> SheetList $ upsert k v' l where mv = lookup k l {-# INLINE at #-} upsert :: (Eq k) => k -> v -> [(k,v)] -> [(k,v)] upsert k v [] = [(k,v)] upsert k v ((k1,v1):r) = if k == k1 then (k,v):r else (k1,v1):upsert k v r -- | lens giving access to a worksheet from 'Xlsx' object -- by its name ixSheet :: Text -> Traversal' Xlsx Worksheet ixSheet s = xlSheets . \f -> fmap unSheetList . ix s f . 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 s = xlSheets . \f -> fmap unSheetList . at s f . 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 = ixCellRC -- | lens to access cell in a worksheet ixCellRC :: (RowIndex, ColumnIndex) -> Traversal' Worksheet Cell ixCellRC i = wsCells . ix i -- | lens to access cell in a worksheet using more traditional -- x+y coordinates ixCellXY :: (ColumnIndex, RowIndex) -> Traversal' Worksheet Cell ixCellXY i = ixCellRC $ swap 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 = atCellRC -- | lens to read, write or delete cell in a worksheet atCellRC :: (RowIndex, ColumnIndex) -> Lens' Worksheet (Maybe Cell) atCellRC i = wsCells . at 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 i = atCellRC $ swap 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 = 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 i = atCell i . non def . 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 i = cellValueAtRC $ swap i