packages feed

BiobaseFasta 0.2.0.0 → 0.3.0.0

raw patch · 6 files changed

+248/−239 lines, 6 filesdep +string-conversionsdep −deepseqdep ~BiobaseTypesdep ~basedep ~lensPVP ok

version bump matches the API change (PVP)

Dependencies added: string-conversions

Dependencies removed: deepseq

Dependency ranges changed: BiobaseTypes, base, lens

API changes (from Hackage documentation)

- Biobase.Fasta.Export: breakByteString :: Int -> ByteString -> [ByteString]
- Biobase.Fasta.Export: fromIntToInt64 :: Int -> Int64
- Biobase.Fasta.Export: instance GHC.Show.Show Biobase.Fasta.Types.Fasta
- Biobase.Fasta.Export: prettyByteStringFasta :: Int -> Fasta -> ByteString
- Biobase.Fasta.Export: prettyPrintFasta :: Int -> Fasta -> String
- Biobase.Fasta.Export: writeFastaFile :: FilePath -> [Fasta] -> IO ()
- Biobase.Fasta.Streaming: Current :: ByteString -> Index 0 -> Current
- Biobase.Fasta.Streaming: Header :: ByteString -> Header
- Biobase.Fasta.Streaming: Overlap :: ByteString -> Overlap
- Biobase.Fasta.Streaming: [currentFasta] :: Current -> ByteString
- Biobase.Fasta.Streaming: [currentStart] :: Current -> Index 0
- Biobase.Fasta.Streaming: [getHeader] :: Header -> ByteString
- Biobase.Fasta.Streaming: [getOverlap] :: Overlap -> ByteString
- Biobase.Fasta.Streaming: [header] :: FindHeader -> !ByteString
- Biobase.Fasta.Streaming: data Current (which :: k)
- Biobase.Fasta.Streaming: eachFasta :: Monad m => Header which1 -> Overlap which2 -> Current which3 -> Stream (Of (ByteString, ByteString, ByteString)) m ()
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Classes.Eq (Biobase.Fasta.Streaming.Current which)
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Classes.Eq (Biobase.Fasta.Streaming.Header which)
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Classes.Eq (Biobase.Fasta.Streaming.Overlap which)
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Classes.Ord (Biobase.Fasta.Streaming.Current which)
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Classes.Ord (Biobase.Fasta.Streaming.Header which)
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Classes.Ord (Biobase.Fasta.Streaming.Overlap which)
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Show.Show (Biobase.Fasta.Streaming.Current which)
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Show.Show (Biobase.Fasta.Streaming.Header which)
- Biobase.Fasta.Streaming: instance forall k (which :: k). GHC.Show.Show (Biobase.Fasta.Streaming.Overlap which)
- Biobase.Fasta.Streaming: newtype Header (which :: k)
- Biobase.Fasta.Streaming: newtype Overlap (which :: k)
- Biobase.Fasta.Streaming: parseFasta :: ByteString -> [Fasta]
- Biobase.Fasta.Streaming: parseFastaFile :: FilePath -> IO [Fasta]
- Biobase.Fasta.Types: Fasta :: ByteString -> ByteString -> Fasta
- Biobase.Fasta.Types: LineInfo :: !Int -> !Int -> !Int -> !Int -> !Int -> LineInfo
- Biobase.Fasta.Types: RawFastaEntry :: ByteString -> RawFastaEntry
- Biobase.Fasta.Types: StreamFasta :: !ByteString -> !ByteString -> !LineInfo -> !ByteString -> StreamEvent
- Biobase.Fasta.Types: StreamHeader :: !ByteString -> !LineInfo -> StreamEvent
- Biobase.Fasta.Types: [_rawFastaEntry] :: RawFastaEntry -> ByteString
- Biobase.Fasta.Types: [fastaHeader] :: Fasta -> ByteString
- Biobase.Fasta.Types: [fastaSequence] :: Fasta -> ByteString
- Biobase.Fasta.Types: [firstCol] :: LineInfo -> !Int
- Biobase.Fasta.Types: [firstIndex] :: LineInfo -> !Int
- Biobase.Fasta.Types: [firstLine] :: LineInfo -> !Int
- Biobase.Fasta.Types: [lastCol] :: LineInfo -> !Int
- Biobase.Fasta.Types: [lastLine] :: LineInfo -> !Int
- Biobase.Fasta.Types: [prevStreamFasta] :: StreamEvent -> !ByteString
- Biobase.Fasta.Types: [streamFasta] :: StreamEvent -> !ByteString
- Biobase.Fasta.Types: [streamHeader] :: StreamEvent -> !ByteString
- Biobase.Fasta.Types: [streamLines] :: StreamEvent -> !LineInfo
- Biobase.Fasta.Types: data Fasta
- Biobase.Fasta.Types: data LineInfo
- Biobase.Fasta.Types: data StreamEvent
- Biobase.Fasta.Types: instance Control.DeepSeq.NFData Biobase.Fasta.Types.LineInfo
- Biobase.Fasta.Types: instance Control.DeepSeq.NFData Biobase.Fasta.Types.StreamEvent
- Biobase.Fasta.Types: instance GHC.Classes.Eq Biobase.Fasta.Types.Fasta
- Biobase.Fasta.Types: instance GHC.Classes.Eq Biobase.Fasta.Types.LineInfo
- Biobase.Fasta.Types: instance GHC.Classes.Eq Biobase.Fasta.Types.RawFastaEntry
- Biobase.Fasta.Types: instance GHC.Classes.Eq Biobase.Fasta.Types.StreamEvent
- Biobase.Fasta.Types: instance GHC.Classes.Ord Biobase.Fasta.Types.LineInfo
- Biobase.Fasta.Types: instance GHC.Classes.Ord Biobase.Fasta.Types.RawFastaEntry
- Biobase.Fasta.Types: instance GHC.Classes.Ord Biobase.Fasta.Types.StreamEvent
- Biobase.Fasta.Types: instance GHC.Generics.Generic Biobase.Fasta.Types.LineInfo
- Biobase.Fasta.Types: instance GHC.Generics.Generic Biobase.Fasta.Types.StreamEvent
- Biobase.Fasta.Types: instance GHC.Show.Show Biobase.Fasta.Types.LineInfo
- Biobase.Fasta.Types: instance GHC.Show.Show Biobase.Fasta.Types.RawFastaEntry
- Biobase.Fasta.Types: instance GHC.Show.Show Biobase.Fasta.Types.StreamEvent
- Biobase.Fasta.Types: newtype RawFastaEntry
+ Biobase.Fasta.Streaming: [fhHeader] :: FindHeader -> !ByteString
+ Biobase.Fasta.Streaming: fastaUid :: Lens' (SequenceIdentifier w) ByteString
+ Biobase.Fasta.Strict: Fasta :: !SequenceIdentifier which -> !BioSequence ty -> Fasta which ty
+ Biobase.Fasta.Strict: [_fasta] :: Fasta which ty -> !BioSequence ty
+ Biobase.Fasta.Strict: [_header] :: Fasta which ty -> !SequenceIdentifier which
+ Biobase.Fasta.Strict: byteStringToFasta :: ByteString -> Either String (Fasta which ty)
+ Biobase.Fasta.Strict: byteStringToMultiFasta :: ByteString -> [Fasta which ty]
+ Biobase.Fasta.Strict: convertString :: ConvertibleStrings a b => a -> b
+ Biobase.Fasta.Strict: data Fasta which ty
+ Biobase.Fasta.Strict: fasta :: forall which_anQa ty_anQb ty_apOX. Lens (Fasta which_anQa ty_anQb) (Fasta which_anQa ty_apOX) (BioSequence ty_anQb) (BioSequence ty_apOX)
+ Biobase.Fasta.Strict: fastaToBuilder :: Int -> Fasta which ty -> Builder
+ Biobase.Fasta.Strict: fastaToByteString :: Int -> Fasta which ty -> ByteString
+ Biobase.Fasta.Strict: header :: forall which_anQa ty_anQb which_apOY. Lens (Fasta which_anQa ty_anQb) (Fasta which_apOY ty_anQb) (SequenceIdentifier which_anQa) (SequenceIdentifier which_apOY)
+ Biobase.Fasta.Strict: instance forall k1 (which :: k1) k2 (ty :: k2). GHC.Classes.Eq (Biobase.Fasta.Strict.Fasta which ty)
+ Biobase.Fasta.Strict: instance forall k1 (which :: k1) k2 (ty :: k2). GHC.Classes.Ord (Biobase.Fasta.Strict.Fasta which ty)
+ Biobase.Fasta.Strict: instance forall k1 (which :: k1) k2 (ty :: k2). GHC.Generics.Generic (Biobase.Fasta.Strict.Fasta which ty)
+ Biobase.Fasta.Strict: instance forall k1 (which :: k1) k2 (ty :: k2). GHC.Read.Read (Biobase.Fasta.Strict.Fasta which ty)
+ Biobase.Fasta.Strict: instance forall k1 (which :: k1) k2 (ty :: k2). GHC.Show.Show (Biobase.Fasta.Strict.Fasta which ty)
+ Biobase.Fasta.Strict: rawFasta :: Int -> Prism' ByteString (Fasta which ty)
+ Biobase.Fasta.Strict: type FastaUntyped = Fasta Void Void
+ Biobase.Fasta.Strict: windowedFasta :: Lens' (BioSequenceWindow w ty k) (Fasta w ty)
- Biobase.Fasta.Streaming: streamingFasta :: forall m w r a. Monad m => HeaderSize -> OverlapSize -> CurrentSize -> (Header w -> Overlap w -> Current w -> Stream (Of a) m ()) -> ByteString m r -> Stream (Of a) m r
+ Biobase.Fasta.Streaming: streamingFasta :: forall m w ty k r a. Monad m => HeaderSize -> OverlapSize -> CurrentSize -> ByteString m r -> Stream (Of (BioSequenceWindow w ty k)) m r

Files

− Biobase/Fasta/Export.hs
@@ -1,38 +0,0 @@--- | Fasta export--module Biobase.Fasta.Export where-import Biobase.Fasta.Types-import qualified Data.ByteString.Lazy.Char8 as B-import Data.List-import GHC.Int--instance Show Fasta where-  show (Fasta _header _sequence) =-    (B.unpack _header) ++ "\n" ++ (B.unpack _sequence) ++ "\n"--prettyPrintFasta :: Int -> Fasta -> String-prettyPrintFasta number (Fasta _header _sequence) = (B.unpack _header) ++ "\n" ++ (B.unpack sequenceLines) ++ "\n"-  where sequenceSlices = breakByteString number _sequence-        sequenceLines = B.intercalate (B.pack "\n") sequenceSlices--prettyByteStringFasta :: Int -> Fasta -> B.ByteString-prettyByteStringFasta number (Fasta _header _sequence) = _header `B.append` bslinebreak `B.append` sequenceLines `B.append` bslinebreak-  where sequenceSlices = breakByteString number _sequence-        sequenceLines = B.intercalate (B.pack "\n") sequenceSlices-        bslinebreak = B.pack "\n"---breakByteString :: Int -> B.ByteString -> [B.ByteString]-breakByteString number bs-  | B.empty == currentLine = []-  | otherwise = currentLine:(breakByteString number rest)-  where (currentLine,rest) = B.splitAt (fromIntToInt64 number) bs--fromIntToInt64 :: Int -> Int64-fromIntToInt64 = fromIntegral--writeFastaFile :: FilePath -> [Fasta] -> IO ()-writeFastaFile filePath fastas = do-  let fastabs = map (prettyByteStringFasta 80) fastas-  let outputbs= B.concat fastabs-  B.writeFile filePath outputbs
Biobase/Fasta/Streaming.hs view
@@ -1,20 +1,15 @@+ -- | Streaming Fasta handling via the @streaming@ library. -- -- The functions in here should be streaming in constant memory. -- -- TODO Check if this is actually true with some unit tests. -{-# LANGUAGE UnicodeSyntax #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-- module Biobase.Fasta.Streaming   ( module Biobase.Fasta.Streaming   ) where +import           Control.Lens hiding (Index,Empty, mapped) import           Control.Monad import           Control.Monad.Trans.Resource (runResourceT, ResourceT(..), MonadResource) import           Data.ByteString.Streaming as BSS@@ -22,18 +17,20 @@ import           Data.ByteString.Streaming.Internal (ByteString(..)) import           Data.Semigroup as SG import           Debug.Trace+import           GHC.Generics import           GHC.TypeLits import           Prelude as P import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as B import qualified Streaming.Internal as SI import           Streaming as S import           Streaming.Prelude as SP-import qualified Data.List as L++import           Biobase.Types.BioSequence import           Biobase.Types.Index.Type-import           Biobase.Fasta.Types+import           Biobase.Types.Strand  + newtype HeaderSize = HeaderSize Int   deriving (Eq,Ord,Show) @@ -43,16 +40,15 @@ newtype CurrentSize = CurrentSize Int   deriving (Eq,Ord,Show) -newtype Header (which ∷ k) = Header { getHeader ∷ BS.ByteString }-  deriving (Eq,Ord,Show)+-- | lens into the unique id / first word of the header. -newtype Overlap (which ∷ k) = Overlap { getOverlap ∷ BS.ByteString }-  deriving (Eq,Ord,Show)+fastaUid ∷ Lens' (SequenceIdentifier w) BS.ByteString+fastaUid = lens getWord updateWord+  where getWord ((BS.words . _sequenceIdentifier) → ws) = case ws of (x:_) → BS.drop 1 x; [] → BS.empty+        updateWord (SequenceIdentifier hdr) w = SequenceIdentifier . BS.unwords $ BS.cons '>' w : tail (BS.words hdr)+{-# Inlinable fastaUid #-} --- | Current Fasta window, together with the start index (0-based). -data Current (which ∷ k) = Current { currentFasta ∷ BS.ByteString, currentStart ∷ Index 0 }-  deriving (Eq,Ord,Show)  -- | Fully stream a fasta file, making sure to never exceed a constant amount -- of memory. The @go@ function yields values of type @a@ down the line for@@ -64,7 +60,7 @@ -- @  streamingFasta-  ∷ forall m w r a+  ∷ forall m w ty k r a   . ( Monad m )   ⇒ HeaderSize   -- ^ Maximal length of the header. Ok to set to @20 000@, only guards against@@ -75,15 +71,12 @@   -- todo at 'overlappedFasta')   → CurrentSize   -- ^ The size of each window to be processed.-  → (Header w → Overlap w → Current w → Stream (Of a) m ())-  -- ^ The processing function. Takes in the header, any overlap from the-  -- previous window, the current window and produces a stream of @a@s.   → ByteString m r   -- ^ A streaming bytestring of Fasta files.-  → Stream (Of a) m r-  -- ^ The outgoing stream of @a@s being processed.+  → Stream (Of (BioSequenceWindow w ty k)) m r+  -- ^ The outgoing stream of @Current@ windows being processed. {-# Inlinable streamingFasta #-}-streamingFasta (HeaderSize hSz) (OverlapSize oSz) (CurrentSize cSz) f = go (FindHeader [] 0) where+streamingFasta (HeaderSize hSz) (OverlapSize oSz) (CurrentSize cSz) = go (FindHeader [] 0) where   -- Find the next FASTA header   go (FindHeader hdr cnt) = \case     -- No more data to be had. If There is some part of a header, we will run@@ -92,8 +85,8 @@     Empty retVal → do       -- handle case of last empty fasta       unless (P.null hdr) $ do-        let thisHeader = BS.take hSz $ BS.concat $ P.reverse hdr-        f (Header thisHeader) (Overlap BS.empty) (Current BS.empty 0)+        let thisHeader = BS.take hSz . BS.drop 1 . BS.concat $ P.reverse hdr+        yield $ seqWindow thisHeader BS.empty BS.empty 0       SI.Return retVal     -- Effects are wrapped up into a 'Stream' effect.     Go m → SI.Effect $ liftM (go (FindHeader hdr cnt)) m@@ -112,7 +105,7 @@       -- We have found a newline at @k@. Prepare the full header (up to @hSz@       -- size) and hand over to @HasHeader@ which processes actual fasta       -- payload.-      | Just k  ← mk → let thisHeader = BS.take hSz $ BS.concat $ P.reverse $ BS.take k b:hdr+      | Just k  ← mk → let thisHeader = BS.take hSz . BS.drop 1 . BS.concat . P.reverse $ BS.take k b:hdr                        in  go (HasHeader thisHeader BS.empty [] 0 0)                               (Chunk (BS.drop (k+1) b) bytestream)       where b = if P.null hdr then BS.dropWhile (\c → c/='>' && c/=';') rawBS else rawBS@@ -121,7 +114,7 @@   go hasHeader@(HasHeader hdr overlap cs cnt entries) = \case     -- No more data, process final input and return.     Empty retVal → do-      when (cnt>0 || entries==0) $ f (Header hdr) (Overlap BS.empty) (Current (BS.concat $ reverse cs) 0)+      when (cnt>0 || entries==0) . yield $ seqWindow hdr BS.empty (BS.concat $ reverse cs) 0       SI.Return retVal     -- Effects to be dealt with.     Go m → SI.Effect $ liftM (go hasHeader) m@@ -135,7 +128,7 @@       Nothing → let (this,next) = BS.splitAt (cSz-cnt) $ BS.filter (/= '\n') b                 in  if BS.length this + cnt >= cSz                     then do let thisFasta = BS.concat $ reverse $ this:cs-                            f (Header hdr) (Overlap overlap) (Current thisFasta 0)+                            yield $ seqWindow hdr overlap thisFasta entries                             go (HasHeader hdr (BS.drop (BS.length thisFasta - oSz) thisFasta) [] 0 (entries+1))                                (if BS.null next then bytestream else Chunk next bytestream)                     else go (HasHeader hdr overlap (this:cs) (BS.length this + cnt) entries)@@ -150,10 +143,19 @@         | otherwise → do let thisFasta = BS.concat $ reverse cs                          -- we only emit on empty @thisFasta@, if there is                          -- data, or it is the only (then empty) entry.-                         when (cnt>0 || entries==0) $ f (Header hdr) (Overlap overlap) (Current thisFasta 0)+                         when (cnt>0 || entries==0) . yield $ seqWindow hdr overlap thisFasta entries                          go (FindHeader [] 0) $ Chunk b bytestream   -- Returns the first index (if any) of a new fasta entry symbol.   newFastaIndex b = getMin <$> (Min <$> BS.elemIndex '>' b) SG.<> (Min <$> BS.elemIndex ';' b)+  -- build up a seq-window+  seqWindow hdr pfx seq entries = BioSequenceWindow+    { _bswIdentifier = SequenceIdentifier hdr+    , _bswPrefix = BioSequence pfx+    , _bswSequence = BioSequence seq+    , _bswSuffix = BioSequence BS.empty+    , _bswStrand = PlusStrand+    , _bswIndex = Index $ entries * cSz+    }  -- | Control structure for 'streamingFasta'. @@ -165,7 +167,7 @@       -- ^ accumulated header length       }   | HasHeader-      { header ∷ !BS.ByteString+      { fhHeader ∷ !BS.ByteString       -- ^ the (size-truncated) header for this fasta file       , dataOverlap ∷ !BS.ByteString       -- ^ overlap (if any) from earlier parts of the fasta file@@ -177,7 +179,6 @@       -- ^ count how many entries we have seen       } - {- t0 = P.unlines   [ ">Aaaa"@@ -189,40 +190,30 @@   ]  -r2 = splitFastaLines $ S8.lines $ S8.fromStrict $ BS.pack t0--r3 = streamFastaLines $ S8.lines $ S8.fromStrict $ BS.pack t0---- r3' ∷ Stream (Stream (Of BS.ByteString) Identity) Identity ()-r3' = toList . mapped toList $ maps (mapped toStrict) r3--r4 = toList . streamingFasta (HeaderSize 2) (OverlapSize 1) (CurrentSize 2) go . S8.fromStrict $ BS.pack t0-  where go (Header h) (Overlap o) (Current c) = yield (h,o,c)+r4 = toList . streamingFasta (HeaderSize 2) (OverlapSize 1) (CurrentSize 2) . S8.fromStrict $ BS.pack t0 -} ---eachFasta :: forall (m0 :: * -> *).  Header Int -> Overlap Int -> Current Int -> Stream (Of (BS.ByteString, BS.ByteString, BS.ByteString)) (ResourceT IO) ()-eachFasta (Header h) (Overlap o) (Current c p) = SP.yield (h,o,c)----readFastaFile ∷ FilePath → IO () -- [(BS.ByteString,BS.ByteString,BS.ByteString)]---readFastaFile f = do---  let s = 1000000000000---  r ← runResourceT---          $ SP.mapM_ (liftIO . P.print)---          $ streamingFasta (HeaderSize s) (OverlapSize 0) (CurrentSize s) eachFasta---          $ S8.readFile f---  return r+{-+--eachFasta (Header h) (Overlap o) (Current c p) = SP.yield (h,o,c)+eachFasta (Header h) (Overlap o) (Current c p) = SP.yield (BS.length h, BS.length o, BS.length c) -parseFastaFile ∷ FilePath → IO [Fasta]-parseFastaFile f = do+--readFastaFile ∷ FilePath → IO [(BS.ByteString,BS.ByteString,BS.ByteString)]+readFastaFile f = do   let s = 1000000000000   r ← runResourceT-          $ toList_+          $ SP.mapM_ (liftIO . P.print)           $ streamingFasta (HeaderSize s) (OverlapSize 0) (CurrentSize s) eachFasta           $ S8.readFile f-  let fastas = L.map (\(a,_,c) -> Fasta (B.fromStrict a) (B.fromStrict c)) r-  return fastas+  return r+-} -parseFasta ∷ B.ByteString → [Fasta]-parseFasta input = L.map (\(a,_,c) -> Fasta (B.fromStrict a) (B.fromStrict c)) (L.head r)-    where s = 1000000000000-          r = toList_ $ streamingFasta (HeaderSize s) (OverlapSize 0) (CurrentSize s) eachFasta $ BSS.fromLazy input+{-+readFastaFile f = do+  let s = 1000000000000+  r ← runResourceT+          $ SP.mapM_ (liftIO . P.print)+          $ SP.mapped S8.toStrict+          $ S8.split '>'+          $ S8.readFile f+  return r+-}
+ Biobase/Fasta/Strict.hs view
@@ -0,0 +1,103 @@++-- | A convenience module for *small* @Fasta@ entries, that are completely in+-- memory and *not* to be streamed.+--+-- The @Data.ByteString.Strict.Lens@ module is very helpful for further+-- handling of 'Fasta' entries.+--+-- For convenience, the 'convertString' function from @string-conversions@ is+-- supplied.++module Biobase.Fasta.Strict+  ( module Biobase.Fasta.Strict+  , convertString+  ) where++import           Control.Lens+import           Data.Bifunctor (first)+import           Data.ByteString (ByteString)+import           Data.String.Conversions+import           Data.Void+import           GHC.Generics (Generic)+import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Streaming as BSS+import qualified Streaming.Prelude as SP++import           Biobase.Fasta.Streaming as FS+import           Biobase.Types.BioSequence++++-- | A *strict* @Fasta@ entry.++data Fasta which ty = Fasta+  { _header ∷ !(SequenceIdentifier which)+  , _fasta  ∷ !(BioSequence ty)+  }+  deriving (Eq,Ord,Read,Show,Generic)+makeLenses ''Fasta++-- | If you don't want to deal with the phantom types.++type FastaUntyped = Fasta Void Void++-- | Render a 'Fasta' entry to a 'ByteString'. Will end with a final @\n@ in+-- any case.++fastaToByteString ∷ Int → Fasta which ty → ByteString+{-# Inlinable fastaToByteString #-}+fastaToByteString k' Fasta{..} = BS.cons '>' (_header^._Wrapped) <> "\n" <> go (_fasta^._Wrapped)+  where go (BS.splitAt k → (hd,tl))+          | BS.null hd = mempty+          | otherwise  = hd <> "\n" <> go tl+        k = max 1 k'++-- | Render a 'Fasta' entry to a 'Builder'. Will end with a final @\n@ in+-- any case.++fastaToBuilder ∷ Int → Fasta which ty → BB.Builder+{-# Inlinable fastaToBuilder #-}+fastaToBuilder k' Fasta{..} = BB.char8 '>' <> (BB.byteString $ _header^._Wrapped) <> BB.char8 '\n' <> go (_fasta^._Wrapped)+  where go (BS.splitAt k → (hd,tl))+          | BS.null hd = mempty+          | otherwise  = BB.byteString hd <> BB.char8 '\n' <> go tl+        k = max 1 k'++-- | Try to parse a 'ByteString' as a 'Fasta', failing with 'Left', succees+-- with 'Right'.++byteStringToFasta ∷ ByteString → Either String (Fasta which ty)+{-# Inlinable byteStringToFasta #-}+byteStringToFasta (BS.lines → ls)+  | null ls = Left "empty bytestring"+  | Just (z, hdr) ← BS.uncons h, z `BS.elem` ">;" = Right $ Fasta { _header = SequenceIdentifier hdr, _fasta = BioSequence $ BS.concat ts }+  | otherwise = Left "no '>'/';' first character"+  where h:ts = ls++-- | Try to parse a 'ByteString' as multiple 'Fasta' entries. Even though this+-- is using the underlying streaming interface, this is not streaming.++byteStringToMultiFasta+  ∷ BSL.ByteString → [Fasta which ty]+{-# Inlinable byteStringToMultiFasta #-}+byteStringToMultiFasta bsl = map (view windowedFasta) $ runIdentity bss+  where bss = SP.toList_ . streamingFasta (HeaderSize maxBound) (OverlapSize 0) (CurrentSize maxBound) $ BSS.fromLazy bsl++-- | A lens that goes from a 'BioSequenceWindow' to a 'Fasta'.++windowedFasta ∷ Lens' (BioSequenceWindow w ty k) (Fasta w ty)+{-# Inline windowedFasta #-}+windowedFasta = lens lr rl+  where lr bsw = Fasta { _header = bsw^.bswIdentifier, _fasta = bsw^.bswSequence }+        rl bsw f = set bswSequence (f^.fasta) $ set bswIdentifier (f^.header) bsw++-- | A prism from a 'ByteString' to a 'Fasta'. Note that this will only be an+-- identity if the underlying fasta file is rendered with @k@ characters per+-- line.++rawFasta ∷ Int → Prism' ByteString (Fasta which ty)+{-# Inline rawFasta #-}+rawFasta k = prism (fastaToByteString k) $ \bs → first (const bs) $ byteStringToFasta bs+
− Biobase/Fasta/Types.hs
@@ -1,56 +0,0 @@-{-# Language DeriveGeneric #-}--module Biobase.Fasta.Types where--import Control.DeepSeq-import Control.Lens-import Data.ByteString.Char8 (ByteString)-import qualified Data.ByteString.Lazy.Char8 as B-import Data.Data-import GHC.Generics-import Biobase.Types.NucleotideSequence-import Biobase.Types.AminoAcidSequence---- |--data Fasta = Fasta { fastaHeader :: B.ByteString, fastaSequence :: B.ByteString }-  deriving (Eq)--newtype RawFastaEntry = RawFastaEntry { _rawFastaEntry :: ByteString }-  deriving (Show,Eq,Ord,Typeable)----makeLenses ''RawFastaEntry---- | 'StreamEvent's are chunked pieces of data, where the raw data is--- a strict @ByteString@. Each element also retains information on the--- first and last line and column (via 'streamLines') that are part of this--- chunk.--data StreamEvent-  -- | A Header event, multiple header events signal that the header name-  -- was longer than the chunk size.-  = StreamHeader  { streamHeader  :: !ByteString, streamLines :: !LineInfo }-  -- | A data event. We keep a pointer to the previous chunk (which is-  -- useful for some algorithms). The chunk is free of newlines!-  | StreamFasta   { streamFasta   :: !ByteString, prevStreamFasta :: !ByteString, streamLines :: !LineInfo, streamHeader :: !ByteString }-  deriving (Show,Eq,Ord,Typeable,Generic)--instance NFData StreamEvent------ | Complete information on line and column start and end for a chunk.------ TODO This is a 1-based format? Lets use the BiobaseTypes facilities!--data LineInfo = LineInfo-  { firstLine   :: !Int   -- ^ first line for this chunk @(lines in complete file!)@-  , firstCol    :: !Int   -- ^ first column in first line for this chunk-  , lastLine    :: !Int   -- ^ last line for this chunk @(lines in complete file!)@-  , lastCol     :: !Int   -- ^ last column in last line for this chunk-  , firstIndex  :: !Int   -- ^ first index in this fasta block. Counts just the number of symbols in the @Fasta@ payload.-  }-  deriving (Show,Eq,Ord,Typeable,Generic)--instance NFData LineInfo-
BiobaseFasta.cabal view
@@ -1,17 +1,17 @@+cabal-version:  2.2 name:           BiobaseFasta-version:        0.2.0.0-author:         Christian Hoener zu Siederdissen, Florian Eggenhofer+version:        0.3.0.0+author:         Christian Hoener zu Siederdissen maintainer:     choener@bioinf.uni-leipzig.de homepage:       https://github.com/choener/BiobaseFasta bug-reports:    https://github.com/choener/BiobaseFasta/issues-copyright:      Christian Hoener zu Siederdissen, 2011-2018+copyright:      Christian Hoener zu Siederdissen, 2011-2019 category:       Bioinformatics-license:        GPL-3+license:        BSD-3-Clause license-file:   LICENSE build-type:     Simple stability:      experimental-cabal-version:  >= 1.10.0-tested-with:    GHC == 8.4.3+tested-with:    GHC == 8.4.4 synopsis:       streaming FASTA parser description:                 Stream-based handling of FASTA files. The user selects a window@@ -19,9 +19,6 @@                 previous (past) window is available, in case some data sits on                 the boundary between windows.                 .-                FastaTool is a simple tool providing information on FASTA-                files, and allowing to extract sequences and subsequences.-                .                 Greg Schwartz' <http://hackage.haskell.org/package/fasta>                 package is a lot more complete. This one is mostly tailored to                 my usage requirements (and may at some point use his library).@@ -37,20 +34,16 @@   -library+common deps   build-depends: base                 >= 4.7    && < 5.0                , bytestring+               , lens                 >= 4.0                , resourcet            >= 1.0                , streaming            >= 0.1                , streaming-bytestring >= 0.1-               , lens-               , deepseq+               , string-conversions   >= 0.4                ---               , BiobaseTypes         == 0.1.4.*-  exposed-modules:-    Biobase.Fasta.Streaming,-    Biobase.Fasta.Types,-    Biobase.Fasta.Export+               , BiobaseTypes         == 0.2.0.*   default-language:     Haskell2010   default-extensions: BangPatterns@@ -58,24 +51,39 @@                     , DeriveDataTypeable                     , DeriveGeneric                     , FlexibleContexts+                    , FlexibleInstances                     , GADTs+                    , GeneralizedNewtypeDeriving                     , KindSignatures                     , LambdaCase+                    , MultiParamTypeClasses                     , MultiWayIf                     , NoMonomorphismRestriction+                    , OverloadedStrings                     , PolyKinds                     , RankNTypes                     , RecordWildCards                     , ScopedTypeVariables                     , TemplateHaskell+                    , TypeApplications+                    , TypeFamilies                     , UnicodeSyntax                     , ViewPatterns   ghc-options:     -O2 +library+  import:+    deps+  exposed-modules:+    Biobase.Fasta.Streaming+    Biobase.Fasta.Strict  + test-suite properties+  import:+    deps   type:     exitcode-stdio-1.0   main-is:@@ -84,19 +92,8 @@     -threaded -rtsopts -with-rtsopts=-N   hs-source-dirs:     tests-  default-language:-    Haskell2010-  default-extensions: TemplateHaskell-                    , UnicodeSyntax-                    , OverloadedStrings-		    -  build-depends: base-               , QuickCheck-               , bytestring+  build-depends: QuickCheck                , filepath-               , resourcet              >= 1.0-               , streaming              >= 0.1-               , streaming-bytestring   >= 0.1                , tasty                  >= 0.11                , tasty-hunit            >= 0.9                , tasty-golden           >= 2.3
tests/properties.hs view
@@ -1,63 +1,69 @@  module Main where -import           Prelude as P-import           Data.Functor.Of-import qualified Data.ByteString.Char8 as BS+import           Control.Lens (view)+import           Control.Monad.Trans.Resource (runResourceT, ResourceT(..), MonadResource) import           Data.ByteString.Streaming as BSS import           Data.ByteString.Streaming.Char8 as S8 import           Data.ByteString.Streaming.Internal (ByteString(..))+import           Data.Functor.Of+import           Data.Void+import           Prelude as P+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as BL8+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Test.Tasty.Golden as Golden import           Streaming as S import           Streaming.Prelude as SP+import           System.FilePath (takeBaseName, replaceExtension) import           Test.Tasty import           Test.Tasty.HUnit import           Test.Tasty.QuickCheck as QC---import           Test.Tasty.Silver as S---import           Test.Tasty.Silver.Interactive as SI import           Test.Tasty.TH-import           Control.Monad.Trans.Resource (runResourceT, ResourceT(..), MonadResource)-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified Test.Tasty.Golden as Golden-import           System.FilePath (takeBaseName, replaceExtension)-import qualified Data.ByteString.Lazy.Char8 as BL8 +import           Biobase.Types.BioSequence+import           Biobase.Types.Strand+ import           Biobase.Fasta.Streaming+import           Biobase.Fasta.Strict   --- * golden tests--readFastaFile ∷ FilePath → IO [(BS.ByteString,BS.ByteString,BS.ByteString)]-readFastaFile f = do-  let s = 1000000-  xs :> r ← runResourceT-          $ SP.toList-          $ streamingFasta (HeaderSize s) (OverlapSize 0) (CurrentSize s) eachFasta-          $ S8.readFile f-  return xs--lazyFastaOut = BL8.concat . P.map go-  where go (h,o,c) = BL8.concat-          [ BL8.fromStrict h-          , BL8.pack "\n"-          , BL8.fromStrict o-          , BL8.pack "\n"-          , BL8.fromStrict c-          , BL8.pack "\n"-          ]--goldenTests ∷ IO TestTree-goldenTests = do-  fastaFiles ← Golden.findByExtension [".fa"] "./tests/"-  return $ testGroup "readFastaFile golden tests"-    [ Golden.goldenVsString-        (takeBaseName fastaFile) -- test name-        goldenFile -- golden file path-        (lazyFastaOut <$> readFastaFile fastaFile) -- action whose result is tested-    | fastaFile <- fastaFiles-    , let goldenFile = replaceExtension fastaFile ".fa-golden"-    ]+-- -- * golden tests+-- +-- eachFasta (HeaderSize h) (OverlapSize o) (CurrentSize c p) = SP.yield (h,o,c)+-- +-- readFastaFile ∷ FilePath → IO [(BS.ByteString,BS.ByteString,BS.ByteString)]+-- readFastaFile f = do+--   let s = 1000000+--   xs :> r ← runResourceT+--           $ SP.toList+--           $ streamingFasta (HeaderSize s) (OverlapSize 0) (CurrentSize s) eachFasta+--           $ S8.readFile f+--   return xs+-- +-- lazyFastaOut = BL8.concat . P.map go+--   where go (h,o,c) = BL8.concat+--           [ BL8.fromStrict h+--           , BL8.pack "\n"+--           , BL8.fromStrict o+--           , BL8.pack "\n"+--           , BL8.fromStrict c+--           , BL8.pack "\n"+--           ]+-- +-- goldenTests ∷ IO TestTree+-- goldenTests = do+--   fastaFiles ← Golden.findByExtension [".fa"] "./tests/"+--   return $ testGroup "readFastaFile golden tests"+--     [ Golden.goldenVsString+--         (takeBaseName fastaFile) -- test name+--         goldenFile -- golden file path+--         (lazyFastaOut <$> readFastaFile fastaFile) -- action whose result is tested+--     | fastaFile <- fastaFiles+--     , let goldenFile = replaceExtension fastaFile ".fa-golden"+--     ]  -- * unit tests @@ -70,28 +76,34 @@   , "890"   ] -smallTest ∷ Int → Int → Int → Of [(BS.ByteString,BS.ByteString,BS.ByteString)] ()+smallTest ∷ Int → Int → Int → Of [BioSequenceWindow Void Void 1] () smallTest h o c = runIdentity          . toList-         . streamingFasta (HeaderSize h) (OverlapSize o) (CurrentSize c) go+--         . SP.map (view windowedFasta)+         . streamingFasta (HeaderSize h) (OverlapSize o) (CurrentSize c)          . S8.fromStrict          $ BS.pack smallInlineFasta-  where go (Header h) (Overlap o) (Current c _) = yield (h,o,c)+  where go (HeaderSize h) (OverlapSize o) (CurrentSize c) = yield (h,o,c)  smallTest333 = testCase "3/3/3" $ do   let res :> r = smallTest 3 3 3   assertEqual "return is null" () r   assertEqual "length is 4" 4 (P.length res)-  assertEqual "!!0" (">Aa","","123") (res!!0)-  assertEqual "!!1" (">Bb","","456") (res!!1)-  assertEqual "!!2" (">Bb","456","7") (res!!2)-  assertEqual "!!3" (">Cc","","890") (res!!3)+  assertEqual "!!0" (BioSequenceWindow "Aaa" ""    "123" "" PlusStrand 1) (res!!0)+  assertEqual "!!1" (BioSequenceWindow "Bbb" ""    "456" "" PlusStrand 1) (res!!1)+  assertEqual "!!2" (BioSequenceWindow "Bbb" "456" "7"   "" PlusStrand 4) (res!!2)+  assertEqual "!!3" (BioSequenceWindow "Ccc" ""    "890" "" PlusStrand 1) (res!!3)+  --+  assertEqual "!!0/Fasta" (Fasta "Aaa" "123") (view windowedFasta $ res!!0)+  assertEqual "!!1/Fasta" (Fasta "Bbb" "456") (view windowedFasta $ res!!1)+  assertEqual "!!2/Fasta" (Fasta "Bbb" "7"  ) (view windowedFasta $ res!!2)+  assertEqual "!!3/Fasta" (Fasta "Ccc" "890") (view windowedFasta $ res!!3)  main :: IO () main = do-  gs ← goldenTests-  defaultMain $ testGroup "all tests"-    [ testGroup "Golden" [gs]-    , testGroup "unit tests" [smallTest333]-    ]+--   gs ← goldenTests+   defaultMain $ testGroup "all tests"+--     [ testGroup "Golden" [gs]+     [ testGroup "unit tests" [smallTest333]+     ]