packages feed

dawdle 0.1.0.0 → 0.1.0.1

raw patch · 8 files changed

+94/−74 lines, 8 filesdep ~dawdlePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: dawdle

API changes (from Hackage documentation)

- Paths_dawdle: getBinDir :: IO FilePath
- Paths_dawdle: getDataDir :: IO FilePath
- Paths_dawdle: getDataFileName :: FilePath -> IO FilePath
- Paths_dawdle: getLibDir :: IO FilePath
- Paths_dawdle: getLibexecDir :: IO FilePath
- Paths_dawdle: getSysconfDir :: IO FilePath
- Paths_dawdle: version :: Version
+ Database.Dawdle.Options: [optVerifyIntegrity] :: Options -> Bool
+ Database.Dawdle.Options: defaultOptions :: Options
- Database.Dawdle.Dawdle: analyzeFile :: [[String]] -> Either String [CellType]
+ Database.Dawdle.Dawdle: analyzeFile :: Options -> [[String]] -> Either String [CellType]
- Database.Dawdle.Options: Options :: Bool -> Maybe FilePath -> Integer -> Bool -> Char -> Options
+ Database.Dawdle.Options: Options :: Bool -> Maybe FilePath -> Maybe Int -> Bool -> Char -> Bool -> Options
- Database.Dawdle.Options: [optStopAfter] :: Options -> Integer
+ Database.Dawdle.Options: [optStopAfter] :: Options -> Maybe Int

Files

bin/main.lhs view
@@ -1,49 +1,54 @@+
 > {-# LANGUAGE ExplicitForAll,TupleSections,
 >              NoMonomorphismRestriction,OverloadedStrings,
->              FlexibleContexts, RankNTypes, ViewPatterns #-}
+>              FlexibleContexts, RankNTypes #-}
 
-> import Database.Dawdle.Parser
-> import Database.Dawdle.Dawdle
-> import Database.Dawdle.Options
-> import Database.Dawdle.Utils
-> import Database.Dawdle.PrettyPrint
+> import           Database.Dawdle.Parser
+> import           Database.Dawdle.Dawdle
+> import           Database.Dawdle.Options
+> import           Database.Dawdle.Utils
+> import           Database.Dawdle.PrettyPrint
 
 > import qualified Data.Text.Lazy.IO as LT
-> import qualified Data.Text.Lazy as LT
-> import Text.Parsec.Text ()
+> import qualified Data.Text.Lazy    as LT
  
-> import Data.Maybe
-> import System.Environment
-> import System.FilePath
-
+> import           Data.Maybe
+> import           System.Environment
+> import           System.FilePath
+> import           System.IO ( hPutStrLn, stdin, stderr )
 
 > main :: IO ()
 > main = do
 >  a <- getArgs
->  opts <- getOpts a
->  let source = fromMaybe "stdin" $ (optInput . fst) opts
->      sepChar = (optSepChar . fst) opts
->      inputMode = (optInput . fst) opts
->      withHeader = (optWithHeader . fst) opts
+>  opts <- fst <$> getOpts a
+>  let source = fromMaybe "stdin" $ optInput opts
+>      sepChar = optSepChar opts
+>      inputMode = optInput opts
+>      withHeader = optWithHeader opts
+>      stopAfter = optStopAfter opts
 >  c <- case inputMode of
 >         Just f -> LT.readFile f
->         Nothing -> do
->           LT.pack <$> getContents
->  case parseCsv sepChar source c of
+>         Nothing -> LT.hGetContents stdin
+>  c' <- case stopAfter of
+>        Nothing ->
+>          hPutStrLn stderr "Warning: No limit was set. Entire file will be read" >> 
+>          return c
+>        Just x -> return $ LT.unlines $ take x $ LT.lines c
+>  case parseCsv sepChar source c' of
 >    Left e -> do
 >      putStrLn "Error parsing input:"
 >      print e
 >    Right [] -> error "Empty parsing result. Empty CSV?"
->    Right res@(x:xs) -> do
+>    Right res@(x:xs) ->
 >      if withHeader
 >      then if null xs
 >        then error "No content after header. Empty CSV!"
->        else -- Parser rest with pretty columns
+>        else -- Parse rest with pretty columns
 >             let x' = normalizeNames x
->             in putStrLn $ either error (pretty (genTbName source) x') $ analyzeFile xs
->      else -- No header
+>             in putStrLn $ either error (pretty (genTbName source) x') $ analyzeFile opts xs
+>      else -- No header, parse entire file
 >        let colNms = take (length x) [ "col_"++show i | i <- [1..] :: [Int] ]
->        in putStrLn $ either error (pretty (genTbName source) colNms) $ analyzeFile res
+>        in putStrLn $ either error (pretty (genTbName source) colNms) $ analyzeFile opts res
  
 >  where
 >    genTbName = takeBaseName
dawdle.cabal view
@@ -2,9 +2,9 @@ -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                dawdle
-version:             0.1.0.0
+version:             0.1.0.1
 synopsis:            Generates DDL suggestions based on a CSV file
--- description:         
+description:         Generates DDL suggestions based on a CSV file
 homepage:            https://github.com/arnons1/dawdle
 license:             MIT
 license-file:        LICENSE
@@ -36,12 +36,10 @@                        Database.Dawdle.PrettyPrint,
                        Database.Dawdle.Types,
                        Database.Dawdle.Options,
-                       Database.Dawdle.Utils,
-                       Paths_dawdle
+                       Database.Dawdle.Utils
   ghc-options:         -Wall -O3
 executable dawdle
   main-is:             main.lhs
-  other-modules:       Paths_dawdle
   hs-source-dirs:      bin
   default-language:    Haskell2010
   build-depends:       base >=4.8 && <4.9,
@@ -49,5 +47,5 @@                        pretty >= 1.0 && < 1.2,
                        text >= 0.11.1.13 && < 1.3,
                        filepath >= 1.4.0.0,
-                       dawdle == 0.1.0.0
+                       dawdle == 0.1.0.1
   ghc-options:         -Wall -O3
src/Database/Dawdle/Dawdle.lhs view
@@ -1,31 +1,34 @@+
 > {-# LANGUAGE ExplicitForAll,TupleSections,
 >              NoMonomorphismRestriction,OverloadedStrings,
->              FlexibleContexts, RankNTypes, ViewPatterns #-}
+>              FlexibleContexts, RankNTypes #-}
 
 > module Database.Dawdle.Dawdle
 >        (analyzeFile)
 > where
-> import Database.Dawdle.Types
-> import Database.Dawdle.Utils
+> import           Database.Dawdle.Options
+> import           Database.Dawdle.Types
+> import           Database.Dawdle.Utils
 
-> import Data.Char
-> import Data.Int
-> import Data.List
-> import Data.Time.Format
-> import Data.Time.Calendar
-> import Text.Read (readMaybe)
-> import Control.Monad
+> import           Control.Monad
+> import           Data.Char
+> import           Data.Int
+> import           Data.List
+> import           Data.Time.Format
+> import           Data.Time.Calendar
+> import           Text.Read (readMaybe)
 
-> analyzeFile :: [[String]] -> Either String [CellType]
-> analyzeFile mtrx = do
+> analyzeFile :: Options -> [[String]] -> Either String [CellType]
+> analyzeFile o mtrx = do
 >   let cols = transpose mtrx
 >   -- sanity on structure
->   unless (allTheSame (map length cols)) $ Left $ "Invalid CSV: Some rows have a different number of columns.\nPerhaps your separator is wrong?"
+>   when (optVerifyIntegrity o) $ unless (allTheSame (map length cols))
+>     $ Left "Invalid CSV: Some rows have a different number of columns.\nPerhaps your separator is wrong?"
 >   mapM workOnAColumn cols
 
 > workOnAColumn :: [String] -> Either String CellType
 > workOnAColumn [] = Left "Empty column, no cell types inferred"
-> workOnAColumn cts = do
+> workOnAColumn cts = 
 >   foldM analyzeCell Unknown cts
 
 > analyzeCell :: CellType -> String -> Either String CellType
@@ -33,17 +36,17 @@ >  | null s || isNullText s = Right $ composeMaxTypesWithNulls mp $ Nullable Unknown
 >  | b <- map toLower s
 >  , b `elem` ["false","true"]
->  , (removeNull mp) `elem` [CTBool,Unknown] =
+>  , removeNull mp `elem` [CTBool,Unknown] =
 >      Right $ composeMaxTypesWithNulls mp CTBool
->  | (isDateOrUnknown $ removeNull mp)
+>  | isDateOrUnknown $ removeNull mp
 >  , Just fFmt <- msum
->       [ ptm fmt s | fmt <- (iso8601DateFormat Nothing) : 
->                            (if length s == 8 then ["%Y%m%d"] else [])] = 
+>       [ ptm fmt s | fmt <- iso8601DateFormat Nothing : 
+>                            ["%Y%m%d" | length s == 8] ] = 
 >      case getDateFormatFromCT mp of
 >        Just oFmt |
 >          oFmt /= fFmt -> return $ composeMaxTypesWithNulls mp $ CTChar (length s)
 >        _ -> return $ composeMaxTypesWithNulls mp $ CTDate fFmt
->  | (isDateTimeOrUnknown $ removeNull mp)
+>  | isDateTimeOrUnknown $ removeNull mp
 >  , Just fFmt <- msum
 >       [ ptm fmt s | fmt <- [iso8601DateFormat $ Just "%H:%M:%S"
 >                                      ,"%Y-%m-%d %H:%M:%S%Q"
@@ -54,22 +57,21 @@ >          oFmt /= fFmt -> return $ composeMaxTypesWithNulls mp $ CTChar (length s)
 >        _ -> return $ composeMaxTypesWithNulls mp $ CTDateTime fFmt
 >  | isInt s
->  , (removeNull mp) < CTDateTime "" = do
->       s' <- maybeToEither ("Can't read integer as Integer (??)") (readMaybe s :: Maybe Integer)
+>  , removeNull mp < CTDateTime ""
+>  , Right s' <- maybeToEither "Can't read integer as Integer (??)" (readMaybe s :: Maybe Integer) =
 >       composeMaxTypesWithNulls mp <$> intType' s'
 >  | (not . isInt) s && isCTNumber s
->  , (removeNull mp) < CTChar 1 = do
->      s' <- maybeToEither ("Can't read float type as Double (??)") (readMaybe s :: Maybe Double)
+>  , removeNull mp < CTChar 1
+>  , Right s' <- maybeToEither "Can't read float type as Double (??)" (readMaybe s :: Maybe Double) =
 >      composeMaxTypesWithNulls mp <$> floatType s'
 >   | otherwise = Right $ composeMaxTypesWithNulls mp $ CTChar (length s)
-
+> {-# INLINE analyzeCell #-}
 
 > isNullText :: String -> Bool
 > isNullText s = (map toLower s == "null") || (s == "\\N")
 
 > ptm :: String -> String -> Maybe String
-> ptm tf ts = maybe Nothing (const $ Just tf) ((parseTimeM True defaultTimeLocale tf ts) :: Maybe Day)
-
+> ptm tf ts = maybe Nothing (const $ Just tf) (parseTimeM True defaultTimeLocale tf ts :: Maybe Day)
 
 > intType' :: Integer -> Either String CellType
 > intType' x
@@ -82,6 +84,6 @@ > floatType :: Double -> Either String CellType
 > floatType x = 
 >   let x' = realToFrac x :: Float
->   in if (show x') /= (show x)
+>   in if show x' /= show x
 >      then Right CTDouble
 >      else Right CTFloat
src/Database/Dawdle/Options.lhs view
@@ -1,27 +1,31 @@+
+
 > module Database.Dawdle.Options
 >  (Options(..)
->  ,getOpts)
+>  ,getOpts
+>  ,defaultOptions)
 > where
 >
-> import System.Console.GetOpt
-> import Data.Maybe ( fromMaybe )
-> import Text.Read ( readMaybe )
+> import           System.Console.GetOpt
+> import           Text.Read ( readMaybe )
 >
 > data Options = Options
 >  { optVerbose     :: Bool
 >  , optInput       :: Maybe FilePath
->  , optStopAfter   :: Integer
+>  , optStopAfter   :: Maybe Int
 >  , optWithHeader  :: Bool
 >  , optSepChar     :: Char
+>  , optVerifyIntegrity :: Bool
 >  } deriving Show
 
 > defaultOptions :: Options
 > defaultOptions    = Options
 >  { optVerbose     = False
 >  , optInput       = Nothing
->  , optStopAfter   = 10000
+>  , optStopAfter   = Just 10000
 >  , optWithHeader  = False
 >  , optSepChar     = ','
+>  , optVerifyIntegrity = True
 >  }
 >
 > options :: [OptDescr (Options -> Options)]
@@ -30,17 +34,20 @@ >      (NoArg (\ opts -> opts { optVerbose = True }))
 >      "chatty output on stderr"
 >   ,Option ['f']     ["input"]
->      (OptArg ((\ f opts -> opts { optInput = Just f }) . fromMaybe "input")
->              "FILE")
+>      (ReqArg (\ f opts -> opts { optInput = Just f }) "FILE")
 >      "input FILE"
 >   ,Option ['t']     ["threshold"]
->      (ReqArg (\ d opts -> opts { optStopAfter = (fromMaybe (1000 :: Integer) (readMaybe d :: Maybe Integer))}) "INTEGER")
+>      (ReqArg (\ d opts -> opts { optStopAfter = readMaybe d :: Maybe Int}) "INT")
 >      "stop after how many lines"
 >   ,Option ['h']     ["with-header"]
 >      (NoArg (\ opts -> opts { optWithHeader = True }))
 >      "is there a header line"
 >   ,Option ['s']     ["separator"]
->      (ReqArg (\d opts -> opts { optSepChar = headErr' d }) "CHAR") "Separator char"
+>      (ReqArg (\d opts -> opts { optSepChar = headErr' d }) "CHAR")
+>      "Separator char"
+>   ,Option [] ["skip-verify-integrity"]
+>      (NoArg (\ opts -> opts { optVerifyIntegrity = False }))
+>      "Skip integrity verification of CSV file - all lines have the same number of columns"
 >   ]
 
 > headErr' :: [a] -> a
src/Database/Dawdle/Parser.lhs view
@@ -1,3 +1,4 @@+
 > {-# LANGUAGE ExplicitForAll,TupleSections,
 >              NoMonomorphismRestriction,OverloadedStrings,
 >              FlexibleContexts, RankNTypes #-}
@@ -24,13 +25,15 @@ > line sepChar = sepBy (cell sepChar) (char sepChar)
 > cell :: Char -> SParser String
 > cell sepChar = quotedCell <|> many (noneOf (sepChar:newLines))
+> {-# INLINE cell #-}
 
 > quotedCell :: SParser String
 > quotedCell = do
->        sChar <- (oneOf quoteChars)
+>        sChar <- oneOf quoteChars
 >        content <- many (fmap return nonEscape <|> escape) --(quotedChar sChar)
 >        _ <- char sChar <?> "quote at end of cell"
 >        return $ concat content
+> {-# INLINE quotedCell #-}
 
  quotedChar :: Char -> SParser Char
  quotedChar csep =
@@ -39,12 +42,14 @@ 
 > nonEscape :: SParser Char
 > nonEscape = noneOf "\\\"\0\n\r\v\t\b\f"
+> {-# INLINE nonEscape #-}
 
 > escape :: SParser String
 > escape = do
 >   d <- char '\\'
 >   c <- oneOf "\\\"0nrvtbf"
 >   return [d,c]
+> {-# INLINE escape #-}
 
 > eol :: SParser String
 > eol =   try (string newLines)
src/Database/Dawdle/PrettyPrint.lhs view
@@ -1,3 +1,4 @@+
 > {-# LANGUAGE ExplicitForAll,LambdaCase,
 >              NoMonomorphismRestriction,OverloadedStrings,
 >              FlexibleContexts, RankNTypes #-}
@@ -8,12 +9,12 @@ > import Text.PrettyPrint
 
 > pretty :: String -> [String] -> [CellType] -> String
-> pretty fn colns = render . (ddl fn colns)
+> pretty fn colns = render . ddl fn colns
 
 > ddl :: String -> [String] -> [CellType] -> Doc
 > ddl fn colns cts = text "create table" <+> text fn <+> body
 >   where
->     body = parens $ nest 3 $ vcat $ (punctuate comma clnms)
+>     body = parens $ nest 3 $ vcat $ punctuate comma clnms
 >     clnms = zipWith (\a b -> text a <+> b) colns $ map (prettyTypes True) cts
 
 > prettyTypes :: Bool -> CellType -> Doc
@@ -28,7 +29,7 @@ >   CTDouble -> intp $ text "float"
 >   CTDate _ -> intp $ text "date"
 >   CTDateTime _ -> intp $ text "datetime"
->   CTChar i -> intp $ text "varchar" <> (parens $ int i)
+>   CTChar i -> intp $ text "varchar" <> (parens (int i))
 >   Unknown -> intp $ text "!!UNKNOWN!!"
 >   where
 >     intp tn = if isNullPass
src/Database/Dawdle/Types.lhs view
@@ -1,3 +1,4 @@+
 > {-# LANGUAGE FlexibleContexts, DeriveDataTypeable, RankNTypes, KindSignatures #-}
  
 > module Database.Dawdle.Types
src/Database/Dawdle/Utils.lhs view
@@ -1,3 +1,4 @@+
 > module Database.Dawdle.Utils
 >  (getFstIfJust
 >  ,isCTNumber
@@ -25,9 +26,9 @@ > maybeToEither s Nothing = Left s
 
 > allTheSame :: (Eq a) => [a] -> Bool
-> allTheSame xs = and $ map (== head xs) (tail xs)
+> allTheSame xs = all (== head xs) (tail xs)
 
 > normalizeNames :: [String] -> [String]
 > normalizeNames = map normalize
 >  where
->    normalize = map toLower . (filter (\x -> x=='_' || isAlphaNum x)) . intercalate "_" . words
+>    normalize = map toLower . filter (\x -> x=='_' || isAlphaNum x) . intercalate "_" . words