packages feed

Tables-0.1.0.2: Main.hs

{- |
   Module      :   Tables.Tables2
   Copyright   :   (c) 2014 by Nate Pisarski
   License     :   BSD3
   Maintainer  :   nathanpisarski@gmail.com
   Stability   :   Stable
   Portability :   Portable (Standalone - ghc)
   Database client for the Quill2 database language. Use this, as Tables1 will be deprecated on June 1st, 2014.
-}
module Main(main,dispatch) where

import qualified Cookbook.Project.Quill.Quill2.Meta as Qm
import qualified Cookbook.Essential.Meta as Cm

import qualified Cookbook.Ingredients.Lists.Modify as Md
import System.IO
import System.Environment
import System.Exit

-- | Displayed from the "help" request in dispatch.
allArgs = [("Add a Quill","add x to y as z | add x to y | add x as table | add x as list"),
           ("Remove a Quill","remove x from y | remove x"),
           ("Change a Quill","change x in y to z"),
           ("Get a quill", "get x from y | get x | list"),
           ("Combine Quills", "map x to y as z | combine x with y as z")]
          
-- | Dispatches the command line arguments.
main = do
  arguments <- getArgs
  database  <- Qm.fromFile (head arguments)
  dispatch database (head arguments) (tail arguments)
  putStrLn "Done"

-- | Wraps a Quill error in the database by exiting, preventing database corruption.
qError :: Qm.QuillStatus a -> IO a
qError x = case x of
  (Qm.QuillSuccess a)  -> return a -- FIXME make this handle the cases, not complain about them.
  (Qm.QuillMultiple a) -> do
    putStrLn ("Multiple " ++ a ++ " exist in file. Exiting. Fix manually")
    exitFailure
  (Qm.QuillMissing a)  -> do
    putStrLn (a ++ " Missing from file. Exiting. Fix manually.")
    exitFailure

-- | Handles the command-line arguments with destructuring.
dispatch :: [Qm.Quill] -> String -> [String] -> IO ()
dispatch database fName ("change":x:"in":y:"to":z:w) = do
  qu <- qError (Qm.getQuill database y)
  case (snd qu) of
    (Qm.Table c) -> do
      myDB <- (qError (Qm.changeItem database (Qm.ATable (y,x,z))))
      Qm.toFile fName myDB
    (Qm.List c)  -> do
      mod <- qError $ Qm.removeItem database (y,x) -- FIXME make QuillAddition work for lists.
      myDB <- (qError (Qm.addItem mod (Qm.AList (y,z))))
      Qm.toFile fName myDB
  dispatch database fName w

dispatch database fName ("add":x:"to":y:"as":z:w) = do
  myDB <- (qError (Qm.addItem database (Qm.ATable (y,z,x))))
  Qm.toFile fName myDB
  dispatch database fName w

dispatch database fName ("add":x:"to":y:z) = do
  myDB <- (qError (Qm.addItem database (Qm.AList (y,x))))
  Qm.toFile fName myDB
  dispatch database fName z

dispatch database fName ("add":x:"as":y:w) = do
  fl <- Cm.filelines fName
  writeFile fName (unlines ((y ++ "(" ++ x ++ "){}"):fl))
  dispatch database fName w
  
dispatch database fName ("remove":x:"from":y:z) = do
  myDB <- qError (Qm.removeItem database (y,x))
  Qm.toFile fName $ myDB
  dispatch database fName z

dispatch database fName ("remove":x:y) = do
  Qm.toFile fName (Qm.removeQuill database x)
  dispatch database fName y

dispatch database fName ("get":x:"from":y:z) = do
  (qError $ Qm.lookUp database (y,x)) >>= putStrLn 
  dispatch database fName z

dispatch database fName ("get":x:y) = do
  myDB <- (qError (Qm.getQuill database x))
  case (snd myDB) of
    (Qm.List a) -> mapM_ putStrLn (map ppTable [(show fi,se) | fi <- [0..length a], se <- a])
    (Qm.Table a) -> mapM_ putStrLn (map ppTable a)
  dispatch database fName y

dispatch database fName ("list":y) = do
  mapM_ listOff database

-- Composite functions
dispatch database fName ("map":x:"to":y:"as":w:z) = do
  l1 <- qError (Qm.getQuill database x)
  l2 <- qError (Qm.getQuill database y)
  let l1list = case (snd l1) of
        (Qm.List a)  -> a
        (Qm.Table _) -> error "Error! Attempting to map table"
  let l2list = case (snd l2) of
        (Qm.List a)  -> a
        (Qm.Table _) -> error "Error! Attempting to map table"
  Qm.toFile fName ((w,Qm.Table (zip l1list l2list)):database)
  dispatch database fName z

dispatch database fName ("combine":x:"with":y:"as":w:z) = do
  l1 <- qError (Qm.getQuill database x)
  l2 <- qError (Qm.getQuill database y)
  case snd l1 of
    (Qm.List a)  -> case (snd l2) of
      (Qm.List b)  -> Qm.toFile fName ((w,Qm.List (b ++ a)):database)
      (Qm.Table b) -> error "Error! Attempted to combine a table and list"
    (Qm.Table a) -> case (snd l2) of
      (Qm.Table b) -> Qm.toFile fName ((w,Qm.Table (b ++ a)):database)
      (Qm.List b)  -> error "Error! Attempted to combine a table and list"
  dispatch database fName z

dispatch database fname ("file":y:z) = do
  fl <- Cm.filelines y
  cDispatch database fname (map (`Md.splitOn` ' ') fl)
  dispatch database fname z

  
dispatch database fname ("repl":_) = do
  repl fname database
  
dispatch database fName ("help":y) = do
  mapM_ putStrLn (map ppTable allArgs)
  dispatch database fName y
  
dispatch _ _ [] = return ()
dispatch fName db ("and":xs) = dispatch fName db xs
dispatch fName db (x:xs) = do
  putStrLn ("Did not recognize command: "++x)
  dispatch fName db xs

cDispatch _ _ [] = return ()
cDispatch db fname (x:y) = do
  dispatch db fname x
  nDB <- Qm.fromFile fname
  cDispatch nDB fname y
  
-- | Pretty print a table.
ppTable :: (String, String) -> String
ppTable (a,b) = Cm.flt [a," : ",b]

-- | List the contents of a Quill.
listOff :: Qm.Quill -> IO ()
listOff x = case (snd x) of
  (Qm.Table _) -> putStrLn $ "Table " ++ (fst x)
  (Qm.List _)  -> putStrLn $ "List " ++ (fst x)

-- | Evaluate Quill commands in an interactive Read-eval-print loop.
repl :: String -> [Qm.Quill] -> IO ()
repl fname dbase = do
  inp <- Cm.prompt "$ "
  dispatch dbase fname (Md.splitOn inp ' ')
  x <- Qm.fromFile fname
  repl fname x