spreadsheet 0.1.2.1 → 0.1.3
raw patch · 3 files changed
+159/−4 lines, 3 filesdep +bytestringnew-component:exe:csvreplace
Dependencies added: bytestring
Files
- spreadsheet.cabal +55/−4
- src/CSVReplace.hs +103/−0
- src/Data/Spreadsheet/CharSource.hs +1/−0
spreadsheet.cabal view
@@ -1,5 +1,5 @@ Name: spreadsheet-Version: 0.1.2.1+Version: 0.1.3 License: BSD3 License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de>@@ -16,13 +16,55 @@ <http://www.xoltar.org/languages/haskell/CSV.hs>. Both do not parse lazy. Reading from other source than plain 'String's could be easily added.+ .+ If you install this package by+ .+ cabal install -fbuildExamples+ .+ then an example program is compiled and installed, too.+ This program fills a template text using data from a CSV file.+ E.g. given a file @template.txt@ with content+ .+ > Name: FIRSTNAME SURNAME+ > Born: BIRTH+ .+ and @names.csv@ with content+ .+ > "FIRSTNAME","SURNAME",BIRTH+ > "Georg","Cantor",1845+ > "Haskell","Curry",1900+ > "Ada","Lovelace",1815+ .+ the call+ .+ > csvreplace --template=template.txt <names.csv+ .+ produces the output+ .+ > Name: Georg Cantor+ > Born: 1845+ > Name: Haskell Curry+ > Born: 1900+ > Name: Ada Lovelace+ > Born: 1815+ .+ For similar (non-Haskell) programs see @cut@, @csvfix@, @csvtool@. Tested-With: GHC==6.8.2, GHC==6.12.3 Cabal-Version: >=1.6 Build-Type: Simple Source-Repository head- type: darcs- location: http://code.haskell.org/~thielema/spreadsheet/+ Type: darcs+ Location: http://code.haskell.org/~thielema/spreadsheet/ +Source-Repository this+ Tag: 0.1.3+ Type: darcs+ Location: http://code.haskell.org/~thielema/spreadsheet/++Flag buildExamples+ description: Build example executables+ default: False+ Flag splitBase description: Choose the new smaller, split-up base package. @@ -37,10 +79,19 @@ Build-Depends: base >= 1.0 && < 2 GHC-Options: -Wall- Extensions: GeneralizedNewtypeDeriving Hs-Source-Dirs: src Exposed-Modules: Data.Spreadsheet Other-Modules: Data.Spreadsheet.Parser Data.Spreadsheet.CharSource++Executable csvreplace+ If flag(buildExamples)+ Build-Depends:+ bytestring >=0.9 && <0.10+ Else+ Buildable: False+ GHC-Options: -Wall+ Hs-Source-Dirs: src+ Main-Is: CSVReplace.hs
+ src/CSVReplace.hs view
@@ -0,0 +1,103 @@+module Main where++import qualified Data.Spreadsheet as Sheet+import qualified Data.List.HT as ListHT++import System.Console.GetOpt (getOpt, ArgOrder(..), OptDescr(..), ArgDescr(..), usageInfo, )+import System.Environment (getArgs, getProgName, )+import qualified System.Exit as Exit+import qualified System.IO as IO++import qualified Data.ByteString.Char8 as B+import qualified Data.ByteString.Lazy.Char8 as BL++import qualified Control.Monad.Exception.Asynchronous as AExc+import qualified Control.Monad.Exception.Synchronous as Exc+import Control.Monad.Trans.Class (lift, )+import Control.Monad (when, )+import Data.Foldable (forM_, )+++data Flags =+ Flags {+ optMultiFile :: Maybe FilePath,+ optQuotation,+ optDelimiter :: Char+ }++defltFlags :: Flags+defltFlags = Flags {+ optMultiFile = Nothing,+ optQuotation = '"',+ optDelimiter = ','+ }++exitFailureMsg :: String -> IO a+exitFailureMsg msg = do+ IO.hPutStrLn IO.stderr msg+ Exit.exitFailure++options :: [OptDescr (Flags -> IO Flags)]+options =+ Option ['h'] ["help"]+ (NoArg (\_ -> do+ programName <- getProgName+ putStrLn $ flip usageInfo options $+ "Usage: " ++ programName ++ " [OPTIONS] TEMPLATE-FILE\n" +++ "The CSV file is read from standard input.\n"+ Exit.exitSuccess))+ "show options" :+ Option [] ["multifile"]+ (flip ReqArg "FILEPATTERN" $ \str flags ->+ return $ flags{optMultiFile = Just str})+ "generate one file per CSV row" :+ Option ['d'] ["delimiter"]+ (flip ReqArg "CHAR" $ \str flags -> do+ case str of+ [c] -> return $ flags{optDelimiter = c}+ _ -> exitFailureMsg $ "delimiter must be one character, which " ++ show str ++ " is not")+ "field delimiter character" :+ Option ['q'] ["quotation"]+ (flip ReqArg "CHAR" $ \str flags -> do+ case str of+ [c] -> return $ flags{optQuotation = c}+ _ -> exitFailureMsg $ "quotation mark must be one character, which " ++ show str ++ " is not")+ "quotation mark character" :+ []+++replaceRow :: String -> [String] -> [String] -> String+replaceRow template names row =+ ListHT.multiReplace (filter (not . null . fst) $ zip names row) template++replace :: String -> [String] -> Sheet.T -> String+replace template names =+ concatMap (replaceRow template names)+++main :: IO ()+main =+ Exc.resolveT (\e -> exitFailureMsg $ "Aborted: " ++ e) $ do+ argv <- lift getArgs+ let (opts, files, errors) = getOpt RequireOrder options argv+ when (not (null errors)) $ Exc.throwT $ concat $ errors+ flags <- lift $ foldr (=<<) (return defltFlags) opts+ case files of+ [templateName] -> do+ template <- fmap B.unpack $ lift $ B.readFile templateName+ sheet <-+ fmap (Sheet.fromString (optQuotation flags) (optDelimiter flags)+ . BL.unpack) $+ lift BL.getContents+ case AExc.result sheet of+ [] -> Exc.throwT "empty CSV input"+ (names:rows) ->+ case optMultiFile flags of+ Nothing -> do+ lift $ putStr $ replace template names rows+ forM_ (AExc.exception sheet) $ Exc.throwT+ Just filePattern ->+ lift $ forM_ rows $ \row ->+ writeFile (replaceRow filePattern names row) $+ replaceRow template names row+ _ -> Exc.throwT "I need exactly one template file."
src/Data/Spreadsheet/CharSource.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-} module Data.Spreadsheet.CharSource where import Control.Monad.Trans.State (StateT(StateT), gets, runStateT, mapStateT, )