packages feed

one-0.0.2: src/Control/One/AsOneItem.hs

{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wall -Werror #-}

module Control.One.AsOneItem where

import Control.Applicative (Const (..), ZipList (..))
import Control.Lens (Prism', preview, prism', review, view, _Just, _Right, _Wrapped)
import Control.One.ReviewOneItem (ReviewOneItem (ReviewOneItemElement, reviewOneItem))
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Lazy as LazyByteString
import qualified Data.ByteString.Short as ShortByteString
import Data.Functor.Identity (Identity (..))
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet
import Data.Hashable (Hashable)
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet
import Data.List.NonEmpty (NonEmpty (..))
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Ord (Down (..))
import Data.Semigroup (Dual (..), First (..), Last (..), Max (..), Min (..), Product (..), Sum (..), WrappedMonoid (..))
import Data.Sequence (Seq, pattern (:<|))
import qualified Data.Sequence as Sequence
import Data.Set (Set)
import qualified Data.Set as Set
import qualified Data.Text as Text
import qualified Data.Text.Lazy as LazyText
import qualified Data.Text.Short as ShortText
import Data.Tuple (Solo (..))
import Data.Word (Word8)
import GHC.Generics (Par1 (..))

class (ReviewOneItem x) => AsOneItem x where
  type AsOneItemElement x

  matchOneItem :: x -> Maybe (AsOneItemElement x)

  _OneItem :: Prism' x (AsOneItemElement x)
  default _OneItem :: (ReviewOneItemElement x ~ AsOneItemElement x) => Prism' x (AsOneItemElement x)
  _OneItem = prism' (review reviewOneItem) matchOneItem

-- | >>> preview _OneItem [7]
-- Just 7
--
-- >>> preview _OneItem [1, 2]
-- Nothing
--
-- >>> preview _OneItem ([] :: [Int])
-- Nothing
--
-- >>> review _OneItem 7 :: [Int]
-- [7]
instance AsOneItem [a] where
  type AsOneItemElement [a] = a

  matchOneItem = \case
    [a] -> Just a
    _ -> Nothing

-- | >>> preview _OneItem (7 :| [])
-- Just 7
--
-- >>> preview _OneItem (1 :| [2])
-- Nothing
--
-- >>> review _OneItem 7 :: NonEmpty Int
-- 7 :| []
instance AsOneItem (NonEmpty a) where
  type AsOneItemElement (NonEmpty a) = a

  matchOneItem = \case
    a :| [] -> Just a
    _ -> Nothing

-- | >>> preview _OneItem (Sequence.singleton 7)
-- Just 7
--
-- >>> preview _OneItem (Sequence.fromList [1, 2])
-- Nothing
--
-- >>> preview _OneItem Sequence.empty
-- Nothing
--
-- >>> review _OneItem 7 :: Seq Int
-- fromList [7]
instance AsOneItem (Seq a) where
  type AsOneItemElement (Seq a) = a

  matchOneItem = \case
    a :<| Sequence.Empty -> Just a
    _ -> Nothing

-- | >>> preview _OneItem (Text.singleton 'a')
-- Just 'a'
--
-- >>> preview _OneItem (Text.pack "ab")
-- Nothing
--
-- >>> preview _OneItem Text.empty
-- Nothing
--
-- >>> review _OneItem 'a' :: Text.Text
-- "a"
instance AsOneItem Text.Text where
  type AsOneItemElement Text.Text = Char

  matchOneItem x = case Text.uncons x of
    Just (c, rest) | Text.null rest -> Just c
    _ -> Nothing

-- | >>> preview _OneItem (LazyText.singleton 'a')
-- Just 'a'
--
-- >>> preview _OneItem (LazyText.pack "ab")
-- Nothing
--
-- >>> review _OneItem 'a' :: LazyText.Text
-- "a"
instance AsOneItem LazyText.Text where
  type AsOneItemElement LazyText.Text = Char

  matchOneItem x = case LazyText.uncons x of
    Just (c, rest) | LazyText.null rest -> Just c
    _ -> Nothing

-- | >>> preview _OneItem (ShortText.singleton 'a')
-- Just 'a'
--
-- >>> preview _OneItem (ShortText.pack "ab")
-- Nothing
--
-- >>> preview _OneItem (ShortText.pack "")
-- Nothing
--
-- >>> review _OneItem 'a' :: ShortText.ShortText
-- "a"
instance AsOneItem ShortText.ShortText where
  type AsOneItemElement ShortText.ShortText = Char

  matchOneItem x = case ShortText.uncons x of
    Just (c, rest) | ShortText.null rest -> Just c
    _ -> Nothing

-- | >>> preview _OneItem (ByteString.singleton 65)
-- Just 65
--
-- >>> preview _OneItem (ByteString.pack [1, 2])
-- Nothing
--
-- >>> preview _OneItem ByteString.empty
-- Nothing
--
-- >>> review _OneItem 65 :: ByteString.ByteString
-- "A"
instance AsOneItem ByteString.ByteString where
  type AsOneItemElement ByteString.ByteString = Word8

  matchOneItem x = case ByteString.uncons x of
    Just (w, rest) | ByteString.null rest -> Just w
    _ -> Nothing

-- | >>> preview _OneItem (LazyByteString.singleton 65)
-- Just 65
--
-- >>> preview _OneItem (LazyByteString.pack [1, 2])
-- Nothing
--
-- >>> review _OneItem 65 :: LazyByteString.ByteString
-- "A"
instance AsOneItem LazyByteString.ByteString where
  type AsOneItemElement LazyByteString.ByteString = Word8

  matchOneItem x = case LazyByteString.uncons x of
    Just (w, rest) | LazyByteString.null rest -> Just w
    _ -> Nothing

-- | >>> preview _OneItem (ShortByteString.singleton 65)
-- Just 65
--
-- >>> preview _OneItem (ShortByteString.pack [1, 2])
-- Nothing
--
-- >>> review _OneItem 65 :: ShortByteString.ShortByteString
-- "A"
instance AsOneItem ShortByteString.ShortByteString where
  type AsOneItemElement ShortByteString.ShortByteString = Word8

  matchOneItem x =
    if ShortByteString.length x == 1 then Just (ShortByteString.index x 0) else Nothing

-- | >>> preview _OneItem (Map.singleton "key" (7 :: Int))
-- Just ("key",7)
--
-- >>> preview _OneItem (Map.fromList [("a", 1), ("b", 2)] :: Map String Int)
-- Nothing
--
-- >>> review _OneItem ("key", 7) :: Map String Int
-- fromList [("key",7)]
instance AsOneItem (Map k v) where
  type AsOneItemElement (Map k v) = (k, v)

  matchOneItem x =
    if Map.size x == 1 then Just (Map.findMin x) else Nothing

-- | >>> preview _OneItem (HashMap.singleton "key" (7 :: Int))
-- Just ("key",7)
--
-- >>> review _OneItem ("key", 7) :: HashMap String Int
-- fromList [("key",7)]
instance (Hashable k) => AsOneItem (HashMap k v) where
  type AsOneItemElement (HashMap k v) = (k, v)

  matchOneItem x =
    case HashMap.toList x of
      [kv] -> Just kv
      _ -> Nothing

-- | >>> preview _OneItem (IntMap.singleton 1 7)
-- Just (1,7)
--
-- >>> preview _OneItem (IntMap.fromList [(1, 2), (3, 4)])
-- Nothing
--
-- >>> review _OneItem (1, 7) :: IntMap Int
-- fromList [(1,7)]
instance AsOneItem (IntMap v) where
  type AsOneItemElement (IntMap v) = (Int, v)

  matchOneItem x =
    if IntMap.size x == 1 then Just (IntMap.findMin x) else Nothing

-- | >>> preview _OneItem (Set.singleton 7)
-- Just 7
--
-- >>> preview _OneItem (Set.fromList [1, 2])
-- Nothing
--
-- >>> review _OneItem 7 :: Set Int
-- fromList [7]
instance AsOneItem (Set a) where
  type AsOneItemElement (Set a) = a

  matchOneItem x =
    if Set.size x == 1 then Just (Set.findMin x) else Nothing

-- | >>> preview _OneItem (HashSet.singleton 7)
-- Just 7
--
-- >>> review _OneItem 7 :: HashSet Int
-- fromList [7]
instance (Hashable a) => AsOneItem (HashSet a) where
  type AsOneItemElement (HashSet a) = a

  matchOneItem x =
    case HashSet.toList x of
      [a] -> Just a
      _ -> Nothing

-- | >>> preview _OneItem (IntSet.singleton 7)
-- Just 7
--
-- >>> preview _OneItem (IntSet.fromList [1, 2])
-- Nothing
--
-- >>> review _OneItem 7 :: IntSet
-- fromList [7]
instance AsOneItem IntSet where
  type AsOneItemElement IntSet = Int

  matchOneItem x =
    if IntSet.size x == 1 then Just (IntSet.findMin x) else Nothing

-- | >>> preview _OneItem ("", 7 :: Int) :: Maybe Int
-- Just 7
--
-- >>> preview _OneItem ("nonempty", 7 :: Int) :: Maybe Int
-- Nothing
--
-- >>> review _OneItem 7 :: (String, Int)
-- ("",7)
instance (Eq a, Monoid a) => AsOneItem (a, b) where
  type AsOneItemElement (a, b) = b

  matchOneItem = \case
    (a, b) -> if a == mempty then Just b else Nothing

-- | >>> preview _OneItem (Just 7)
-- Just 7
--
-- >>> preview _OneItem (Nothing :: Maybe Int)
-- Nothing
--
-- >>> review _OneItem 7 :: Maybe Int
-- Just 7
instance AsOneItem (Maybe a) where
  type AsOneItemElement (Maybe a) = a

  matchOneItem =
    preview _Just
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Right 7 :: Either String Int)
-- Just 7
--
-- >>> preview _OneItem (Left "err" :: Either String Int)
-- Nothing
--
-- >>> review _OneItem 7 :: Either String Int
-- Right 7
instance AsOneItem (Either a b) where
  type AsOneItemElement (Either a b) = b

  matchOneItem =
    preview _Right
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Identity 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Identity Int
-- Identity 7
instance AsOneItem (Identity a) where
  type AsOneItemElement (Identity a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Const 7 :: Const Int String)
-- Just 7
--
-- >>> review _OneItem 7 :: Const Int String
-- Const 7
instance AsOneItem (Const a b) where
  type AsOneItemElement (Const a b) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (First 7)
-- Just 7
--
-- >>> review _OneItem 7 :: First Int
-- First {getFirst = 7}
instance AsOneItem (First a) where
  type AsOneItemElement (First a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Last 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Last Int
-- Last {getLast = 7}
instance AsOneItem (Last a) where
  type AsOneItemElement (Last a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (WrapMonoid 7)
-- Just 7
--
-- >>> review _OneItem 7 :: WrappedMonoid Int
-- WrapMonoid {unwrapMonoid = 7}
instance AsOneItem (WrappedMonoid a) where
  type AsOneItemElement (WrappedMonoid a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Dual 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Dual Int
-- Dual {getDual = 7}
instance AsOneItem (Dual a) where
  type AsOneItemElement (Dual a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Down 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Down Int
-- Down 7
instance AsOneItem (Down a) where
  type AsOneItemElement (Down a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Sum 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Sum Int
-- Sum {getSum = 7}
instance AsOneItem (Sum a) where
  type AsOneItemElement (Sum a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Product 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Product Int
-- Product {getProduct = 7}
instance AsOneItem (Product a) where
  type AsOneItemElement (Product a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Min 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Min Int
-- Min {getMin = 7}
instance AsOneItem (Min a) where
  type AsOneItemElement (Min a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Max 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Max Int
-- Max {getMax = 7}
instance AsOneItem (Max a) where
  type AsOneItemElement (Max a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (ZipList [7])
-- Just 7
--
-- >>> preview _OneItem (ZipList [1, 2])
-- Nothing
--
-- >>> preview _OneItem (ZipList ([] :: [Int]))
-- Nothing
--
-- >>> review _OneItem 7 :: ZipList Int
-- ZipList {getZipList = [7]}
instance AsOneItem (ZipList a) where
  type AsOneItemElement (ZipList a) = a

  matchOneItem =
    matchOneItem . view _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (Par1 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Par1 Int
-- Par1 {unPar1 = 7}
instance AsOneItem (Par1 a) where
  type AsOneItemElement (Par1 a) = a

  matchOneItem =
    preview _Wrapped
  {-# INLINE matchOneItem #-}

-- | >>> preview _OneItem (MkSolo 7)
-- Just 7
--
-- >>> review _OneItem 7 :: Solo Int
-- MkSolo 7
instance AsOneItem (Solo a) where
  type AsOneItemElement (Solo a) = a

  matchOneItem (MkSolo x) = Just x
  {-# INLINE matchOneItem #-}