suckless-conf improvements

This commit is contained in:
voidlizard 2025-02-01 20:43:37 +03:00
parent 5a1e95b79c
commit c2b49f3fd7
3 changed files with 142 additions and 49 deletions

View File

@ -9,6 +9,8 @@ module Data.Config.Suckless.Script.Internal
) where ) where
import Data.Config.Suckless import Data.Config.Suckless
import Data.Config.Suckless.Syntax
import Data.Config.Suckless.Parse.Fuzzy as P
import Data.Config.Suckless.Almost.RPC import Data.Config.Suckless.Almost.RPC
import Data.Traversable import Data.Traversable
@ -17,6 +19,10 @@ import Control.Monad
import Control.Monad.Identity import Control.Monad.Identity
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Writer import Control.Monad.Writer
import Data.Aeson as Aeson
import Data.Yaml qualified as Yaml
import Data.Ini qualified as Ini
import Data.Ini (Ini(..))
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.ByteString.Char8 qualified as BS8 import Data.ByteString.Char8 qualified as BS8
import Data.ByteString qualified as BS import Data.ByteString qualified as BS
@ -226,39 +232,6 @@ display_ = liftIO . print
{- HLINT ignore "Functor law" -} {- HLINT ignore "Functor law" -}
class IsContext c => MkSym c a where
mkSym :: a -> Syntax c
instance IsContext c => MkSym c String where
mkSym s = Symbol noContext (Id $ Text.pack s)
instance IsContext c => MkSym c Text where
mkSym s = Symbol noContext (Id s)
instance IsContext c => MkSym c Id where
mkSym = Symbol noContext
class IsContext c => MkStr c s where
mkStr :: s -> Syntax c
instance IsContext c => MkStr c String where
mkStr s = Literal noContext $ LitStr (Text.pack s)
instance IsContext c => MkStr c Text where
mkStr s = Literal noContext $ LitStr s
mkBool :: forall c . IsContext c => Bool -> Syntax c
mkBool v = Literal noContext (LitBool v)
class IsContext c => MkForm c a where
mkForm :: a-> [Syntax c] -> Syntax c
instance (IsContext c, MkSym c s) => MkForm c s where
mkForm s sy = List noContext ( mkSym @c s : sy )
mkList :: forall c. IsContext c => [Syntax c] -> Syntax c
mkList = List noContext
isFalse :: forall c . IsContext c => Syntax c -> Bool isFalse :: forall c . IsContext c => Syntax c -> Bool
isFalse = \case isFalse = \case
@ -271,18 +244,6 @@ eatNil f = \case
Nil -> pure () Nil -> pure ()
x -> void $ f x x -> void $ f x
class IsContext c => MkInt c s where
mkInt :: s -> Syntax c
class IsContext c => MkDouble c s where
mkDouble :: s -> Syntax c
instance (IsContext c, RealFrac s) => MkDouble c s where
mkDouble v = Literal noContext $ LitScientific (realToFrac v)
instance (Integral i, IsContext c) => MkInt c i where
mkInt n = Literal noContext $ LitInt (fromIntegral n)
class OptionalVal c b where class OptionalVal c b where
optional :: b -> Syntax c -> b optional :: b -> Syntax c -> b
@ -799,8 +760,6 @@ lookupValue i = do
Just (BindValue s) -> pure s Just (BindValue s) -> pure s
_ -> throwIO (NameNotBound i) _ -> throwIO (NameNotBound i)
nil :: forall c . IsContext c => Syntax c
nil = List noContext []
nil_ :: (IsContext c, MonadIO m) => (a -> RunM c m b) -> a -> RunM c m (Syntax c) nil_ :: (IsContext c, MonadIO m) => (a -> RunM c m b) -> a -> RunM c m (Syntax c)
nil_ m w = m w >> pure (List noContext []) nil_ m w = m w >> pure (List noContext [])
@ -824,6 +783,21 @@ fmt = \case
LitStrVal x -> pretty $ Text.unpack x LitStrVal x -> pretty $ Text.unpack x
x -> pretty x x -> pretty x
newtype IniConfig = IniConfig Ini.Ini
instance IsContext c => MkSyntax c IniConfig where
mkSyntax (IniConfig (Ini{..})) = do
let section kvs = [ mkList [mkSym k, either (const (mkStr v)) fixContext (P.parseSyntax v)]
| (k,v) <- kvs
]
let globals = section iniGlobals
let sections = [ mkForm @c s (section pps) | (s, pps) <- HM.toList iniSections ]
mkList (globals <> sections)
internalEntries :: forall c m . (IsContext c, Exception (BadFormException c), MonadUnliftIO m) => MakeDictM c m () internalEntries :: forall c m . (IsContext c, Exception (BadFormException c), MonadUnliftIO m) => MakeDictM c m ()
internalEntries = do internalEntries = do
@ -1175,12 +1149,37 @@ internalEntries = do
e@(SymbolVal x) -> wrapWith e e@(SymbolVal x) -> wrapWith e
_ -> id _ -> id
entry $ bindMatch "json:stdin" $ const do
parseJson <$> liftIO (LBS.hGetContents stdin)
entry $ bindMatch "json:file" $ \case
[StringLike fn] -> do
parseYaml <$> liftIO (LBS.readFile fn)
_ -> throwIO (BadFormException @c nil)
entry $ bindMatch "yaml:stdin" $ const do
parseYaml <$> liftIO (LBS.hGetContents stdin)
entry $ bindMatch "yaml:file" $ \case
[StringLike fn] -> do
parseYaml <$> liftIO (LBS.readFile fn)
_ -> throwIO (BadFormException @c nil)
entry $ bindMatch "ini:stdin" $ const do
parseIni <$> liftIO (LBS.hGetContents stdin)
entry $ bindMatch "ini:file" $ \case
[StringLike fn] -> do
parseIni <$> liftIO (LBS.readFile fn)
_ -> throwIO (BadFormException @c nil)
entry $ bindMatch "top:stdin" $ const do entry $ bindMatch "top:stdin" $ const do
liftIO TIO.getContents liftIO TIO.getContents
<&> either (const nil) (mkList . fmap fixContext) . parseTop <&> either (const nil) (mkList . fmap fixContext) . parseTop
entry $ bindMatch "top:file" $ \case entry $ bindMatch "top:file" $ \case
[StringLike fn] -> do [StringLike fn] -> do
liftIO $ TIO.readFile fn liftIO $ TIO.readFile fn
@ -1388,9 +1387,26 @@ internalEntries = do
_ -> pure nil _ -> pure nil
parseJson :: forall c . IsContext c => LBS.ByteString -> Syntax c
parseJson input = case Aeson.decode @Value input of
Just val -> mkSyntax @c val
Nothing -> nil
parseYaml :: forall c . IsContext c => LBS.ByteString -> Syntax c
parseYaml input =
case Yaml.decodeEither' @Value (LBS.toStrict input) of
Left _ -> nil @c
Right val -> mkSyntax @c val
parseIni :: forall c . IsContext c => LBS.ByteString -> Syntax c
parseIni input =
case Ini.parseIni (decodeUtf8With ignore $ LBS.toStrict input) of
Left _ -> nil
Right ini -> mkSyntax @c (IniConfig ini)
matchOne :: IsContext c => Text -> Syntax c -> Bool matchOne :: IsContext c => Text -> Syntax c -> Bool
matchOne what = \case matchOne what = \case
s@(TextLike x) | Text.isInfixOf what x -> True s@(TextLike x) | Text.isInfixOf what x -> True
e@(ListVal xs) -> or [ Text.isInfixOf what s | TextLike s <- xs ] e@(ListVal xs) -> or [ Text.isInfixOf what s | TextLike s <- xs ]
_ -> False _ -> False

View File

@ -22,6 +22,15 @@ module Data.Config.Suckless.Syntax
, fromOpaqueThrow , fromOpaqueThrow
, isByteString , isByteString
, SyntaxTypeError(..) , SyntaxTypeError(..)
, nil
, mkList
, mkBool
, MkForm(..)
, MkSym(..)
, MkInt(..)
, MkStr(..)
, MkDouble(..)
, MkSyntax(..)
, pattern SymbolVal , pattern SymbolVal
, pattern ListVal , pattern ListVal
, pattern LitIntVal , pattern LitIntVal
@ -45,7 +54,7 @@ import Data.Scientific
import GHC.Generics (Generic(..)) import GHC.Generics (Generic(..))
import Data.Maybe import Data.Maybe
import Data.Aeson import Data.Aeson
import Data.Aeson.Key import Data.Aeson.Key as Aeson
import Data.Aeson.KeyMap qualified as Aeson import Data.Aeson.KeyMap qualified as Aeson
import Data.Vector qualified as V import Data.Vector qualified as V
import Data.Traversable (forM) import Data.Traversable (forM)
@ -145,6 +154,9 @@ class HasContext c a where
class IsLiteral a where class IsLiteral a where
mkLit :: a -> Literal mkLit :: a -> Literal
class IsContext c => ToSyntax c a where
toSyntax :: a -> Syntax c
newtype Id = newtype Id =
Id Text Id Text
deriving newtype (IsString,Pretty,Semigroup,Monoid) deriving newtype (IsString,Pretty,Semigroup,Monoid)
@ -312,4 +324,66 @@ instance IsContext c => FromJSON (Syntax c) where
pure $ List noContext (Symbol noContext (Id "object") : pairs) pure $ List noContext (Symbol noContext (Id "object") : pairs)
parseJSON _ = fail "Cannot parse JSON to Syntax" parseJSON _ = fail "Cannot parse JSON to Syntax"
class IsContext c => MkSym c a where
mkSym :: a -> Syntax c
instance IsContext c => MkSym c String where
mkSym s = Symbol noContext (Id $ Text.pack s)
instance IsContext c => MkSym c Text where
mkSym s = Symbol noContext (Id s)
instance IsContext c => MkSym c Id where
mkSym = Symbol noContext
class IsContext c => MkStr c s where
mkStr :: s -> Syntax c
instance IsContext c => MkStr c String where
mkStr s = Literal noContext $ LitStr (Text.pack s)
instance IsContext c => MkStr c Text where
mkStr s = Literal noContext $ LitStr s
mkBool :: forall c . IsContext c => Bool -> Syntax c
mkBool v = Literal noContext (LitBool v)
nil :: forall c . IsContext c => Syntax c
nil = List noContext []
class IsContext c => MkForm c a where
mkForm :: a-> [Syntax c] -> Syntax c
instance (IsContext c, MkSym c s) => MkForm c s where
mkForm s sy = List noContext ( mkSym @c s : sy )
mkList :: forall c. IsContext c => [Syntax c] -> Syntax c
mkList = List noContext
class IsContext c => MkInt c s where
mkInt :: s -> Syntax c
class IsContext c => MkDouble c s where
mkDouble :: s -> Syntax c
instance (IsContext c, RealFrac s) => MkDouble c s where
mkDouble v = Literal noContext $ LitScientific (realToFrac v)
instance (Integral i, IsContext c) => MkInt c i where
mkInt n = Literal noContext $ LitInt (fromIntegral n)
class MkSyntax c a where
mkSyntax :: a -> Syntax c
instance IsContext c => MkSyntax c (Syntax c) where
mkSyntax = id
instance IsContext c => MkSyntax c Value where
mkSyntax Null = nil
mkSyntax (Number n) = mkDouble n
mkSyntax (String n) = mkStr n
mkSyntax (Bool b) = mkBool b
mkSyntax (Array ns) = mkList [ mkSyntax n | n <- V.toList ns]
mkSyntax (Object kv) = mkList [ mkList [mkSym (Aeson.toText k), mkSyntax v] | (k,v) <- Aeson.toList kv]

View File

@ -84,6 +84,7 @@ library
, filepattern , filepattern
, fuzzy-parse >= 0.1.3.1 , fuzzy-parse >= 0.1.3.1
, hashable , hashable
, ini
, interpolatedstring-perl6 , interpolatedstring-perl6
, microlens-platform , microlens-platform
, mtl , mtl
@ -96,10 +97,12 @@ library
, text , text
, time , time
, transformers , transformers
, toml-parser
, typed-process , typed-process
, unliftio , unliftio
, unordered-containers , unordered-containers
, vector , vector
, yaml
hs-source-dirs: lib hs-source-dirs: lib
default-language: Haskell2010 default-language: Haskell2010