packages feed

htalkat-0.1.1: Config.hs

-- This file is part of htalkat
-- Copyright (C) 2021 Martin Bays <mbays@sdf.org>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of version 3 of the GNU General Public License as
-- published by the Free Software Foundation, or any later version.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see http://www.gnu.org/licenses/.

module Config where

import           System.Directory (doesFileExist)
import           System.Exit      (exitFailure)
import           System.FilePath  ((</>))

import           Util

data Config = Config
    { listen_host        :: String
    , listen_port        :: Int
    , accept_unnamed     :: Bool
    , curses_local_top   :: Bool
    , curses_log         :: Bool
    , interactive_client :: [String]
    } deriving (Read)

confFilePath :: FilePath -> FilePath
confFilePath = (</> "htalkat.conf")

defaultConfigFile :: String
defaultConfigFile = unlines
    [ "Config"
    , "    { listen_host = \"\"  # Empty means bind all available hosts"
    , "    , listen_port = 5518"
    , "    , accept_unnamed = True  # Accept connections even from unnamed users"
    , ""
    , "    , curses_local_top = True  # Display you above remote in curses client"
    , "    , curses_log = False  # Log conversations to files in ~/.htalkat/logs/"
    , ""
    , "    # interactive_client: if non-empty, run in place of built-in client."
    , "    # First string is the command to run, and subsequent strings are arguments."
    , "    # It will be executed with two further arguments:"
    , "    # the name of the remote user,"
    , "    # and the path to a unix domain socket to interact with."
    , "    # Minimal example:"
    , "    #, interactive_client = [\"sh\", \"-c\","
    , "    #    \"socat unix-connect:\\\"$2\\\" stdio\", \"talkatc\"]"
    , "    , interactive_client = []"
    , "    }"
    ]

createConfigFileIfNecessary :: FilePath -> IO ()
createConfigFileIfNecessary ddir =
    let cpath = confFilePath ddir
    in doesFileExist cpath >>! writeFile cpath defaultConfigFile

loadConfig :: FilePath -> IO Config
loadConfig ddir = do
    createConfigFileIfNecessary ddir
    s <- readFile (confFilePath ddir)
    case readReadConfig (confFilePath ddir) s of
        Left err   -> putStrLn err >> exitFailure >> undefined
        Right conf -> pure conf

readReadConfig :: FilePath -> String -> Either String Config
readReadConfig path s =
    -- Based on https://hackage.haskell.org/package/tce-conf by Dino Morelli
    case reads $ removeComments s of
        [(c, _)    ] -> Right c
        ((_, x) : _) -> Left $ "ERROR parsing config file: " <> x <> hint
        [          ] -> Left $ "ERROR parsing config file" <> hint
    where
    hint = "; to recreate, remove " <> path <> " and try again."
    removeComments :: String -> String
    removeComments = unlines . map removeComment . lines
    removeComment = takeWhile (/= '#')