packages feed

hi (empty) → 0.0.1

raw patch · 15 files changed

+443/−0 lines, 15 filesdep +Globdep +HUnitdep +basesetup-changed

Dependencies added: Glob, HUnit, base, bytestring, directory, filepath, hspec, process, split, template, temporary, text, time

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2013, Fujimura Daisuke++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 Fujimura Daisuke 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hi.cabal view
@@ -0,0 +1,124 @@+name:                hi+version:             0.0.1+synopsis:            Generate scaffold for cabal project+license:             BSD3+license-file:        LICENSE+author:              Fujimura Daisuke+maintainer:          me@fujimuradaisuke.com+category:            Distribution+build-type:          Simple+cabal-version:       >=1.8+homepage:            https://github.com/fujimura/hi+description:+    This application generates a scaffold for Haskell project from a Git repository.++    .+    This command+    .++    .+    > $ hi --package-name "foo-bar-baz" --module-name "Foo.Bar.Baz" --author "Fujimura Daisuke" --email "me@fujimuradaisuke.com"+    .++    .+    will generate:+    .++    .+    > $ tree .+    > .+    > ├── LICENSE+    > ├── README.md+    > ├── foo-bar-baz.cabal+    > ├── src+    > │  └── Foo+    > │      └── Bar+    > │          ├── Baz+    > │          │  └── Internal.hs+    > │          └── Baz.hs+    > └── test+    >     ├── Foo+    >     │  └── Bar+    >     │      ├── Baz+    >     │      └── BazSpec.hs+    >     └── Spec.hs+    .++    .+    See <https://github.com/fujimura/hi> for further usage.+    .+++library+  exposed-modules:+      Distribution.Hi+      Distribution.Hi.Compiler+      Distribution.Hi.Context+      Distribution.Hi.Directory+      Distribution.Hi.FilePath+      Distribution.Hi.Flag+      Distribution.Hi.Option+      Distribution.Hi.Template+      Distribution.Hi.Types+      Distribution.Hi.Version+  ghc-options:+      -Wall+  hs-source-dirs:+      src+  build-depends:+        base       == 4.*+      , Glob+      , bytestring+      , directory+      , filepath+      , process+      , split+      , template   == 0.2.*+      , temporary+      , text+      , time++executable hi+  main-is:+      Main.hs+  ghc-options:+      -Wall+  hs-source-dirs:+      src+  build-depends:+        base       == 4.*+      , Glob+      , bytestring+      , directory+      , filepath+      , process+      , split+      , template   == 0.2.*+      , temporary+      , text+      , time++test-suite spec+  main-is:+      Spec.hs+  type:+      exitcode-stdio-1.0+  ghc-options:+      -Wall+  hs-source-dirs:+      src+    , test+  build-depends:+        base+      , HUnit+      , bytestring+      , directory+      , hspec       >= 1.5+      , process+      , template    == 0.2.*+      , temporary+      , text++source-repository head+  type:     git+  location: https://github.com/fujimura/hi.git
+ src/Distribution/Hi.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE NamedFieldPuns #-}+module Distribution.Hi where++import           Distribution.Hi.Compiler (compile)+import           Distribution.Hi.FilePath (toDestionationPath, toDir)+import           Distribution.Hi.Context  (context)+import           Distribution.Hi.Types+import           Distribution.Hi.Template (withTemplatesFromRepo)+import           Control.Arrow        ((&&&))+import           Control.Monad+import           System.Directory     (createDirectoryIfMissing,+                                       getCurrentDirectory)+import           System.FilePath      (joinPath)++-- | Main function+cli :: InitFlags -> IO ()+cli initFlags@(InitFlags {moduleName, repository}) = do+    currentDirecotory <- getCurrentDirectory++    withTemplatesFromRepo (repository) $ \templates -> do++      let sourceAndDestinations = map (id &&& toDestionationPath initFlags) templates++      createDirectoryIfMissing True $ joinPath [currentDirecotory, "src",  toDir moduleName]+      createDirectoryIfMissing True $ joinPath [currentDirecotory, "test", toDir moduleName]++      forM_ sourceAndDestinations $ \(s,d) -> compile s (joinPath [currentDirecotory, d]) $ context initFlags
+ src/Distribution/Hi/Compiler.hs view
@@ -0,0 +1,17 @@+module Distribution.Hi.Compiler+    (+      compile+    ) where++import qualified Data.Text.IO       as T+import qualified Data.Text.Lazy.IO  as LT+import           Data.Text.Template (Context, substitute)++-- | Compile a file from template with given 'Context'.+compile :: FilePath -- Source+        -> FilePath -- Destionation+        -> Context  -- Context+        -> IO ()+compile src dst ctx = do+    tmpl <- T.readFile src+    LT.writeFile dst $ substitute tmpl ctx
+ src/Distribution/Hi/Context.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE RecordWildCards #-}+module Distribution.Hi.Context+    (+      context+    ) where++import           Distribution.Hi.Types+import qualified Data.Text          as T+import           Data.Text.Template (Context)++-- | Create a 'Context' from 'InitFlags Will raise error if the key was not found.+context :: InitFlags -> Context+context (InitFlags {..}) x = T.pack . lookup' $ T.unpack x+  -- TODO FIXME boilerplate+  where lookup' "packageName" = packageName+        lookup' "moduleName"  = moduleName+        lookup' "author"      = author+        lookup' "email"       = email+        lookup' "repository"  = repository+        lookup' "year"        = year+        lookup' k             = error $ "Key is not defined: " ++ k
+ src/Distribution/Hi/Directory.hs view
@@ -0,0 +1,23 @@+module Distribution.Hi.Directory+    (+      inDirectory+    , inTemporaryDirectory+    ) where++import           Control.Exception    (bracket_)+import           System.Directory+import           System.IO.Temp       (withSystemTempDirectory)++-- |Run callback in a temporary directory.+inTemporaryDirectory :: String         -- ^ Base of temorary directory name+                     -> (IO a -> IO a) -- ^ Callback+inTemporaryDirectory name callback =+    withSystemTempDirectory name $ flip inDirectory callback+++-- |Run callback in given directory.+inDirectory :: FilePath        -- ^ Filepath to run callback+            -> (IO a -> IO a)  -- ^ Callback+inDirectory path callback = do+    pwd <- getCurrentDirectory+    bracket_ (setCurrentDirectory path) (setCurrentDirectory pwd) callback
+ src/Distribution/Hi/FilePath.hs view
@@ -0,0 +1,29 @@+module Distribution.Hi.FilePath+    (+      toDestionationPath+    , toDir+    ) where++import           Distribution.Hi.Types+import           Distribution.Hi.Template (untemplate)+import           Data.List+import           Data.List.Split      (splitOn)+import           System.FilePath      (joinPath)++-- | Convert given path to the destination path, with given options.+toDestionationPath :: InitFlags -> FilePath -> FilePath+toDestionationPath InitFlags {moduleName=m, packageName=p} =+    rename1 . rename2 . untemplate+  where+    rename1 = replace "package-name" p+    rename2 = replace "ModuleName" (toDir m)++-- | Convert module name to path+-- @+-- toDir "Foo.bar" # => "Foo/Bar"+-- @+toDir :: String -> String+toDir = joinPath . splitOn "."++replace :: Eq a => [a] -> [a] -> [a] -> [a]+replace a b = foldl1 (++) . intersperse b . splitOn a
+ src/Distribution/Hi/Flag.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE OverloadedStrings #-}++module Distribution.Hi.Flag+    (+      extractInitFlags+    ) where++import           Distribution.Hi.Types++-- | Extract 'InitFlags' from a list of 'Arg'+extractInitFlags :: [Arg] -> InitFlags+extractInitFlags args = InitFlags { packageName = lookupPackageName+                                  , moduleName  = lookupModuleName+                                  , author      = lookupAuthor+                                  , email       = lookupEmail+                                  , repository  = lookupRepository+                                  , year        = lookupRepository+                                  }+  where+    lookupPackageName = lookup' "packageName"+    lookupModuleName  = lookup' "moduleName"+    lookupAuthor      = lookup' "author"+    lookupEmail       = lookup' "email"+    lookupRepository  = lookup' "repository"+    lookup' label = case lookup label $ [(l, v) | (Val l v) <- args] of+                        Just v  -> v+                        Nothing -> if label == "repository"+                                     then defaultRepo+                                     else error $ errorMessageFor label+    errorMessageFor l = concat [ "Could not find option: "+                                , l+                                , "\n (Run with no arguments to see usage)"+                               ]++defaultRepo :: String+defaultRepo = "git://github.com/fujimura/hi-hspec.git"
+ src/Distribution/Hi/Option.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE OverloadedStrings #-}++module Distribution.Hi.Option+    (+      getInitFlags+    , getMode+    ) where++import           Distribution.Hi.Flag      (extractInitFlags)+import           Distribution.Hi.Types+import           Control.Applicative+import           Data.Time.Calendar    (toGregorian)+import           Data.Time.Clock       (getCurrentTime, utctDay)+import           System.Console.GetOpt+import           System.Environment    (getArgs)++-- | Available options.+options :: [OptDescr Arg]+options =+    [ Option ['p'] ["package-name"](ReqArg (Val "packageName") "package-name")  "Name of package"+    , Option ['m'] ["module-name"] (ReqArg (Val "moduleName") "Module.Name")  "Name of Module"+    , Option ['a'] ["author"]      (ReqArg (Val "author") "NAME")  "Name of the project's author"+    , Option ['e'] ["email"]       (ReqArg (Val "email") "EMAIL")  "Email address of the maintainer"+    , Option ['r'] ["repository"]  (ReqArg (Val "repository") "REPOSITORY")  "Template repository(optional)"+    , Option ['v'] ["version"]     (NoArg Version) "show version number"+    ]++-- | Returns 'InitFlags'.+getInitFlags :: IO InitFlags+getInitFlags = extractInitFlags <$> (addYear =<< fst <$> parseArgs <$> getArgs)++-- | Returns 'Mode'.+getMode :: IO Mode+getMode = do+    args <- fst <$> parseArgs <$> getArgs+    return $ if any id [True |Version <- args]+               then ShowVersion+               else Run++parseArgs :: [String] -> ([Arg], [String])+parseArgs argv =+   case getOpt Permute options argv of+      ([],_,errs) -> error $ concat errs ++ usageInfo header options+      (o,n,[]   ) -> (o,n)+      (_,_,errs ) -> error $ concat errs ++ usageInfo header options+  where+    header = "Usage: hi [OPTION...]"++addYear :: [Arg] -> IO [Arg]+addYear args = do+    (y,_,_) <- (toGregorian . utctDay) <$> getCurrentTime+    return $ (Val "year" $ show y):args
+ src/Distribution/Hi/Template.hs view
@@ -0,0 +1,32 @@+module Distribution.Hi.Template+    (+      withTemplatesFromRepo+    , untemplate+    ) where++import           Distribution.Hi.Directory (inTemporaryDirectory)+import           Data.List.Split       (splitOn)+import           System.Exit           (ExitCode)+import           System.FilePath.Glob  (compile, globDir1)+import           System.Process        (system)++-- | Run callback with list of template files.+withTemplatesFromRepo :: String               -- ^ Repository url+                      -> ([FilePath] -> IO a) -- ^ Callback which takes list of the template file+                      -> IO a                 -- ^ Result+withTemplatesFromRepo repo cb =+    inTemporaryDirectory "hi" $ do+        -- TODO Handle error+        _ <- cloneRepo repo+        paths <- globDir1 (compile "./**/*.template") "./"+        cb paths++-- | Remove \".template\" from 'FilePath'+untemplate :: FilePath -> FilePath+untemplate = head . splitOn ".template"++-- | Clone given repository to current directory+cloneRepo :: String -> IO ExitCode+cloneRepo repoUrl = do+    _ <- system $ "git clone --no-checkout --quiet --depth=1 " ++ repoUrl ++ " ./"+    system "git checkout HEAD --quiet"
+ src/Distribution/Hi/Types.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE OverloadedStrings #-}++module Distribution.Hi.Types+    (+      Flag+    , InitFlags(..)+    , Label+    , Arg(..)+    , Mode(..)+    ) where++type Flag = String++data InitFlags =+    InitFlags { packageName :: !Flag+              , moduleName  :: !Flag+              , author      :: !Flag+              , email       :: !Flag+              , repository  :: !Flag+              , year        :: !Flag+              } deriving(Eq, Show)++type Label = String++-- | Arguments.+data Arg = Version | Val Label String deriving(Eq, Show)++-- | Run mode.+data Mode = ShowVersion | Run deriving(Eq, Show)
+ src/Distribution/Hi/Version.hs view
@@ -0,0 +1,7 @@+module Distribution.Hi.Version+    (+      version+    ) where++version :: String+version = "0.0.1"
+ src/Main.hs view
@@ -0,0 +1,13 @@+module Main where++import qualified Distribution.Hi         as Hi+import           Distribution.Hi.Option  (getInitFlags, getMode)+import           Distribution.Hi.Types+import           Distribution.Hi.Version (version)++main :: IO ()+main = do+    mode <- getMode+    case mode of+      ShowVersion -> print version+      Run         -> Hi.cli =<< getInitFlags
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}