packages feed

cgrep-9.0.0: src/Config.hs

--
-- Copyright (c) 2013-2025 Nicola Bonelli <nicola@larthia.com>
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
--

module Config (
    Config (..),
    dumpPalette,
    getConfig,
) where

import Control.Monad (MonadPlus (mzero), filterM, forM_)
import System.Console.ANSI.Types (ConsoleIntensity (BoldIntensity), xterm6LevelRGB)
import System.Directory (doesFileExist, getHomeDirectory)

import Data.Aeson (FromJSON (parseJSON), (.!=), (.:?))
import Data.Maybe (fromMaybe, listToMaybe, mapMaybe)
import qualified Data.Yaml as Y

import CGrep.FileType (FileType)
import GHC.Generics (Generic)

import Data.List.Split (splitOn)
import System.FilePath ((</>))

import CGrep.FileKind (FileKind)
import System.Console.ANSI (Color (..), ColorIntensity (..), ConsoleLayer (..), SGR (..))
import System.Console.ANSI.Codes (setSGRCode)
import Text.Read (readMaybe)

cgreprc :: FilePath
cgreprc = "cgreprc"

data Config = Config
    { configFileTypes :: [FileType]
    , configFileKinds :: [FileKind]
    , configPruneDirs :: [String]
    , configColors :: Bool
    , configColorFile :: [SGR]
    , configColorMatch :: [SGR]
    , configFileLine :: Bool
    , configJobs :: Maybe Int
    }
    deriving stock (Show, Read)

defaultConfig :: Config
defaultConfig =
    Config
        { configFileTypes = []
        , configFileKinds = []
        , configPruneDirs = []
        , configColors = False
        , configColorFile = [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Blue]
        , configColorMatch = [SetConsoleIntensity BoldIntensity]
        , configFileLine = False
        , configJobs = Nothing
        }

mkConfig :: YamlConfig -> Config
mkConfig YamlConfig{..} =
    let configFileTypes = mapMaybe readMaybe yamlFileTypes
        configFileKinds = mapMaybe readMaybe yamlFileKinds
        configPruneDirs = yamlPruneDirs
        configColors = yamlColors
        configColorFile = fromMaybe [] (yamlColorFileName >>= readColor)
        configColorMatch = fromMaybe [] (yamlColorMatch >>= readColor)
        configFileLine = yamlFileLine
        configJobs = yamlJobs
     in Config{..}

data YamlConfig = YamlConfig
    { yamlFileTypes :: [String]
    , yamlFileKinds :: [String]
    , yamlPruneDirs :: [String]
    , yamlColors :: Bool
    , yamlColorFileName :: Maybe String
    , yamlColorMatch :: Maybe String
    , yamlFileLine :: Bool
    , yamlJobs :: Maybe Int
    }
    deriving stock (Show, Generic)

instance Y.FromJSON YamlConfig where
    parseJSON (Y.Object v) =
        YamlConfig
            <$> v .:? "file_types" .!= []
            <*> v .:? "file_kinds" .!= []
            <*> v .:? "prune_dirs" .!= []
            <*> v .:? "colors" .!= False
            <*> v .:? "color_filename" .!= Nothing
            <*> v .:? "color_match" .!= Nothing
            <*> v .:? "file_line" .!= False
            <*> v .:? "threads" .!= Nothing
    parseJSON _ = mzero

getConfig :: IO (Config, Maybe FilePath)
getConfig = do
    home <- getHomeDirectory
    confs <- filterM doesFileExist ["." <> cgreprc, home </> "." <> cgreprc, "/etc" </> cgreprc]
    case (listToMaybe confs) of
        Just fp -> do
            conf <- Y.decodeFileEither fp
            case conf of
                Left e -> errorWithoutStackTrace $ "CGrep:" <> Y.prettyPrintParseException e
                Right yconf -> return (mkConfig yconf, Just fp)
        Nothing -> return (defaultConfig, Nothing)

readColor :: String -> Maybe [SGR]
readColor "Bold" = Just [SetConsoleIntensity BoldIntensity]
readColor "Red" = Just [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Red]
readColor "Green" = Just [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Green]
readColor "Yellow" = Just [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Yellow]
readColor "Blue" = Just [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Blue]
readColor "Magenta" = Just [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Magenta]
readColor "Cyan" = Just [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Cyan]
readColor "White" = Just [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid White]
readColor "Orange" = Just [SetConsoleIntensity BoldIntensity, SetPaletteColor Foreground $ xterm6LevelRGB 5 2 0]
readColor "Acqua" = Just [SetConsoleIntensity BoldIntensity, SetPaletteColor Foreground $ xterm6LevelRGB 2 5 4]
readColor xs = case splitOn ":" xs of
    [r, g, b] -> Just [SetConsoleIntensity BoldIntensity, SetPaletteColor Foreground $ xterm6LevelRGB (read r) (read g) (read b)]
    _ -> Nothing

dumpPalette :: IO ()
dumpPalette = do
    let palette = [(r, g, b) | r <- [0 .. 5], g <- [0 .. 5], b <- [0 .. 5]]
    forM_ palette $ \(r, g, b) -> do
        putStrLn $ setSGRCode [SetConsoleIntensity BoldIntensity, SetPaletteColor Foreground $ xterm6LevelRGB r g b] <> "COLOR " <> show r <> ":" <> show g <> ":" <> show b <> setSGRCode []