packages feed

monatone-0.1.0.0: src/Monatone/Types.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}

module Monatone.Types
  ( ParseError(..)
  , Parser
  , readInt
  , readText
  , formatError  -- Export error formatter
  ) where

import Control.Monad.Except (ExceptT)
import Data.ByteString (ByteString)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import GHC.Generics (Generic)

-- | Parser errors with detailed context
-- 
-- These errors provide enough information for debugging without
-- exposing internal implementation details to end users.
data ParseError 
  = UnsupportedFormat Text        -- ^ File format not supported (with format details)
  | CorruptedFile Text            -- ^ File structure invalid (with corruption details)
  | IOError Text                  -- ^ IO operation failed (with system error)
  | PartialParse Text             -- ^ File partially parsed (common with corrupted MP3s)
  | InvalidEncoding Text          -- ^ Text encoding issues (common with old MP3 tags) 
  deriving (Show, Eq, Generic)

-- | Format error for user display
formatError :: ParseError -> Text
formatError (UnsupportedFormat detail) = "Unsupported format: " <> detail
formatError (CorruptedFile detail) = "Corrupted file: " <> detail  
formatError (IOError detail) = "IO error: " <> detail
formatError (PartialParse detail) = "Warning: " <> detail <> " (partial data recovered)"
formatError (InvalidEncoding detail) = "Encoding error: " <> detail

-- | Parser monad
type Parser = ExceptT ParseError IO

-- | Helper: Parse integer from text
readInt :: Text -> Maybe Int
readInt s = case reads (T.unpack s) of
  [(x, "")] -> Just x
  _ -> Nothing

-- | Helper: Parse text with UTF-8 decoding
readText :: ByteString -> Maybe Text
readText bs = case TE.decodeUtf8' bs of
  Left _ -> Nothing
  Right txt -> Just txt