{-# LANGUAGE CPP #-}
{-# LANGUAGE MultiParamTypeClasses #-}

{-|
Description:    Functions providing metadata about the contents of a disc.

Copyright:      (c) 2019-2021 Sam May
License:        GPL-3.0-or-later
Maintainer:     ag@eitilt.life

Stability:      stable
Portability:    non-portable (requires libcdio)

Metadata is stored in a binary format both library-internally and on the disc.
Most audio archivists will recognize it as "those information fields in a CUE
file" (though there are [other
formats](https://www.gnu.org/software/libcdio/cd-text-format.html#Sony-Text-File-Format-_0028Input-Sheet-Version-0_002e7T_0029)
as well), and casual listeners will recognize it as the scrolling text that
you're always happy to see, on the rare times your music player shows it.
Little-used and even-less-known, however, is that a single disc can
theoretically contain metadata in up to eight different languages; because of
the complexity that introduces, it makes more sense to use a second monadic
interface than to try to provide a single monolithic datatype within 'Cdio'.
-}
module Sound.Libcdio.Read.CdText
    ( -- * Types
      CdText
    , CdTextError ( .. )
    , CdTextErrorType ( .. )
    , Info ( .. )
    , emptyInfo
    , Foreign.Genre ( .. )
    , Foreign.Language ( .. )
      -- * Evaluation
    , cdText
    , parseCdText
    , withLanguage
    , withIndex
    , withAll
    , runCdText
      -- * Data
    , language
    , languages
    , firstTrack
    , lastTrack
    , info
    , discId
    , genre
    , cdTextRaw
    ) where


import qualified Control.Applicative as A
#if MIN_VERSION_mtl(2,2,1)
import qualified Control.Monad.Except as N.E
#else
import qualified Control.Monad.Error as N.E
#endif
import qualified Control.Monad.Fail as N.F

import qualified Data.ByteString as BS
import qualified Data.Maybe as Y
import qualified Data.Text as T

import qualified Foreign.Libcdio.CdText as Foreign
import qualified Foreign.Libcdio.Disc as Foreign
import qualified Foreign.Libcdio.Logging as Foreign

import qualified Text.Show as R

import Sound.Libcdio.Logging
import Sound.Libcdio.Track
import Sound.Libcdio.Types.Cdio

import Control.Applicative ( (<|>) )
import Data.Functor ( ($>) )


-- | A computation within the environment of metadata (in a particular
-- language) stored on a CD.  The options for affecting that environment
-- from within are limited by design, as this library is intended for /reading/
-- discs rather than /authoring/ them.
newtype CdText a = CdText (Maybe InitialLanguage -> Foreign.Cdio -> IO (Either CdTextError a))
instance Functor CdText where
    fmap :: (a -> b) -> CdText a -> CdText b
fmap a -> b
f (CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
a) = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
-> CdText b
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
 -> CdText b)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
-> CdText b
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
l Cdio
c -> (a -> b) -> Either CdTextError a -> Either CdTextError b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f (Either CdTextError a -> Either CdTextError b)
-> IO (Either CdTextError a) -> IO (Either CdTextError b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
a Maybe InitialLanguage
l Cdio
c
instance Applicative CdText where
    pure :: a -> CdText a
pure a
a = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ -> Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> Either CdTextError a -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ a -> Either CdTextError a
forall a b. b -> Either a b
Right a
a
    CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError (a -> b))
f <*> :: CdText (a -> b) -> CdText a -> CdText b
<*> CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
a = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
-> CdText b
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
 -> CdText b)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
-> CdText b
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
l Cdio
c -> do
        Either CdTextError (a -> b)
f' <- Maybe InitialLanguage -> Cdio -> IO (Either CdTextError (a -> b))
f Maybe InitialLanguage
l Cdio
c
        Either CdTextError a
a' <- Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
a Maybe InitialLanguage
l Cdio
c
        Either CdTextError b -> IO (Either CdTextError b)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError b -> IO (Either CdTextError b))
-> Either CdTextError b -> IO (Either CdTextError b)
forall a b. (a -> b) -> a -> b
$ Either CdTextError (a -> b)
f' Either CdTextError (a -> b)
-> Either CdTextError a -> Either CdTextError b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Either CdTextError a
a'
-- | 'A.empty' fails with 'CdTextEmpty'.
instance A.Alternative CdText where
    empty :: CdText a
empty = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ -> Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> (Text -> Either CdTextError a)
-> Text
-> IO (Either CdTextError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextError -> Either CdTextError a
forall a b. a -> Either a b
Left (CdTextError -> Either CdTextError a)
-> (Text -> CdTextError) -> Text -> Either CdTextError a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextErrorType -> Text -> CdTextError
CdTextError CdTextErrorType
CdTextEmpty (Text -> IO (Either CdTextError a))
-> Text -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
"empty"
    CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f <|> :: CdText a -> CdText a -> CdText a
<|> CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
g = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
l Cdio
c -> Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f Maybe InitialLanguage
l Cdio
c IO (Either CdTextError a)
-> IO (Either CdTextError a) -> IO (Either CdTextError a)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
g Maybe InitialLanguage
l Cdio
c
instance Monad CdText where
    CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
a >>= :: CdText a -> (a -> CdText b) -> CdText b
>>= a -> CdText b
f = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
-> CdText b
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
 -> CdText b)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b))
-> CdText b
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
l Cdio
c -> do
        Either CdTextError a
a' <- Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
a Maybe InitialLanguage
l Cdio
c
        let bind' :: a -> IO (Either CdTextError b)
bind' a
a'' = let CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b)
b = a -> CdText b
f a
a'' in Maybe InitialLanguage -> Cdio -> IO (Either CdTextError b)
b Maybe InitialLanguage
l Cdio
c
        (CdTextError -> IO (Either CdTextError b))
-> (a -> IO (Either CdTextError b))
-> Either CdTextError a
-> IO (Either CdTextError b)
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Either CdTextError b -> IO (Either CdTextError b)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError b -> IO (Either CdTextError b))
-> (CdTextError -> Either CdTextError b)
-> CdTextError
-> IO (Either CdTextError b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextError -> Either CdTextError b
forall a b. a -> Either a b
Left) a -> IO (Either CdTextError b)
bind' Either CdTextError a
a'
-- | Wraps the text in a 'FreeformCdTextError', for recovery with 'N.E.catchError'.
instance N.F.MonadFail CdText where
    fail :: String -> CdText a
fail String
e = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ ->
        Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> (CdTextError -> Either CdTextError a)
-> CdTextError
-> IO (Either CdTextError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextError -> Either CdTextError a
forall a b. a -> Either a b
Left (CdTextError -> IO (Either CdTextError a))
-> CdTextError -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ CdTextErrorType -> Text -> CdTextError
CdTextError (Text -> CdTextErrorType
FreeformCdTextError (Text -> CdTextErrorType) -> Text -> CdTextErrorType
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
e) (String -> Text
T.pack String
"fail")
instance N.E.MonadError CdTextError CdText where
    throwError :: CdTextError -> CdText a
throwError CdTextError
err = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ -> Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> Either CdTextError a -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ CdTextError -> Either CdTextError a
forall a b. a -> Either a b
Left CdTextError
err
    catchError :: CdText a -> (CdTextError -> CdText a) -> CdText a
catchError (CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f) CdTextError -> CdText a
e = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
l Cdio
c -> Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f Maybe InitialLanguage
l Cdio
c IO (Either CdTextError a)
-> (Either CdTextError a -> IO (Either CdTextError a))
-> IO (Either CdTextError a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \Either CdTextError a
a' -> case Either CdTextError a
a' of
        Left CdTextError
err ->
            let CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
g = CdTextError -> CdText a
e CdTextError
err
            in  Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
g Maybe InitialLanguage
l Cdio
c
        Right a
a -> Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> Either CdTextError a -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ a -> Either CdTextError a
forall a b. b -> Either a b
Right a
a
instance LibcdioLogger CdText where
    logCutoff :: CdText LogLevel
logCutoff = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError LogLevel))
-> CdText LogLevel
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage
  -> Cdio -> IO (Either CdTextError LogLevel))
 -> CdText LogLevel)
-> (Maybe InitialLanguage
    -> Cdio -> IO (Either CdTextError LogLevel))
-> CdText LogLevel
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ -> LogLevel -> Either CdTextError LogLevel
forall a b. b -> Either a b
Right (LogLevel -> Either CdTextError LogLevel)
-> IO LogLevel -> IO (Either CdTextError LogLevel)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO LogLevel
Foreign.logCutoff
    setLogCutoff :: LogLevel -> CdText ()
setLogCutoff LogLevel
l = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
-> CdText ()
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
 -> CdText ())
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
-> CdText ()
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ -> () -> Either CdTextError ()
forall a b. b -> Either a b
Right (() -> Either CdTextError ())
-> IO () -> IO (Either CdTextError ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> LogLevel -> IO ()
Foreign.setLogCutoff LogLevel
l
    readLog :: CdText [LogEntry]
readLog = (Maybe InitialLanguage
 -> Cdio -> IO (Either CdTextError [LogEntry]))
-> CdText [LogEntry]
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage
  -> Cdio -> IO (Either CdTextError [LogEntry]))
 -> CdText [LogEntry])
-> (Maybe InitialLanguage
    -> Cdio -> IO (Either CdTextError [LogEntry]))
-> CdText [LogEntry]
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ -> [LogEntry] -> Either CdTextError [LogEntry]
forall a b. b -> Either a b
Right ([LogEntry] -> Either CdTextError [LogEntry])
-> IO [LogEntry] -> IO (Either CdTextError [LogEntry])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [LogEntry]
Foreign.readLog
    clearLog :: CdText ()
clearLog = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
-> CdText ()
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
 -> CdText ())
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
-> CdText ()
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ -> () -> Either CdTextError ()
forall a b. b -> Either a b
Right (() -> Either CdTextError ())
-> IO () -> IO (Either CdTextError ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO ()
Foreign.clearLog
    putLog :: LogEntry -> CdText ()
putLog LogEntry
e = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
-> CdText ()
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
 -> CdText ())
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError ()))
-> CdText ()
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
_ -> () -> Either CdTextError ()
forall a b. b -> Either a b
Right (() -> Either CdTextError ())
-> IO () -> IO (Either CdTextError ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> LogEntry -> IO ()
Foreign.putLog LogEntry
e

-- | Lift a metadata computation from the C-style "Foreign.Libcdio" interface
-- into the monadic "Sound.Libcdio".
liftCdText :: (Foreign.Cdio -> IO a) -> CdText a
liftCdText :: (Cdio -> IO a) -> CdText a
liftCdText Cdio -> IO a
f = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
_ Cdio
c -> a -> Either CdTextError a
forall a b. b -> Either a b
Right (a -> Either CdTextError a) -> IO a -> IO (Either CdTextError a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Cdio -> IO a
f Cdio
c


-- | Associates a well-typed error with human-readable context information.
data CdTextError = CdTextError CdTextErrorType T.Text
  deriving ( CdTextError -> CdTextError -> Bool
(CdTextError -> CdTextError -> Bool)
-> (CdTextError -> CdTextError -> Bool) -> Eq CdTextError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CdTextError -> CdTextError -> Bool
$c/= :: CdTextError -> CdTextError -> Bool
== :: CdTextError -> CdTextError -> Bool
$c== :: CdTextError -> CdTextError -> Bool
Eq, Int -> CdTextError -> ShowS
[CdTextError] -> ShowS
CdTextError -> String
(Int -> CdTextError -> ShowS)
-> (CdTextError -> String)
-> ([CdTextError] -> ShowS)
-> Show CdTextError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CdTextError] -> ShowS
$cshowList :: [CdTextError] -> ShowS
show :: CdTextError -> String
$cshow :: CdTextError -> String
showsPrec :: Int -> CdTextError -> ShowS
$cshowsPrec :: Int -> CdTextError -> ShowS
Show, ReadPrec [CdTextError]
ReadPrec CdTextError
Int -> ReadS CdTextError
ReadS [CdTextError]
(Int -> ReadS CdTextError)
-> ReadS [CdTextError]
-> ReadPrec CdTextError
-> ReadPrec [CdTextError]
-> Read CdTextError
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [CdTextError]
$creadListPrec :: ReadPrec [CdTextError]
readPrec :: ReadPrec CdTextError
$creadPrec :: ReadPrec CdTextError
readList :: ReadS [CdTextError]
$creadList :: ReadS [CdTextError]
readsPrec :: Int -> ReadS CdTextError
$creadsPrec :: Int -> ReadS CdTextError
Read )

-- | Potential situations which may cause a computation to fail.
data CdTextErrorType
    = InvalidBlock Word
        -- ^ The requested language index is outside of the bounds accessible
        -- by the library (@[0..7]@ in libcdio 2.1 and after, or the smaller
        -- range of languages returned by 'languages' before that version).
    | LanguageNotFound Foreign.Language
        -- ^ The CdText data does not contain the requested language, or
        -- 'Foreign.UnknownLanguage' was requested (prior to libcdio 2.1 there
        -- was no way to select the latter blocks; even after that version,
        -- 'withIndex' must be used instead).
    | BadBinaryRead
        -- ^ The binary data can not be parsed into a CdText object.
    | NoCdText
        -- ^ The CD contains no CdText data.
    | CdTextEmpty
        -- ^ 'A.empty' was called and no better alternative was encountered.
    | FreeformCdTextError T.Text
        -- ^ Escape hatch from structured typing to allow user-specified
        -- (and user-triggered) errors.
  deriving ( CdTextErrorType -> CdTextErrorType -> Bool
(CdTextErrorType -> CdTextErrorType -> Bool)
-> (CdTextErrorType -> CdTextErrorType -> Bool)
-> Eq CdTextErrorType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CdTextErrorType -> CdTextErrorType -> Bool
$c/= :: CdTextErrorType -> CdTextErrorType -> Bool
== :: CdTextErrorType -> CdTextErrorType -> Bool
$c== :: CdTextErrorType -> CdTextErrorType -> Bool
Eq, Int -> CdTextErrorType -> ShowS
[CdTextErrorType] -> ShowS
CdTextErrorType -> String
(Int -> CdTextErrorType -> ShowS)
-> (CdTextErrorType -> String)
-> ([CdTextErrorType] -> ShowS)
-> Show CdTextErrorType
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CdTextErrorType] -> ShowS
$cshowList :: [CdTextErrorType] -> ShowS
show :: CdTextErrorType -> String
$cshow :: CdTextErrorType -> String
showsPrec :: Int -> CdTextErrorType -> ShowS
$cshowsPrec :: Int -> CdTextErrorType -> ShowS
Show, ReadPrec [CdTextErrorType]
ReadPrec CdTextErrorType
Int -> ReadS CdTextErrorType
ReadS [CdTextErrorType]
(Int -> ReadS CdTextErrorType)
-> ReadS [CdTextErrorType]
-> ReadPrec CdTextErrorType
-> ReadPrec [CdTextErrorType]
-> Read CdTextErrorType
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [CdTextErrorType]
$creadListPrec :: ReadPrec [CdTextErrorType]
readPrec :: ReadPrec CdTextErrorType
$creadPrec :: ReadPrec CdTextErrorType
readList :: ReadS [CdTextErrorType]
$creadList :: ReadS [CdTextErrorType]
readsPrec :: Int -> ReadS CdTextErrorType
$creadsPrec :: Int -> ReadS CdTextErrorType
Read )


-- | The language which was active at the beginning of the 'CdText'
-- computation.
data InitialLanguage
    = Lang Foreign.Language
        -- ^ The language was chosen specifically.
    | Index Word
        -- ^ The language was chosen by index.
    | Default
        -- ^ No language was explicitly set.

-- | Restore the active language to whatever it was before a 'CdText'
-- computation was run.
resetLanguage :: Maybe InitialLanguage -> Foreign.Cdio -> IO ()
resetLanguage :: Maybe InitialLanguage -> Cdio -> IO ()
resetLanguage Maybe InitialLanguage
Nothing Cdio
_ = IO ()
forall a. Monoid a => a
mempty
resetLanguage (Just (Lang Language
l)) Cdio
c = Cdio -> Language -> IO Bool
Foreign.selectLanguage Cdio
c Language
l IO Bool -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO ()
forall a. Monoid a => a
mempty
resetLanguage (Just (Index Word
i)) Cdio
c = Cdio -> Word -> IO Bool
Foreign.selectLanguageIndex Cdio
c Word
i IO Bool -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO ()
forall a. Monoid a => a
mempty
resetLanguage (Just InitialLanguage
Default) Cdio
c = Cdio -> Word -> IO Bool
Foreign.selectLanguageIndex Cdio
c Word
0 IO Bool -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO ()
forall a. Monoid a => a
mempty


-- | Textual metadata describing a single track on a disc, or the disc itself.
data Info = Info
    { Info -> Maybe Text
title      :: Maybe T.Text
    , Info -> Maybe Text
performer  :: Maybe T.Text
    , Info -> Maybe Text
songwriter :: Maybe T.Text
    , Info -> Maybe Text
composer   :: Maybe T.Text
    , Info -> Maybe Text
arranger   :: Maybe T.Text
    , Info -> Maybe Text
message    :: Maybe T.Text
        -- ^ An otherwise-uncategorized comment.
    , Info -> Maybe Text
code       :: Maybe T.Text
        -- ^ Either a UPC/EAN (for the disc) or an ISRC (for a track).
    }
  deriving ( Info -> Info -> Bool
(Info -> Info -> Bool) -> (Info -> Info -> Bool) -> Eq Info
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Info -> Info -> Bool
$c/= :: Info -> Info -> Bool
== :: Info -> Info -> Bool
$c== :: Info -> Info -> Bool
Eq )
-- | Modeled after the standard record syntax, but omitting any 'Nothing'
-- fields for space reasons.
instance Show Info where
    showsPrec :: Int -> Info -> ShowS
showsPrec Int
d Info
ts = Bool -> ShowS -> ShowS
R.showParen (Int
d Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
10)
        (ShowS -> ShowS) -> ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$ String -> ShowS
R.showString String
"Info { "
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Info -> Maybe Text) -> String -> ShowS
forall a. Show a => (Info -> Maybe a) -> String -> ShowS
maybeShows Info -> Maybe Text
title String
"title"
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Info -> Maybe Text) -> String -> ShowS
forall a. Show a => (Info -> Maybe a) -> String -> ShowS
maybeShows Info -> Maybe Text
performer String
"performer"
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Info -> Maybe Text) -> String -> ShowS
forall a. Show a => (Info -> Maybe a) -> String -> ShowS
maybeShows Info -> Maybe Text
songwriter String
"songwriter"
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Info -> Maybe Text) -> String -> ShowS
forall a. Show a => (Info -> Maybe a) -> String -> ShowS
maybeShows Info -> Maybe Text
composer String
"composer"
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Info -> Maybe Text) -> String -> ShowS
forall a. Show a => (Info -> Maybe a) -> String -> ShowS
maybeShows Info -> Maybe Text
arranger String
"arranger"
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Info -> Maybe Text) -> String -> ShowS
forall a. Show a => (Info -> Maybe a) -> String -> ShowS
maybeShows Info -> Maybe Text
message String
"message"
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Info -> Maybe Text) -> String -> ShowS
forall a. Show a => (Info -> Maybe a) -> String -> ShowS
maybeShows Info -> Maybe Text
code String
"code"
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
R.showString String
"}"
      where maybeShows :: (Info -> Maybe a) -> String -> ShowS
maybeShows Info -> Maybe a
f String
n
                | Just a
t <- Info -> Maybe a
f Info
ts =
                      String -> ShowS
R.showString (String
n String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" = ")
                    ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> ShowS
forall a. Show a => a -> ShowS
shows a
t
                    ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
R.showString String
", "
                | Bool
otherwise = ShowS
forall a. a -> a
id
instance Read Info where
    readsPrec :: Int -> ReadS Info
readsPrec Int
p =
           Bool -> ReadS Info -> ReadS Info
forall a. Bool -> ReadS a -> ReadS a
readParen (Int
p Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
10) (ReadS Info -> ReadS Info) -> ReadS Info -> ReadS Info
forall a b. (a -> b) -> a -> b
$ \String
r ->
            [ (Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Info
Info Maybe Text
t Maybe Text
f Maybe Text
w Maybe Text
c Maybe Text
a Maybe Text
m Maybe Text
o, String
r10)
            | (String
"Info", String
r1) <- ReadS String
lex String
r
            , (String
"{", String
r2) <- ReadS String
lex String
r1
            , (Maybe Text
t, String
r3) <- String -> String -> [(Maybe Text, String)]
forall a. Read a => String -> String -> [(Maybe a, String)]
maybeLex String
"title" String
r2
            , (Maybe Text
f, String
r4) <- String -> String -> [(Maybe Text, String)]
forall a. Read a => String -> String -> [(Maybe a, String)]
maybeLex String
"performer" String
r3
            , (Maybe Text
w, String
r5) <- String -> String -> [(Maybe Text, String)]
forall a. Read a => String -> String -> [(Maybe a, String)]
maybeLex String
"songwriter" String
r4
            , (Maybe Text
c, String
r6) <- String -> String -> [(Maybe Text, String)]
forall a. Read a => String -> String -> [(Maybe a, String)]
maybeLex String
"composer" String
r5
            , (Maybe Text
a, String
r7) <- String -> String -> [(Maybe Text, String)]
forall a. Read a => String -> String -> [(Maybe a, String)]
maybeLex String
"arranger" String
r6
            , (Maybe Text
m, String
r8) <- String -> String -> [(Maybe Text, String)]
forall a. Read a => String -> String -> [(Maybe a, String)]
maybeLex String
"message" String
r7
            , (Maybe Text
o, String
r9) <- String -> String -> [(Maybe Text, String)]
forall a. Read a => String -> String -> [(Maybe a, String)]
maybeLex String
"code" String
r8
            , (String
"}", String
r10) <- ReadS String
lex String
r9
            ]
      where maybeLex :: String -> String -> [(Maybe a, String)]
maybeLex String
t String
s' = do
                (String
t', String
s1) <- ReadS String
lex String
s'
                if String
t String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
t'
                then do
                    (String
"=", String
s2) <- ReadS String
lex String
s1
                    (a
x, String
s3) <- ReadS a
forall a. Read a => ReadS a
reads String
s2
                    (String
",", String
s4) <- ReadS String
lex String
s3
                    (Maybe a, String) -> [(Maybe a, String)]
forall (m :: * -> *) a. Monad m => a -> m a
return (a -> Maybe a
forall a. a -> Maybe a
Just a
x, String
s4)
                else (Maybe a, String) -> [(Maybe a, String)]
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe a
forall a. Maybe a
Nothing, String
s')

-- | An 'Info' object with values suitable as defaults.
emptyInfo :: Info
emptyInfo :: Info
emptyInfo = Info :: Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Info
Info
    { title :: Maybe Text
title      = Maybe Text
forall a. Maybe a
Nothing
    , performer :: Maybe Text
performer  = Maybe Text
forall a. Maybe a
Nothing
    , songwriter :: Maybe Text
songwriter = Maybe Text
forall a. Maybe a
Nothing
    , composer :: Maybe Text
composer   = Maybe Text
forall a. Maybe a
Nothing
    , arranger :: Maybe Text
arranger   = Maybe Text
forall a. Maybe a
Nothing
    , message :: Maybe Text
message    = Maybe Text
forall a. Maybe a
Nothing
    , code :: Maybe Text
code       = Maybe Text
forall a. Maybe a
Nothing
    }


-- | Use a C-style 'Foreign.Cdio' object as the base to run a Haskell-style
-- 'CdText' computation.
-- 
-- Note that some invariants of the monadic interface may not work as expected
-- when used with the mutable objects, usually due to changing the active
-- language block:
-- 
-- @
-- l  <- 'Foreign.language' cdio
-- _  <- 'runCdText' cdio $ 'withIndex' i g
-- l' <- 'Foreign.language' cdio
-- (l == l') == undefined
-- @
runCdText :: Foreign.Cdio -> CdText a -> IO (Either CdTextError a)
runCdText :: Cdio -> CdText a -> IO (Either CdTextError a)
runCdText Cdio
c (CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f) = IO (Either CdTextError a) -> IO (Either CdTextError a)
forall (m :: * -> *) a. (Monad m, LibcdioLogger m) => m a -> m a
isolateLogs (IO (Either CdTextError a) -> IO (Either CdTextError a))
-> IO (Either CdTextError a) -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f Maybe InitialLanguage
forall a. Maybe a
Nothing Cdio
c


-- | Run the given computation within the CdText data associated with the
-- 'Cdio' session.  At this top level, a @'withIndex' 0@ specifically will
-- almost always have the same effect as running the computation directly.
-- Fails with 'NoCdText' if the disc doesn't provide any metadata.
cdText :: CdText a -> Cdio (Either CdTextError a)
cdText :: CdText a -> Cdio (Either CdTextError a)
cdText (CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f) = (Cdio -> IO (Either CdTextError a)) -> Cdio (Either CdTextError a)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Either CdTextError a))
 -> Cdio (Either CdTextError a))
-> (Cdio -> IO (Either CdTextError a))
-> Cdio (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ \Cdio
c' -> if Cdio -> Bool
Foreign.hasCdText Cdio
c'
    then IO (Either CdTextError a) -> IO (Either CdTextError a)
forall (m :: * -> *) a. (Monad m, LibcdioLogger m) => m a -> m a
isolateLogs (IO (Either CdTextError a) -> IO (Either CdTextError a))
-> IO (Either CdTextError a) -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f (InitialLanguage -> Maybe InitialLanguage
forall a. a -> Maybe a
Just InitialLanguage
Default) Cdio
c'
    else Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> (Text -> Either CdTextError a)
-> Text
-> IO (Either CdTextError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextError -> Either CdTextError a
forall a b. a -> Either a b
Left (CdTextError -> Either CdTextError a)
-> (Text -> CdTextError) -> Text -> Either CdTextError a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextErrorType -> Text -> CdTextError
CdTextError CdTextErrorType
NoCdText (Text -> IO (Either CdTextError a))
-> Text -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
"cdText"

-- | Given a binary stream, attempt to parse it as a CdText block and run the
-- given computation.  Returns @'Left' 'BadBinaryRead'@ if that parse fails.
-- At this top level, a @'withIndex' 0@ specifically will almost always have
-- the same effect as running the computation directly.
--
-- Note that binary CdText dumps will frequently include four bytes at the
-- beginning indicating the size of the file; this implementation expects that
-- those bytes /are not/ included.  If your dump does indeed begin with them,
-- @'BS.drop' 4@ before passing the 'BS.ByteString' to this function.
--
-- /Before libcdio 0.94:  Always returns @'Left' 'BadBinaryRead'@/
parseCdText :: BS.ByteString -> CdText a -> IO (Either CdTextError a)
parseCdText :: ByteString -> CdText a -> IO (Either CdTextError a)
parseCdText ByteString
bs (CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f) = IO (Either CdTextError a) -> IO (Either CdTextError a)
forall (m :: * -> *) a. (Monad m, LibcdioLogger m) => m a -> m a
isolateLogs (IO (Either CdTextError a) -> IO (Either CdTextError a))
-> IO (Either CdTextError a) -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ do
    Maybe Cdio
c <- ByteString -> IO (Maybe Cdio)
Foreign.cdTextDataInit ByteString
bs
    case Maybe Cdio
c of
        Just Cdio
c' -> Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f (InitialLanguage -> Maybe InitialLanguage
forall a. a -> Maybe a
Just InitialLanguage
Default) Cdio
c'
        Maybe Cdio
Nothing -> Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> (Text -> Either CdTextError a)
-> Text
-> IO (Either CdTextError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextError -> Either CdTextError a
forall a b. a -> Either a b
Left (CdTextError -> Either CdTextError a)
-> (Text -> CdTextError) -> Text -> Either CdTextError a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextErrorType -> Text -> CdTextError
CdTextError CdTextErrorType
BadBinaryRead (Text -> IO (Either CdTextError a))
-> Text -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
"parseCdText"


-- | Try to use a specific language as the context for the given computation;
-- as this will frequently fail with 'LanguageNotFound' if given anything other
-- than 'Foreign.English', it is recommended that you provide a fallback with
-- 'A.<|>' or at least allow recovery with 'A.optional'.  Note that
-- 'Foreign.UnknownLanguage' will always fail.
withLanguage :: Foreign.Language -> CdText a -> CdText a
withLanguage :: Language -> CdText a -> CdText a
withLanguage Language
l (CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f) = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
l' Cdio
c -> IO (Either CdTextError a) -> IO (Either CdTextError a)
forall (m :: * -> *) a. (Monad m, LibcdioLogger m) => m a -> m a
isolateLogs (IO (Either CdTextError a) -> IO (Either CdTextError a))
-> IO (Either CdTextError a) -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ do
    Bool
b <- Cdio -> Language -> IO Bool
Foreign.selectLanguage Cdio
c Language
l
    Either CdTextError a
a <- if Bool
b
        then Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f (InitialLanguage -> Maybe InitialLanguage
forall a. a -> Maybe a
Just (InitialLanguage -> Maybe InitialLanguage)
-> InitialLanguage -> Maybe InitialLanguage
forall a b. (a -> b) -> a -> b
$ Language -> InitialLanguage
Lang Language
l) Cdio
c
        else Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> (Text -> Either CdTextError a)
-> Text
-> IO (Either CdTextError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextError -> Either CdTextError a
forall a b. a -> Either a b
Left (CdTextError -> Either CdTextError a)
-> (Text -> CdTextError) -> Text -> Either CdTextError a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextErrorType -> Text -> CdTextError
CdTextError (Language -> CdTextErrorType
LanguageNotFound Language
l) (Text -> IO (Either CdTextError a))
-> Text -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
"withLanguage"
    Maybe InitialLanguage -> Cdio -> IO ()
resetLanguage Maybe InitialLanguage
l' Cdio
c
    Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return Either CdTextError a
a

-- | Run the given computation over the data in a specific block.  Fails with
-- 'InvalidBlock' if the index can't be accessed.
withIndex :: Word -> CdText a -> CdText a
withIndex :: Word -> CdText a -> CdText a
withIndex Word
i CdText a
x = (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
 -> CdText a)
-> (Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
l Cdio
c -> do
    Either CdTextError a
a <- String -> Cdio -> CdText a -> Word -> IO (Either CdTextError a)
forall a.
String -> Cdio -> CdText a -> Word -> IO (Either CdTextError a)
withIndex' String
"withIndex" Cdio
c CdText a
x Word
i
    Maybe InitialLanguage -> Cdio -> IO ()
resetLanguage Maybe InitialLanguage
l Cdio
c
    Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return Either CdTextError a
a

-- | Underlying logic for index-based block selection.  Fails with
-- 'InvalidBlock' if the index can't be accessed.
withIndex'
    :: String
        -- ^ The function calling this, for error printing.
    -> Foreign.Cdio
        -- ^ The underlying disc session.
    -> CdText a
        -- ^ The computation to run.
    -> Word
        -- ^ The index to select.
    -> IO (Either CdTextError a)
withIndex' :: String -> Cdio -> CdText a -> Word -> IO (Either CdTextError a)
withIndex' String
s Cdio
c (CdText Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f) Word
i = do
    Bool
b <- Cdio -> Word -> IO Bool
Foreign.selectLanguageIndex Cdio
c Word
i
    if Bool
b
    then Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a)
f (InitialLanguage -> Maybe InitialLanguage
forall a. a -> Maybe a
Just (InitialLanguage -> Maybe InitialLanguage)
-> InitialLanguage -> Maybe InitialLanguage
forall a b. (a -> b) -> a -> b
$ Word -> InitialLanguage
Index Word
i) Cdio
c
    else Either CdTextError a -> IO (Either CdTextError a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError a -> IO (Either CdTextError a))
-> (Text -> Either CdTextError a)
-> Text
-> IO (Either CdTextError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextError -> Either CdTextError a
forall a b. a -> Either a b
Left (CdTextError -> Either CdTextError a)
-> (Text -> CdTextError) -> Text -> Either CdTextError a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CdTextErrorType -> Text -> CdTextError
CdTextError (Word -> CdTextErrorType
InvalidBlock Word
i) (Text -> IO (Either CdTextError a))
-> Text -> IO (Either CdTextError a)
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
s

-- | Run the given computation over /all/ of the accessible languages in the
-- CdText data.  When successful, the resulting list can be associated exactly
-- with the list of 'languages':
--
-- >>> ls <- 'languages'
-- >>> Right ls' <- 'cdText' $ 'withAll' 'language'
-- >>> ls == ls'
-- True
withAll :: CdText a -> CdText [Maybe a]
withAll :: CdText a -> CdText [Maybe a]
withAll CdText a
x = (Maybe InitialLanguage
 -> Cdio -> IO (Either CdTextError [Maybe a]))
-> CdText [Maybe a]
forall a.
(Maybe InitialLanguage -> Cdio -> IO (Either CdTextError a))
-> CdText a
CdText ((Maybe InitialLanguage
  -> Cdio -> IO (Either CdTextError [Maybe a]))
 -> CdText [Maybe a])
-> (Maybe InitialLanguage
    -> Cdio -> IO (Either CdTextError [Maybe a]))
-> CdText [Maybe a]
forall a b. (a -> b) -> a -> b
$ \Maybe InitialLanguage
l Cdio
c -> do
    [Maybe Language]
ls <- Cdio -> IO [Maybe Language]
Foreign.listAllLanguages Cdio
c
    let is :: [Maybe Word]
is = (Maybe Language -> Word -> Maybe Word)
-> [Maybe Language] -> [Word] -> [Maybe Word]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Maybe Language -> Word -> Maybe Word
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
($>) [Maybe Language]
ls [Word
0..]
    [Either CdTextError (Maybe a)]
as <- (Maybe Word -> IO (Either CdTextError (Maybe a)))
-> [Maybe Word] -> IO [Either CdTextError (Maybe a)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (Cdio -> Maybe Word -> IO (Either CdTextError (Maybe a))
withBlock Cdio
c) [Maybe Word]
is
    Maybe InitialLanguage -> Cdio -> IO ()
resetLanguage Maybe InitialLanguage
l Cdio
c
    Either CdTextError [Maybe a] -> IO (Either CdTextError [Maybe a])
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError [Maybe a] -> IO (Either CdTextError [Maybe a]))
-> Either CdTextError [Maybe a]
-> IO (Either CdTextError [Maybe a])
forall a b. (a -> b) -> a -> b
$ [Either CdTextError (Maybe a)] -> Either CdTextError [Maybe a]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence [Either CdTextError (Maybe a)]
as
  where withBlock :: Cdio -> Maybe Word -> IO (Either CdTextError (Maybe a))
withBlock Cdio
c (Just Word
i) = (a -> Maybe a)
-> Either CdTextError a -> Either CdTextError (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Maybe a
forall a. a -> Maybe a
Just (Either CdTextError a -> Either CdTextError (Maybe a))
-> IO (Either CdTextError a) -> IO (Either CdTextError (Maybe a))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Cdio -> CdText a -> Word -> IO (Either CdTextError a)
forall a.
String -> Cdio -> CdText a -> Word -> IO (Either CdTextError a)
withIndex' String
"withAll" Cdio
c CdText a
x Word
i
        withBlock Cdio
_ Maybe Word
Nothing = Either CdTextError (Maybe a) -> IO (Either CdTextError (Maybe a))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either CdTextError (Maybe a) -> IO (Either CdTextError (Maybe a)))
-> Either CdTextError (Maybe a)
-> IO (Either CdTextError (Maybe a))
forall a b. (a -> b) -> a -> b
$ Maybe a -> Either CdTextError (Maybe a)
forall a b. b -> Either a b
Right Maybe a
forall a. Maybe a
Nothing


-- | Get the raw binary data making up the CdText data, if any exists on the
-- disc.
cdTextRaw :: Cdio (Maybe BS.ByteString)
cdTextRaw :: Cdio (Maybe ByteString)
cdTextRaw = (Cdio -> IO (Maybe ByteString)) -> Cdio (Maybe ByteString)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio Cdio -> IO (Maybe ByteString)
Foreign.cdTextRaw


-- | Get the language in which any info is currently being retrieved.
language :: CdText Foreign.Language
language :: CdText Language
language = (Cdio -> IO Language) -> CdText Language
forall a. (Cdio -> IO a) -> CdText a
liftCdText ((Cdio -> IO Language) -> CdText Language)
-> (Cdio -> IO Language) -> CdText Language
forall a b. (a -> b) -> a -> b
$ (Maybe Language -> Language) -> IO (Maybe Language) -> IO Language
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Language -> Maybe Language -> Language
forall a. a -> Maybe a -> a
Y.fromMaybe Language
Foreign.UnknownLanguage) (IO (Maybe Language) -> IO Language)
-> (Cdio -> IO (Maybe Language)) -> Cdio -> IO Language
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Cdio -> IO (Maybe Language)
Foreign.language

-- | List every language with associated data in the CdText data associated
-- with the session.  Drops any 'Foreign.UnknownLanguage' values from the end
-- of the list.  For a version which operates within the 'CdText' monad, the
-- construction @'withAll' 'language'@ will have an identical effect.
--
-- /Before libcdio 2.1.0:  Silently drops any 'Nothing' or/
-- /@'Just' 'Foreign.UnknownLanguage'@ values, even in the middle of the list,/
-- /and only lists any single language a maximum of one time./
languages :: Cdio [Maybe Foreign.Language]
languages :: Cdio [Maybe Language]
languages = (Cdio -> IO [Maybe Language]) -> Cdio [Maybe Language]
forall a. (Cdio -> IO a) -> Cdio a
liftCdio Cdio -> IO [Maybe Language]
Foreign.listAllLanguages


-- | The earliest track with any associated metadata in the current language.
-- Note that this may differ from 'Sound.Libcdio.Track.minTrack' which gives
-- the first track on the disk, CDTEXT or not.
firstTrack :: CdText Track
firstTrack :: CdText Track
firstTrack = (Cdio -> IO Track) -> CdText Track
forall a. (Cdio -> IO a) -> CdText a
liftCdText ((Cdio -> IO Track) -> CdText Track)
-> (Cdio -> IO Track) -> CdText Track
forall a b. (a -> b) -> a -> b
$ (Maybe Track -> Track) -> IO (Maybe Track) -> IO Track
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Track -> Maybe Track -> Track
forall a. a -> Maybe a -> a
Y.fromMaybe Track
1) (IO (Maybe Track) -> IO Track)
-> (Cdio -> IO (Maybe Track)) -> Cdio -> IO Track
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Cdio -> IO (Maybe Track)
Foreign.firstTrack

-- | The final track with any associated metadata in the current language.
-- Note that this may differ from 'Sound.Libcdio.Track.maxTrack' which gives
-- the last track on the disc, CDTEXT or not.
lastTrack :: CdText Track
lastTrack :: CdText Track
lastTrack = (Cdio -> IO Track) -> CdText Track
forall a. (Cdio -> IO a) -> CdText a
liftCdText ((Cdio -> IO Track) -> CdText Track)
-> (Cdio -> IO Track) -> CdText Track
forall a b. (a -> b) -> a -> b
$ (Maybe Track -> Track) -> IO (Maybe Track) -> IO Track
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Track -> Maybe Track -> Track
forall a. a -> Maybe a -> a
Y.fromMaybe Track
1) (IO (Maybe Track) -> IO Track)
-> (Cdio -> IO (Maybe Track)) -> Cdio -> IO Track
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Cdio -> IO (Maybe Track)
Foreign.lastTrack


-- | Publisher-specific catalogue number, or some other context-specific
-- identifier.  Note that this may be different than @'code' $ 'info'
-- 'Nothing'@ which is (assumed to be) the unambiguous bar code unique to this
-- disc release.
discId :: CdText (Maybe T.Text)
discId :: CdText (Maybe Text)
discId = (Cdio -> IO (Maybe Text)) -> CdText (Maybe Text)
forall a. (Cdio -> IO a) -> CdText a
liftCdText ((Cdio -> IO (Maybe Text)) -> CdText (Maybe Text))
-> (Cdio -> IO (Maybe Text)) -> CdText (Maybe Text)
forall a b. (a -> b) -> a -> b
$ \Cdio
c -> (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> Text
T.pack (Maybe String -> Maybe Text)
-> IO (Maybe String) -> IO (Maybe Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c Field
Foreign.DiscId Maybe Track
forall a. Maybe a
Nothing

-- | The genre describing the music on this disc, and any associated
-- human-readable name or subgenre.
genre :: CdText (Maybe Foreign.Genre, Maybe T.Text)
genre :: CdText (Maybe Genre, Maybe Text)
genre = (Cdio -> IO (Maybe Genre, Maybe Text))
-> CdText (Maybe Genre, Maybe Text)
forall a. (Cdio -> IO a) -> CdText a
liftCdText ((Cdio -> IO (Maybe Genre, Maybe Text))
 -> CdText (Maybe Genre, Maybe Text))
-> (Cdio -> IO (Maybe Genre, Maybe Text))
-> CdText (Maybe Genre, Maybe Text)
forall a b. (a -> b) -> a -> b
$ \Cdio
c -> do
    Maybe Genre
g <- Cdio -> IO (Maybe Genre)
Foreign.genre Cdio
c
    Maybe String
s <- Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c Field
Foreign.GenreName Maybe Track
forall a. Maybe a
Nothing
    (Maybe Genre, Maybe Text) -> IO (Maybe Genre, Maybe Text)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Genre
g, String -> Text
T.pack (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
s)


-- | Retrieve the collection of data associated with a specific track, or if
-- 'Nothing', the disc itself.
info :: Maybe Track -> CdText Info
info :: Maybe Track -> CdText Info
info Maybe Track
t = (Cdio -> IO Info) -> CdText Info
forall a. (Cdio -> IO a) -> CdText a
liftCdText ((Cdio -> IO Info) -> CdText Info)
-> (Cdio -> IO Info) -> CdText Info
forall a b. (a -> b) -> a -> b
$ \Cdio
c -> do
    Maybe String
l <- Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c Field
Foreign.Title Maybe Track
t
    Maybe String
p <- Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c Field
Foreign.Performer Maybe Track
t
    Maybe String
s <- Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c Field
Foreign.Songwriter Maybe Track
t
    Maybe String
w <- Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c Field
Foreign.Composer Maybe Track
t
    Maybe String
a <- Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c Field
Foreign.Arranger Maybe Track
t
    Maybe String
m <- Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c Field
Foreign.Message Maybe Track
t
    Maybe String
o <- Cdio -> Field -> Maybe Track -> IO (Maybe String)
Foreign.cdTextGet Cdio
c (if Maybe Track -> Bool
forall a. Maybe a -> Bool
Y.isNothing Maybe Track
t then Field
Foreign.UpcEan else Field
Foreign.Isrc) Maybe Track
t
    Info -> IO Info
forall (m :: * -> *) a. Monad m => a -> m a
return (Info -> IO Info) -> Info -> IO Info
forall a b. (a -> b) -> a -> b
$ Info :: Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Info
Info
        { title :: Maybe Text
title = String -> Text
T.pack (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
l
        , performer :: Maybe Text
performer = String -> Text
T.pack (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
p
        , songwriter :: Maybe Text
songwriter = String -> Text
T.pack (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
s
        , composer :: Maybe Text
composer = String -> Text
T.pack (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
w
        , arranger :: Maybe Text
arranger = String -> Text
T.pack (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
a
        , message :: Maybe Text
message = String -> Text
T.pack (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
m
        , code :: Maybe Text
code = String -> Text
T.pack (String -> Text) -> Maybe String -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
o
        }