mmtf (empty) → 0.1.0.0
raw patch · 11 files changed
+560/−0 lines, 11 filesdep +QuickCheckdep +basedep +binarysetup-changed
Dependencies added: QuickCheck, base, binary, bytestring, containers, data-msgpack, hspec, mmtf, text
Files
- LICENSE +30/−0
- README.md +0/−0
- Setup.hs +2/−0
- mmtf.cabal +47/−0
- src/Bio/MMTF.hs +14/−0
- src/Bio/MMTF/Decode.hs +107/−0
- src/Bio/MMTF/Decode/Codec.hs +148/−0
- src/Bio/MMTF/Decode/MessagePack.hs +61/−0
- src/Bio/MMTF/MessagePack.hs +20/−0
- src/Bio/MMTF/Type.hs +117/−0
- test/Spec.hs +14/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Pavel Yakovlev (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Pavel Yakovlev nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ mmtf.cabal view
@@ -0,0 +1,47 @@+name: mmtf+version: 0.1.0.0+synopsis: Macromolecular Transmission Format implementation+description: Haskell implementation of MMTF biological structure format.+homepage: https://github.com/zmactep/mmtf#readme+license: BSD3+license-file: LICENSE+author: Pavel Yakovlev+maintainer: pavel@yakovlev.me+copyright: (c) 2017, Pavel Yakovlev+category: Bioinformatics+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Bio.MMTF+ other-modules: Bio.MMTF.Type+ , Bio.MMTF.MessagePack+ , Bio.MMTF.Decode+ , Bio.MMTF.Decode.Codec+ , Bio.MMTF.Decode.MessagePack+ build-depends: base >= 4.8 && < 5+ , data-msgpack >= 0.0.9 && < 0.1+ , text >= 1.2.2.1 && < 1.3+ , bytestring >= 0.10.8.1 && < 0.11+ , binary >= 0.8.3.0 && < 0.9+ , containers >= 0.5.7.1 && < 0.6+ default-language: Haskell2010+ ghc-options: -Wall++test-suite mmtf-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: base+ , mmtf+ , QuickCheck >= 2.9.2 && < 2.10+ , hspec >= 2.4.1 && < 2.5+ , bytestring+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/zmactep/mmtf
+ src/Bio/MMTF.hs view
@@ -0,0 +1,14 @@+module Bio.MMTF+ ( module T+ , decode+ ) where++import Bio.MMTF.MessagePack ()+import Bio.MMTF.Type as T++import Data.ByteString.Lazy (ByteString)+import Data.MessagePack (unpack)++-- |Decodes a 'ByteString' to 'MMTF'+decode :: Monad m => ByteString -> m MMTF+decode = unpack
+ src/Bio/MMTF/Decode.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE OverloadedStrings #-}++module Bio.MMTF.Decode where++import Bio.MMTF.Decode.Codec+import Bio.MMTF.Decode.MessagePack+import Bio.MMTF.Type++import Control.Monad ((>=>))+import Data.Map.Strict (Map)+import Data.MessagePack (Object)+import Data.Text (Text)++-- |Parses format data from ObjectMap+formatData :: Monad m => Map Text Object -> m FormatData+formatData mp = do v <- atP mp "mmtfVersion" asStr+ p <- atP mp "mmtfProducer" asStr+ pure $ FormatData v p++-- |Parses model data from ObjectMap+modelData :: Monad m => Map Text Object -> m ModelData+modelData mp = atP mp "chainsPerModel" asIntList >>= return . ModelData++-- |Parses chain data from ObjectMap+chainData :: Monad m => Map Text Object -> m ChainData+chainData mp = do gpc <- atP mp "groupsPerChain" asIntList+ cil <- codec5 . parseBinary <$> atP mp "chainIdList" asBinary+ cnl <- (codec5 . parseBinary <$>) <$> atPM mp "chainNameList" asBinary+ pure $ ChainData gpc cil cnl++-- |Parses atom data from ObjectMap+atomData :: Monad m => Map Text Object -> m AtomData+atomData mp = do ail' <- (codec8 . parseBinary <$>) <$> atPM mp "atomIdList" asBinary+ all' <- (codec6 . parseBinary <$>) <$> atPM mp "altLocList" asBinary+ bfl' <- (codec10 . parseBinary <$>) <$> atPM mp "bFactorList" asBinary+ xcl' <- codec10 . parseBinary <$> atP mp "xCoordList" asBinary+ ycl' <- codec10 . parseBinary <$> atP mp "yCoordList" asBinary+ zcl' <- codec10 . parseBinary <$> atP mp "zCoordList" asBinary+ ol' <- (codec9 . parseBinary <$>) <$> atPM mp "occupancyList" asBinary+ pure $ AtomData ail' all' bfl' xcl' ycl' zcl' ol'++-- |Parses group data from ObjectMap+groupData :: Monad m => Map Text Object -> m GroupData+groupData mp = do gl' <- atP mp "groupList" asObjectList >>= sequence . map (transformObjectMap >=> groupType)+ gtl' <- codec4 . parseBinary <$> atP mp "groupTypeList" asBinary+ gil' <- codec8 . parseBinary <$> atP mp "groupIdList" asBinary+ ssl' <- (map ssDec . codec2 . parseBinary <$>) <$> atPM mp "secStructList" asBinary+ icl' <- (codec6 . parseBinary <$>) <$> atPM mp "insCodeList" asBinary+ sil' <- (codec8 . parseBinary <$>) <$> atPM mp "sequenceIndexList" asBinary+ pure $ GroupData gl' gtl' gil' ssl' icl' sil'++-- |Parses group type from ObjectMap+groupType :: Monad m => Map Text Object -> m GroupType+groupType mp = do fcl' <- atP mp "formalChargeList" asIntList+ anl' <- atP mp "atomNameList" asStrList+ el' <- atP mp "elementList" asStrList+ bal' <- atP mp "bondAtomList" asIntList+ bol' <- atP mp "bondOrderList" asIntList+ gn' <- atP mp "groupName" asStr+ slc' <- atP mp "singleLetterCode" asChar+ cct' <- atP mp "chemCompType" asStr+ pure $ GroupType fcl' anl' el' bal' bol' gn' slc' cct'++-- |Parses structure data from ObjectMap+structureData :: Monad m => Map Text Object -> m StructureData+structureData mp = do ttl' <- atPM mp "title" asStr+ sid' <- atPM mp "structureId" asStr+ dd' <- atPM mp "depositionDate" asStr+ rd' <- atPM mp "releaseDate" asStr+ nb' <- atP mp "numBonds" asInt+ na' <- atP mp "numAtoms" asInt+ ng' <- atP mp "numGroups" asInt+ nc' <- atP mp "numChains" asInt+ nm' <- atP mp "numModels" asInt+ sg' <- atPM mp "spaceGroup" asStr+ uc' <- (>>= ucDec) <$> atPM mp "unitCell" asFloatList+ nol' <- ((>>= asFloatList) <$>) <$> atPM mp "ncsOperatorList" asObjectList+ bal' <- (>>= sequence . map (transformObjectMap >=> bioAssembly)) <$> atPM mp "bioAssemblyList" asObjectList+ el' <- (>>= sequence . map (transformObjectMap >=> entity)) <$> atPM mp "entityList" asObjectList+ res' <- atPM mp "resolution" asFloat+ rf' <- atPM mp "rFree" asFloat+ rw' <- atPM mp "rWork" asFloat+ em' <- atPM mp "experimentalMethods" asStrList+ btl' <- (codec4 . parseBinary <$>) <$> atPM mp "bondAtomList" asBinary+ bol' <- (codec2 . parseBinary <$>) <$> atPM mp "bondOrderList" asBinary+ pure $ StructureData ttl' sid' dd' rd' nb' na' ng' nc' nm' sg' uc' nol'+ bal' el' res' rf' rw' em' btl' bol'++-- |Parses bio assembly data from ObjectMap+bioAssembly :: Monad m => Map Text Object -> m Assembly+bioAssembly mp = do nme' <- atP mp "name" asStr+ tlt' <- atP mp "transformList" asObjectList >>= sequence . map (transformObjectMap >=> transform)+ pure $ Assembly tlt' nme'++-- |Parses transform data from ObjectMap+transform :: Monad m => Map Text Object -> m Transform+transform mp = do cil' <- atP mp "chainIndexList" asIntList+ mtx' <- atP mp "matrix" asFloatList+ pure $ Transform cil' mtx'++-- |Parses entity data from ObjectMap+entity :: Monad m => Map Text Object -> m Entity+entity mp = do cil' <- atP mp "chainIndexList" asIntList+ dsc' <- atP mp "description" asStr+ tpe' <- atP mp "type" asStr+ sqc' <- atP mp "sequence" asStr+ pure $ Entity cil' dsc' tpe' sqc'
+ src/Bio/MMTF/Decode/Codec.hs view
@@ -0,0 +1,148 @@+module Bio.MMTF.Decode.Codec where++import Data.Binary (Binary, decode)+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as B (length, null, splitAt, unpack)+import Data.Char (chr)+import Data.Int (Int16, Int32, Int8)+import Data.List (mapAccumL)+import Data.Text (Text)+import qualified Data.Text as T (pack)++import Bio.MMTF.Type++codecCommon :: Binary a => (ByteString -> a) -> Int -> ByteString -> [a]+codecCommon f th bs | B.null bs = []+ | B.length bs < ith = error "Wrong number of bytes in bytestring"+ | otherwise = let (start, rest) = B.splitAt ith bs+ in f start : codecCommon f th rest+ where ith = fromIntegral th++data BinaryData = BD { binaryCodec :: Int32+ , binaryLength :: Int32+ , binaryParam :: Int32+ , binaryData :: ByteString+ }++-- |Parse useless header for binary data+parseBinary :: ByteString -> BinaryData+parseBinary bs = let (cdc, rest1) = B.splitAt 4 bs+ (lnh, rest2) = B.splitAt 4 rest1+ (prm, rest) = B.splitAt 4 rest2+ in BD (decode cdc) (decode lnh) (decode prm) rest++-- |Interpret bytes as array of 32-bit floating-point numbers.+codec1 :: BinaryData -> [Float]+codec1 = codecCommon decode 4 . binaryData++-- |Interpret bytes as array of 8-bit signed integers.+codec2 :: BinaryData -> [Int8]+codec2 = codecCommon decode 1 . binaryData++-- |Interpret bytes as array of 16-bit signed integers.+codec3 :: BinaryData -> [Int16]+codec3 = codecCommon decode 2 . binaryData++-- |Interpret bytes as array of 32-bit signed integers.+codec4 :: BinaryData -> [Int32]+codec4 = codecCommon decode 4 . binaryData++-- |Interpret bytes as array of 8-bit unsigned integers, then iteratively+-- consume length many bytes to form a string array.+codec5 :: BinaryData -> [Text]+codec5 bd = codecCommon decodeBytes (fromIntegral $ binaryParam bd) (binaryData bd)+ where decodeBytes :: ByteString -> Text+ decodeBytes bs = T.pack $ chr <$> filter (/=0) (fromIntegral <$> B.unpack bs)++-- |Interpret bytes as array of 32-bit signed integers, then run-length+-- decode into array of characters.+codec6 :: BinaryData -> [Char]+codec6 = map (chr . fromIntegral) . codec7++-- |Interpret bytes as array of 32-bit signed integers, then run-length+-- decode into array of 32-bit signed integers.+codec7 :: BinaryData -> [Int32]+codec7 = runLengthDec . codec4++-- |Interpret bytes as array of 32-bit signed integers, then run-length+-- decode into array of 32-bit signed integers, then delta decode into+-- array of 32-bit signed integers.+codec8 :: BinaryData -> [Int32]+codec8 = deltaDec . codec7++-- |Interpret bytes as array of 32-bit signed integers, then run-length+-- decode into array of 32-bit signed integers, then integer decode into+-- array of 32-bit floating-point numbers using the divisor parameter.+codec9 :: BinaryData -> [Float]+codec9 bd = integerDec (binaryParam bd) $ codec7 bd++-- |Interpret bytes as array of 16-bit signed integers, then unpack into+-- array of 32-bit integers, then delta decode into array of 32-bit+-- integers, then integer decode into array of 32-bit floating-point+-- numbers using the divisor parameter.+codec10 :: BinaryData -> [Float]+codec10 bd = integerDec (binaryParam bd) $ map fromIntegral $ deltaDec $ codec3 bd++-- |Interpret bytes as array of 16-bit signed integers, then integer+-- decode into array of 32-bit floating-point numbers using the divisor parameter.+codec11 :: BinaryData -> [Float]+codec11 bd = integerDec (binaryParam bd) $ map fromIntegral $ codec3 bd++-- |Interpret bytes as array of 16-bit signed integers, then unpack into+-- array of 32-bit signed integers, then integer decode into array+-- of 32-bit floating-point numbers using the divisor parameter.+codec12 :: BinaryData -> [Float]+codec12 bd = integerDec (binaryParam bd) $ recIndexDec $ codec3 bd++-- |Interpret array of bytes as array of 8-bit signed integers, then+-- unpack into array of 32-bit signed integers, then integer decode into+-- array of 32-bit floating-point numbers using the divisor parameter.+codec13 :: BinaryData -> [Float]+codec13 bd = integerDec (binaryParam bd) $ recIndexDec $ codec2 bd++-- |Interpret bytes as array of 16-bit signed integers, then unpack+-- into array of 32-bit signed integers.+codec14 :: BinaryData -> [Int32]+codec14 bd = recIndexDec $ codec3 bd++-- |Interpret bytes as array of 8-bit signed integers, then unpack+-- into array of 32-bit signed integers.+codec15 :: BinaryData -> [Int32]+codec15 bd = recIndexDec $ codec2 bd++-- Decodings++runLengthDec :: Integral a => [a] -> [a]+runLengthDec [] = []+runLengthDec [_] = error "List must have even length for run-length encoding"+runLengthDec (x:l:xs) = (replicate (fromIntegral l) x) ++ runLengthDec xs++deltaDec :: Num a => [a] -> [a]+deltaDec = snd . mapAccumL (\x y -> (x+y,x+y)) 0++recIndexDec :: (Integral a, Bounded a, Eq a) => [a] -> [Int32]+recIndexDec [] = []+recIndexDec xs = recIndexDecAcc 0 xs+ where recIndexDecAcc :: (Integral a, Bounded a) => Int32 -> [a] -> [Int32]+ recIndexDecAcc acc [] | acc /= 0 = [acc]+ | otherwise = []+ recIndexDecAcc acc (x:ys) | x > minBound && x < maxBound = fromIntegral x + acc : recIndexDecAcc 0 ys+ | otherwise = recIndexDecAcc (fromIntegral x + acc) ys++integerDec :: Integral a => a -> [a] -> [Float]+integerDec divisor = map (\x -> fromIntegral x / fromIntegral divisor)++ssDec :: Int8 -> SecondaryStructure+ssDec n | n == 0 = PiHelix+ | n == 1 = Bend+ | n == 2 = AlphaHelix+ | n == 3 = Extended+ | n == 4 = ThreeTenHelix+ | n == 5 = Bridge+ | n == 6 = Turn+ | n == 7 = Coil+ | otherwise = Undefined++ucDec :: Monad m => [Float] -> m UnitCell+ucDec [a,b,c,d,e,f] = pure $ UnitCell a b c d e f+ucDec _ = fail "Wrong list format for unit cell"
+ src/Bio/MMTF/Decode/MessagePack.hs view
@@ -0,0 +1,61 @@+module Bio.MMTF.Decode.MessagePack where++import Data.ByteString.Lazy (ByteString, fromStrict)+import Data.Map.Strict (Map, fromList)+import qualified Data.Map.Strict as M (lookup)+import Data.MessagePack+import Data.Text (Text)+import qualified Data.Text as T (unpack)++transformObjectMap :: Monad m => Object -> m (Map Text Object)+transformObjectMap (ObjectMap kv) = let mkPair :: Monad m => (Object, Object) -> m (Text, Object)+ mkPair ((ObjectStr txt), v) = pure (txt, v)+ mkPair _ = fail "Non-string key"+ in fromList <$> sequence (map mkPair kv)+transformObjectMap _ = fail "Wrong MessagePack MMTF format"++atP :: Monad m => Map Text Object -> Text -> (Object -> m a) -> m a+atP m k conv =+ case M.lookup k m of+ Just x -> conv x+ Nothing -> fail $ "Required field '" ++ uk ++ "' was not found"+ where uk = T.unpack k++atPM :: Monad m => Map Text Object -> Text -> (Object -> m a) -> m (Maybe a)+atPM m k conv = traverse conv $ M.lookup k m++asStr :: Monad m => Object -> m Text+asStr (ObjectStr s) = pure s+asStr _ = fail "Not a string data"++asChar :: Monad m => Object -> m Char+asChar = (head . T.unpack <$>) . asStr++asInt :: (Monad m, Integral a) => Object -> m a+asInt (ObjectInt i) = pure (fromIntegral i)+asInt (ObjectWord w) = pure (fromIntegral w)+asInt _ = fail "Not an int data"++asFloat :: Monad m => Object -> m Float+asFloat (ObjectFloat f) = pure f+asFloat _ = fail "Not a float data"++asIntList :: (Monad m, Integral a) => Object -> m [a]+asIntList (ObjectArray l) = sequence $ map asInt l+asIntList _ = fail "Not an array of ints data"++asStrList :: Monad m => Object -> m [Text]+asStrList (ObjectArray l) = sequence $ map asStr l+asStrList _ = fail "Not an array of string data"++asFloatList :: Monad m => Object -> m [Float]+asFloatList (ObjectArray l) = sequence $ map asFloat l+asFloatList _ = fail "Not an array of float data"++asObjectList :: Monad m => Object -> m [Object]+asObjectList (ObjectArray l) = pure l+asObjectList _ = fail "Not an array data"++asBinary :: Monad m => Object -> m ByteString+asBinary (ObjectBin bs) = pure (fromStrict bs)+asBinary _ = fail "Not a binary data"
+ src/Bio/MMTF/MessagePack.hs view
@@ -0,0 +1,20 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Bio.MMTF.MessagePack where++import Bio.MMTF.Decode+import Bio.MMTF.Decode.MessagePack+import Bio.MMTF.Type++import Data.MessagePack (MessagePack (..))++instance MessagePack MMTF where+ toObject = undefined -- TODO: add MMTF encoding+ fromObject obj = do mp <- transformObjectMap obj+ f <- formatData mp+ s <- structureData mp+ m <- modelData mp+ c <- chainData mp+ g <- groupData mp+ a <- atomData mp+ pure $ MMTF f s m c g a
+ src/Bio/MMTF/Type.hs view
@@ -0,0 +1,117 @@+module Bio.MMTF.Type where++import Data.Int (Int32, Int8)+import Data.Text (Text)++data UnitCell = UnitCell { ucA :: !Float+ , ucB :: !Float+ , ucC :: !Float+ , ucAlpha :: !Float+ , ucBeta :: !Float+ , ucGamma :: !Float+ }+ deriving (Show, Eq)++data Transform = Transform { chainIndexList :: ![Int32]+ , matrix :: ![Float]+ }+ deriving (Show, Eq)++data Assembly = Assembly { transformList :: ![Transform]+ , assemblyName :: !Text+ }+ deriving (Show, Eq)++data Entity = Entity { entityChainIndexList :: ![Int32]+ , entityDescription :: !Text+ , entityType :: !Text+ , entitySequence :: !Text+ }+ deriving (Show, Eq)++data GroupType = GroupType { gtFormalChargeList :: ![Int32]+ , gtAtomNameList :: ![Text]+ , gtElementList :: ![Text]+ , gtBondAtomList :: ![Int32]+ , gtBondOrderList :: ![Int32]+ , gtGroupName :: !Text+ , gtSingleLetterCode :: !Char+ , gtChemCompType :: !Text+ }+ deriving (Show, Eq)++data SecondaryStructure = PiHelix -- 0+ | Bend -- 1+ | AlphaHelix -- 2+ | Extended -- 3+ | ThreeTenHelix -- 4+ | Bridge -- 5+ | Turn -- 6+ | Coil -- 7+ | Undefined -- -1+ deriving (Show, Eq)++data FormatData = FormatData { mmtfVersion :: !Text+ , mmtfProducer :: !Text+ }+ deriving (Show, Eq)++data StructureData = StructureData { title :: !(Maybe Text)+ , structureId :: !(Maybe Text)+ , depositionDate :: !(Maybe Text)+ , releaseDate :: !(Maybe Text)+ , numBonds :: !Int32+ , numAtoms :: !Int32+ , numGroups :: !Int32+ , numChains :: !Int32+ , numModels :: !Int32+ , spaceGroup :: !(Maybe Text)+ , unitCell :: !(Maybe UnitCell)+ , ncsOperatorList :: !(Maybe [[Float]])+ , bioAssemblyList :: !(Maybe [Assembly])+ , entityList :: !(Maybe [Entity])+ , resolution :: !(Maybe Float)+ , rFree :: !(Maybe Float)+ , rWork :: !(Maybe Float)+ , experimentalMethods :: !(Maybe [Text])+ , bondAtomList :: !(Maybe [Int32]) -- binary (type 4)+ , bondOrderList :: !(Maybe [Int8]) -- binary (type 2)+ }+ deriving (Show, Eq)++data ModelData = ModelData { chainsPerModel :: ![Int32] }+ deriving (Show, Eq)++data ChainData = ChainData { groupsPerChain :: ![Int32]+ , chainIdList :: ![Text] -- binary (type 5, length 4)+ , chainNameList :: !(Maybe [Text]) -- binary (type 5, length 4)+ }+ deriving (Show, Eq)++data GroupData = GroupData { groupList :: ![GroupType]+ , groupTypeList :: ![Int32] -- binary (type 4)+ , groupIdList :: ![Int32] -- binary (type 8)+ , secStructList :: !(Maybe [SecondaryStructure]) -- binary (type 2)+ , insCodeList :: !(Maybe [Char]) -- binary (type 6)+ , sequenceIndexList :: !(Maybe [Int32]) -- binary (type 8)+ }+ deriving (Show, Eq)++data AtomData = AtomData { atomIdList :: !(Maybe [Int32]) -- binary (type 8)+ , altLocList :: !(Maybe [Char]) -- binary (type 6)+ , bFactorList :: !(Maybe [Float]) -- binary (type 10)+ , xCoordList :: ![Float] -- binary (type 10)+ , yCoordList :: ![Float] -- binary (type 10)+ , zCoordList :: ![Float] -- binary (type 10)+ , occupancyList :: !(Maybe [Float]) -- binary (type 9)+ }+ deriving (Show, Eq)++data MMTF = MMTF { format :: !FormatData+ , structure :: !StructureData+ , model :: !ModelData+ , chain :: !ChainData+ , group :: !GroupData+ , atom :: !AtomData+ }+ deriving (Show, Eq)
+ test/Spec.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE OverloadedStrings #-}++import Bio.MMTF+import qualified Data.ByteString.Lazy as B+import Test.Hspec++main :: IO ()+main = hspec $+ describe "MMTF" $+ it "should parse 1FSD" $ do+ contents <- B.readFile "resource/1FSD.mmtf"+ m <- decode contents+ let sid = (structureId . structure) m+ sid `shouldBe` Just "1FSD"