ofx 0.2.0.0 → 0.4.0.0
raw patch · 2 files changed
+65/−78 lines, 2 filesdep −explicit-exception
Dependencies removed: explicit-exception
Files
- Data/OFX.hs +64/−38
- ofx.cabal +1/−40
Data/OFX.hs view
@@ -55,8 +55,11 @@ -- module Data.OFX- ( -- * The OFX data tree- HeaderTag+ ( -- * Error handling+ Err+ + -- * The OFX data tree+ , HeaderTag , HeaderValue , OFXHeader(..) , TagName@@ -124,17 +127,20 @@ , pExceptional ) where +--+-- # Imports+--+ import Control.Applicative ( (<$), (<|>), (*>), many, optional, (<$>), (<*), (<*>), pure ) import Control.Monad (replicateM)-import qualified Control.Monad.Exception.Synchronous as Ex import qualified Data.Time as T import Text.Parsec.String (Parser) import Text.Parsec ( lookAhead, char, manyTill, anyChar, (<?>), eof,- try, digit, many1, spaces )+ try, digit, many1, spaces, string, choice ) import qualified Text.Parsec as P import Data.Maybe (listToMaybe) import qualified Data.Monoid as M@@ -145,9 +151,17 @@ ----- Data types+-- # Error handling -- +-- | Error handling. Errors are indicated with a Left String;+-- successes with a Right.+type Err = Either String++--+-- # Data types+--+ -- | Headers consists of simple @tag:value@ pairs; this represents the -- tag. type HeaderTag = String@@ -185,7 +199,7 @@ } deriving (Eq, Show) ----- Parsers+-- # Parsers -- -- | Parses either a UNIX or an MS-DOS newline. According to 1.2.2,@@ -199,19 +213,29 @@ -- | Parses a character, possibly with an escape sequence. The -- greater-than sign, less-than sign, and ampersand must be entered -- with escape sequences.+--+-- According to OFX spec section 2.3.2.1, ampersands, less-than signs,+-- and greater-than signs must appear as entities. However some banks+-- deliver broken OFX files that do not use entities for ampersands+-- (and possibly for less-than or greater-than signs too, although I+-- have not yet observed such behavior.) There is now an error message+-- that reflects this problem. Client code can filter the OFX data+-- for known offenders before passing it to this library. escChar :: Parser Char escChar = do c <- anyChar case c of '&' -> do- cs <- manyTill anyChar (char ';')- case cs of- "lt" -> return '<'- "gt" -> return '>'- "amp" -> return '&'- _ -> fail $ "unrecognized esacpe sequence: \"&"- ++ cs ++ ";\""+ let mkParser (ch, str) = try (ch <$ string str)+ ent <- choice (map mkParser+ [('<', "lt"), ('>', "gt"), ('&', "amp")])+ <?> ( "entity (\"lt\", \"gt\", or \"amp\")\n"+ ++ "some banks create broken OFX files. Try\n"+ ++ "removing the ampersands from the file and\n"+ ++ "trying again.")+ _ <- char ';'+ return ent _ -> return c <?> "character" @@ -286,9 +310,9 @@ <?> "OFX file" -- | Parses an OFX date; provides an error message if the parse fails.-parseDate :: String -> Ex.Exceptional String T.ZonedTime+parseDate :: String -> Err T.ZonedTime parseDate s = case P.parse date "" s of- Left e -> Ex.throw $ "could not parse date: " ++ s ++ ": "+ Left e -> Left $ "could not parse date: " ++ s ++ ": " ++ show e Right g -> return g @@ -370,7 +394,7 @@ Just _ -> return negate ----- Manipulating trees of tags+-- # Manipulating trees of tags -- -- | Finds child tags with the given name. When a tag is found, that@@ -446,14 +470,14 @@ -- | Finds the first tag (either this tag or any children) that has -- the given name and that is a data tag. Gives an error message if -- the tag is not found.-required :: TagName -> Tag -> Ex.Exceptional String TagData+required :: TagName -> Tag -> Err TagData required n t = case findData n t of- Nothing -> Ex.throw $ "required tag missing: " ++ n+ Nothing -> Left $ "required tag missing: " ++ n Just r -> return r ----- OFX data+-- # OFX data -- -- | OFX transaction types. These are used in STMTTRN aggregates, see@@ -664,13 +688,13 @@ | ACREDITLINE deriving (Eq, Show, Ord) -acctType :: String -> Ex.Exceptional String AcctType+acctType :: String -> Err AcctType acctType s | s == "CHECKING" = return ACHECKING | s == "SAVINGS" = return ASAVINGS | s == "MONEYMRKT" = return AMONEYMRKT | s == "CREDITLINE" = return ACREDITLINE- | otherwise = Ex.throw $ "unrecognized account type: " ++ s+ | otherwise = Left $ "unrecognized account type: " ++ s -- | Holds all data both for CURRENCY and for ORIGCURRENCY. data CurrencyData = CurrencyData@@ -689,7 +713,7 @@ deriving (Eq, Show) ----- Helpers to build aggregates+-- # Helpers to build aggregates -- trnType :: TagData -> Maybe TrnType trnType d = case d of@@ -712,13 +736,15 @@ "OTHER" -> Just TOTHER _ -> Nothing + -- | Gets a single Transaction from a tag. The tag should be the one -- named STMTTRN. Fails with an error message if any required field -- was not present.-transaction :: Tag -> Ex.Exceptional String Transaction+transaction :: Tag -> Err Transaction transaction t = do+ let fromMaybe e = maybe (Left e) Right trntyStr <- required "TRNTYPE" t- trnTy <- Ex.fromMaybe ("could not parse transaction type: " ++ trntyStr)+ trnTy <- fromMaybe ("could not parse transaction type: " ++ trntyStr) $ trnType trntyStr dtpStr <- required "DTPOSTED" t@@ -738,7 +764,7 @@ Nothing -> return Nothing Just d -> maybe (return Nothing)- (Ex.fromMaybe ("could not parse correct action: " ++ d))+ (fromMaybe ("could not parse correct action: " ++ d)) . safeRead $ d let srvrtid = findData "SRVRTID" t@@ -785,10 +811,10 @@ -- ^ The tag which contains the PAYEE tag, if there is one. This -- would typically be a STMTTRN tag. - -> Maybe (Ex.Exceptional String Payee)+ -> Maybe (Err Payee) -- ^ Nothing if there is no PAYEE tag. Just if a PAYEE tag is found,- -- with an Exception if the tag is lacking a required element, or a- -- Success if the tag is successfully parsed.+ -- with a Left if the tag is lacking a required element, or a+ -- Right if the tag is successfully parsed. -- -- If there is more than one PAYEE tag, only the first one is -- considered.@@ -806,14 +832,14 @@ <*> required "PHONE" t -currency :: Tag -> Maybe (Ex.Exceptional String Currency)+currency :: Tag -> Maybe (Err Currency) currency = fmap (fmap Currency) . fmap currencyData . listToMaybe . find "CURRENCY" -origCurrency :: Tag -> Maybe (Ex.Exceptional String OrigCurrency)+origCurrency :: Tag -> Maybe (Err OrigCurrency) origCurrency = fmap (fmap OrigCurrency) . fmap currencyData@@ -826,12 +852,12 @@ :: Tag -- ^ The tag that contains the data, e.g. CURRENCY or ORIGCURRENCY. - -> Ex.Exceptional String CurrencyData+ -> Err CurrencyData currencyData t = CurrencyData <$> required "CURRATE" t <*> required "CURSYM" t -bankAcctTo :: Tag -> Maybe (Ex.Exceptional String BankAcctTo)+bankAcctTo :: Tag -> Maybe (Err BankAcctTo) bankAcctTo = fmap getTo . listToMaybe . find "BANKACCTTO" where getTo t = BankAcctTo@@ -841,7 +867,7 @@ <*> (required "ACCTTYPE" t >>= acctType) <*> pure (findData "ACCTKEY" t) -ccAcctTo :: Tag -> Maybe (Ex.Exceptional String CCAcctTo)+ccAcctTo :: Tag -> Maybe (Err CCAcctTo) ccAcctTo = fmap getTo . listToMaybe . find "CCACCTTO" where getTo t = CCAcctTo@@ -859,11 +885,11 @@ -- library.) In case of the former, you can manually parse the -- transaction information yourself using functions like -- 'pathData'. In case of the latter, please send bugreports :-)-transactions :: OFXFile -> Ex.Exceptional String [Transaction]+transactions :: OFXFile -> Err [Transaction] transactions = mapM transaction . find "STMTTRN" . fTag ----- Pretty printers+-- # Pretty printers -- pPayee :: Payee -> Doc pPayee p = hang "Payee:" 2 ls@@ -945,8 +971,8 @@ pExceptional :: (e -> Doc) -> (a -> Doc)- -> Ex.Exceptional e a+ -> Either e a -> Doc pExceptional fe fa =- Ex.switch (\e -> hang "Exception:" 2 $ parens (fe e))- (\g -> hang "Success:" 2 $ parens (fa g))+ either (\e -> hang "Exception:" 2 $ parens (fe e))+ (\g -> hang "Success:" 2 $ parens (fa g))
ofx.cabal view
@@ -1,21 +1,6 @@--- Initial ofx.cabal generated by cabal init. For further documentation, --- see http://haskell.org/cabal/users-guide/---- The name of the package. name: ofx---- The package version. See the Haskell package versioning policy (PVP) --- for standards guiding when and how versions should be incremented.--- http://www.haskell.org/haskellwiki/Package_versioning_policy--- PVP summary: +-+------- breaking API changes--- | | +----- non-breaking API additions--- | | | +--- code changes with no API change-version: 0.2.0.0---- A short (one-line) description of the package.+version: 0.4.0.0 synopsis: Parser for OFX data---- A longer description of the package. description: A parser for Open Financial Exchange (OFX) financial data. This@@ -43,51 +28,27 @@ The OFX spec is available at <http://www.ofx.net> --- URL for the project homepage or repository. homepage: http://www.github.com/massysett/ofx---- The license under which the package is released. license: BSD3---- The file containing the license text. license-file: LICENSE---- The package author(s). author: Omari Norman---- An email address to which users can send suggestions, bug reports, and --- patches. maintainer: omari@smileystation.com---- A copyright notice.--- copyright: - category: Finance- build-type: Simple---- Constraint on the version of Cabal needed to build this package. cabal-version: >=1.8 - source-repository head type: git location: git://github.com/massysett/ofx.git library- -- Modules exported by the library. ghc-options: -Wall exposed-modules: Data.OFX - -- Modules included in this library but not exported.- -- other-modules: - - -- Other library packages from which modules are imported. build-depends: base ==4.6.*- , explicit-exception ==0.1.* , parsec ==3.1.* , pretty ==1.1.* , time ==1.4.*