packages feed

hledger-flow-0.19.0: src/Hledger/Flow/Init.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

-- | Start a new ledger.
module Hledger.Flow.Init
  ( InitOutcome (..),
    initLedger,
    runInit,
  )
where

import Control.Exception (IOException, catch)
import Control.Monad (when)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Hledger.Flow.BaseDir (findBaseDir)
import Hledger.Flow.Common (Shell (..), nativeShell, shellArgument)
import Hledger.Flow.PathHelpers (AbsDir)
import Hledger.Flow.Status.Render (printNext, tildePath)
import Hledger.Flow.Types (BaseDir)
import Path
import Path.IO (createDirIfMissing, doesDirExist, getHomeDir)
import qualified System.FilePath as FilePath

data InitOutcome
  = -- | The new ledger, and an unrelated ledger above it, if any.
    Created BaseDir (Maybe BaseDir)
  | -- | The folder already has @import/@.
    AlreadyLedger BaseDir
  | -- | The folder is inside this ledger's @import/@ tree.
    InsideLedger BaseDir
  deriving (Eq, Show)

-- | Create @DIR/import/@ unless that would nest inside another ledger.
initLedger :: AbsDir -> IO InitOutcome
initLedger dir = do
  hasImport <- doesDirExist (dir </> [reldir|import|])
  if hasImport
    then return (AlreadyLedger dir)
    else do
      above <- findBaseDir dir
      case above of
        Just b | let imp = b </> [reldir|import|], imp == dir || imp `isProperPrefixOf` dir -> return (InsideLedger b)
        _ -> do
          createDirIfMissing True (dir </> [reldir|import|])
          return (Created dir above)

-- | Run @hledger-flow init@ for a folder, printing what happened and what's next.
runInit :: Maybe FilePath -> AbsDir -> AbsDir -> IO ()
runInit hledgerPath cwd dir = do
  home <- (Just <$> getHomeDir) `catch` (\e -> const (return Nothing) (e :: IOException))
  let absolute = T.pack . FilePath.dropTrailingPathSeparator . toFilePath
      shown = T.pack . tildePath home . T.unpack . absolute
  outcome <- initLedger dir
  case outcome of
    AlreadyLedger b ->
      ioError . userError . T.unpack $
        shown b <> " is already a hledger-flow ledger. Run `hledger-flow status` there to see where it stands."
    InsideLedger b ->
      ioError . userError . T.unpack $
        "That folder is inside the ledger at " <> shown b <> ". Start a new ledger outside its import/ folder."
    Created base parentLedger -> do
      T.putStrLn ("Started a ledger in " <> shown base)
      mapM_ (\p -> T.putStrLn ("Note: " <> shown p <> " also has an import/ folder. Commands run inside " <> shown base <> " use the new ledger.")) parentLedger
      when (base /= cwd) $ do
        let changeDir = case nativeShell of
              Posix -> "cd "
              PowerShell -> "Set-Location -LiteralPath "
        T.putStrLn ("Change to it first: " <> changeDir <> shellArgument nativeShell (absolute base))
      printNext hledgerPath base