packages feed

bioinformatics-toolkit-0.1.0: src/Bio/Data/Fasta.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
--------------------------------------------------------------------------------
-- |
-- Module      :  $Header$
-- Copyright   :  (c) 2014 Kai Zhang
-- License     :  MIT

-- Maintainer  :  kai@kzhang.org
-- Stability   :  experimental
-- Portability :  portable

-- Functions for processing Fasta files
--------------------------------------------------------------------------------

module Bio.Data.Fasta
    ( FastaLike(..)
    , fastaReader
    ) where

import Bio.Motif
import Bio.Seq
import Control.Monad.State.Lazy
import qualified Data.ByteString.Char8 as B
import Data.Conduit
import qualified Data.Conduit.List as CL
import System.IO

class FastaLike f where
    fromFastaRecord :: ( B.ByteString    -- ^ record header
                       , [B.ByteString]  -- ^ record body
                       )
                    -> f

    readFasta :: FilePath -> Source IO f
    readFasta fl = mapOutput fromFastaRecord . fastaReader $ fl

    -- | non-stream version, read whole file in memory
    readFasta' :: FilePath -> IO [f]
    readFasta' fl = readFasta fl $$ CL.consume
--    writeFasta :: FilePath -> [f] -> IO ()
    {-# MINIMAL fromFastaRecord #-}

instance BioSeq s a => FastaLike (s a) where
    fromFastaRecord (_, xs) = fromBS . B.concat $ xs
    {-# INLINE fromFastaRecord #-}

instance FastaLike Motif where
    fromFastaRecord (name, mat) = Motif name (toPWM mat)
    {-# INLINE fromFastaRecord #-}

fastaReader :: FilePath -> Source IO (B.ByteString, [B.ByteString])
fastaReader fl = do handle <- liftIO $ openFile fl ReadMode
                    loop [] handle
  where
    loop :: [B.ByteString] -> Handle -> Source IO (B.ByteString, [B.ByteString])
    loop acc h = do 
        eof <- liftIO $ hIsEOF h
        if eof then liftIO (hClose h) >> output acc else do
            l <- liftIO $ B.hGetLine h
            case () of
                _ | B.null l -> loop acc h  -- empty line, go to next line
                  | B.head l == '>' -> output acc >> loop [B.tail l] h
                  | otherwise -> loop (acc ++ [l]) h
    output (x:xs) = yield (x, xs)
    output _ = return ()
{-# INLINE fastaReader #-}