diff options
author | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-10-30 01:14:02 +0200 |
---|---|---|
committer | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-10-30 01:14:02 +0200 |
commit | cc5327fa854fd0274697d39b8be24475c7387e61 (patch) | |
tree | a00d7bbf4bb302e8f04779061b8a0a3b44326a7b | |
parent | Minor bugfixes. (diff) | |
download | vimwiki-scripts-cc5327fa854fd0274697d39b8be24475c7387e61.tar.gz vimwiki-scripts-cc5327fa854fd0274697d39b8be24475c7387e61.tar.xz |
Replace ini parser.
ini-config had the problem where I needed to do a low-level hack for
dynamic keys. Plain ini just works.
-rw-r--r-- | hs/Config.hs | 75 | ||||
-rw-r--r-- | hs/Data/Ini/Config.hs | 529 | ||||
-rw-r--r-- | hs/Data/Ini/Config/KeyValue.hs | 15 | ||||
-rw-r--r-- | hs/Data/Ini/Config/URIDict.hs | 27 | ||||
-rw-r--r-- | hs/Html.hs | 5 | ||||
-rw-r--r-- | hs/vimwiki.cabal | 16 |
6 files changed, 58 insertions, 609 deletions
diff --git a/hs/Config.hs b/hs/Config.hs index 69dedf6..a2ceaeb 100644 --- a/hs/Config.hs +++ b/hs/Config.hs @@ -1,17 +1,20 @@ {-# LANGUAGE TemplateHaskell , OverloadedStrings , FlexibleInstances + , ImportQualifiedPost #-} module Config where import Control.Lens -import Data.Ini.Config import Data.Map (Map) -import Network.URI (URI) +import Data.Map qualified as M +import Network.URI (URI, parseURI) import Options.Applicative -import Data.Ini.Config.URIDict +import Data.Ini +import Data.Maybe (isJust, fromMaybe, fromJust) import Data.String +import Data.Text (Text, unpack) data Args = Args { indir :: Maybe String @@ -30,9 +33,9 @@ argp = Args <*> strOption (long "conf" <> short 'c' <> help "Alternative Configuration" <> metavar "CONF" <> value Nothing) <*> strOption (long "webp" <> help "Prefix for web" <> metavar "PREFIX" <> value Nothing) -opts = info (argp <**> helper) - (fullDesc - <> progDesc "Build Vimwiki" +opts = info (argp <**> helper) + (fullDesc + <> progDesc "Build Vimwiki" <> header "build - build vimwiki") @@ -61,25 +64,47 @@ makeLenses ''Configuration makeLenses ''DataConfig makeLenses ''OutputConfig -confParser :: IniParser Configuration -confParser = do - out <- section "Output" $ do - mu4eURL <- fieldMbOf "mu4eURL" string - xapianOmega <- fieldMbOf "xapianOmega" string - webPath <- fieldDefOf "webpath" string "" - manProvider <- fieldMbOf "manProvider" string - return OutputConfig { _mu4eURL = mu4eURL - , _xapianOmega = xapianOmega - , _manProvider = manProvider - , _webPath = webPath } - data' <- sectionDef "Data" (_data' defaultConf) $ do - inputDir <- fieldOf "inputDir" string - outputDir <- fieldOf "outputDir" string - return DataConfig { _inputDir = inputDir, _outputDir = outputDir } - wikis <- section "Wikis" uriDictParser - return $ Configuration out data' wikis - -runParser = flip parseIniFile $ confParser +-- +-- either to maybe +etm :: Either a b -> Maybe b +etm (Left _) = Nothing +etm (Right a) = Just a + +fromRight (Right x) = x + +parseURIs :: [(Text, String)] -> Map String URI +parseURIs lst = lst & fmap (_2 %~ parseURI) + & filter (isJust . snd) + & fmap (_2 %~ fromJust) + & fmap (_1 %~ unpack) + & M.fromList + +confParser :: Ini -> Configuration +confParser ini = + Configuration + { _output = OutputConfig { _mu4eURL = d "Output" "mu4eURL" + , _xapianOmega = d "Output" "xapianOmega" + , _webPath = fromMaybe "" $ d "Output" "webpath" + , _manProvider = d "Output" "manProvider" + } + , _data' = if "Data" `elem` sections ini + then DataConfig + -- Thi4s will crash if key is missing + { _inputDir = f "Data" "inputDir" + , _outputDir = f "Data" "outputDir" + } + else (_data' defaultConf) + , _wikis = case keys "Wikis" ini of + Left _ -> mempty + -- This is safe + Right keys -> parseURIs $ zip keys $ f "Wikis" <$> keys + } + + where d a b = etm $ unpack <$> lookupValue a b ini + f a b = unpack . fromRight $ lookupValue a b ini + +runParser :: Text -> Either String Configuration +runParser t = confParser <$> parseIni t parseCmdline = execParser opts diff --git a/hs/Data/Ini/Config.hs b/hs/Data/Ini/Config.hs deleted file mode 100644 index 00a3819..0000000 --- a/hs/Data/Ini/Config.hs +++ /dev/null @@ -1,529 +0,0 @@ -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - --- | --- Module : Data.Ini.Config --- Copyright : (c) Getty Ritter, 2017 --- License : BSD --- Maintainer : Getty Ritter <config-ini@infinitenegativeutility.com> --- Stability : experimental --- --- The 'config-ini' library exports some simple monadic functions to --- make parsing INI-like configuration easier. INI files have a --- two-level structure: the top-level named chunks of configuration, --- and the individual key-value pairs contained within those chunks. --- For example, the following INI file has two sections, @NETWORK@ --- and @LOCAL@, and each contains its own key-value pairs. Comments, --- which begin with @#@ or @;@, are ignored: --- --- > [NETWORK] --- > host = example.com --- > port = 7878 --- > --- > # here is a comment --- > [LOCAL] --- > user = terry --- --- The combinators provided here are designed to write quick and --- idiomatic parsers for files of this form. Sections are parsed by --- 'IniParser' computations, like 'section' and its variations, --- while the fields within sections are parsed by 'SectionParser' --- computations, like 'field' and its variations. If we want to --- parse an INI file like the one above, treating the entire --- @LOCAL@ section as optional, we can write it like this: --- --- > data Config = Config --- > { cfNetwork :: NetworkConfig, cfLocal :: Maybe LocalConfig } --- > deriving (Eq, Show) --- > --- > data NetworkConfig = NetworkConfig --- > { netHost :: String, netPort :: Int } --- > deriving (Eq, Show) --- > --- > data LocalConfig = LocalConfig --- > { localUser :: Text } --- > deriving (Eq, Show) --- > --- > configParser :: IniParser Config --- > configParser = do --- > netCf <- section "NETWORK" $ do --- > host <- fieldOf "host" string --- > port <- fieldOf "port" number --- > return NetworkConfig { netHost = host, netPort = port } --- > locCf <- sectionMb "LOCAL" $ --- > LocalConfig <$> field "user" --- > return Config { cfNetwork = netCf, cfLocal = locCf } --- --- --- We can run our computation with 'parseIniFile', which, --- when run on our example file above, would produce the --- following: --- --- >>> parseIniFile example configParser --- Right (Config {cfNetwork = NetworkConfig {netHost = "example.com", netPort = 7878}, cfLocal = Just (LocalConfig {localUser = "terry"})}) -module Data.Ini.Config - ( -- * Parsing Files - parseIniFile, - - -- * Parser Types - IniParser, - SectionParser(SectionParser), - - -- * Section-Level Parsing - section, - sections, - sectionOf, - sectionsOf, - sectionMb, - sectionDef, - - -- * Field-Level Parsing - field, - fieldOf, - fieldMb, - fieldMbOf, - fieldDef, - fieldDefOf, - fieldFlag, - fieldFlagDef, - - -- * Reader Functions - readable, - number, - string, - flag, - listWithSeparator, - ) -where - -import Control.Applicative (Alternative (..)) -import Control.Monad.Trans.Except -import Data.Ini.Config.Raw -import Data.Sequence (Seq) -import qualified Data.Sequence as Seq -import Data.String (IsString (..)) -import Data.Text (Text) -import qualified Data.Text as T -import Data.Typeable (Proxy (..), Typeable, typeRep) -import GHC.Exts (IsList (..)) -import Text.Read (readMaybe) - -lkp :: NormalizedText -> Seq (NormalizedText, a) -> Maybe a -lkp t = go . Seq.viewl - where - go ((t', x) Seq.:< rs) - | t == t' = Just x - | otherwise = go (Seq.viewl rs) - go Seq.EmptyL = Nothing - -addLineInformation :: Int -> Text -> StParser s a -> StParser s a -addLineInformation lineNo sec = withExceptT go - where - go e = - "Line " ++ show lineNo - ++ ", in section " - ++ show sec - ++ ": " - ++ e - -type StParser s a = ExceptT String ((->) s) a - --- | An 'IniParser' value represents a computation for parsing entire --- INI-format files. -newtype IniParser a = IniParser (StParser RawIni a) - deriving (Functor, Applicative, Alternative, Monad) - --- | A 'SectionParser' value represents a computation for parsing a single --- section of an INI-format file. -newtype SectionParser a = SectionParser (StParser IniSection a) - deriving (Functor, Applicative, Alternative, Monad) - --- | Parse a 'Text' value as an INI file and run an 'IniParser' over it -parseIniFile :: Text -> IniParser a -> Either String a -parseIniFile text (IniParser mote) = do - ini <- parseRawIni text - runExceptT mote ini - --- | Find a named section in the INI file and parse it with the provided --- section parser, failing if the section does not exist. In order to --- support classic INI files with capitalized section names, section --- lookup is __case-insensitive__. --- --- >>> parseIniFile "[ONE]\nx = hello\n" $ section "ONE" (field "x") --- Right "hello" --- >>> parseIniFile "[ONE]\nx = hello\n" $ section "TWO" (field "x") --- Left "No top-level section named \"TWO\"" -section :: Text -> SectionParser a -> IniParser a -section name (SectionParser thunk) = IniParser $ - ExceptT $ \(RawIni ini) -> - case lkp (normalize name) ini of - Nothing -> Left ("No top-level section named " ++ show name) - Just sec -> runExceptT thunk sec - --- | Find multiple named sections in the INI file and parse them all --- with the provided section parser. In order to support classic INI --- files with capitalized section names, section lookup is --- __case-insensitive__. --- --- >>> parseIniFile "[ONE]\nx = hello\n[ONE]\nx = goodbye\n" $ sections "ONE" (field "x") --- Right (fromList ["hello","goodbye"]) --- >>> parseIniFile "[ONE]\nx = hello\n" $ sections "TWO" (field "x") --- Right (fromList []) -sections :: Text -> SectionParser a -> IniParser (Seq a) -sections name (SectionParser thunk) = IniParser $ - ExceptT $ \(RawIni ini) -> - let name' = normalize name - in mapM - (runExceptT thunk . snd) - (Seq.filter (\(t, _) -> t == name') ini) - --- | A call to @sectionOf f@ will apply @f@ to each section name and, --- if @f@ produces a "Just" value, pass the extracted value in order --- to get the "SectionParser" to use for that section. This will --- find at most one section, and will produce an error if no section --- exists. --- --- >>> parseIniFile "[FOO]\nx = hello\n" $ sectionOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x")) --- Right ("F","hello") --- >>> parseIniFile "[BAR]\nx = hello\n" $ sectionOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x")) --- Left "No matching top-level section" -sectionOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser a -sectionOf fn sectionParser = IniParser $ - ExceptT $ \(RawIni ini) -> - let go Seq.EmptyL = Left "No matching top-level section" - go ((t, sec) Seq.:< rs) - | Just v <- fn (actualText t) = - let SectionParser thunk = sectionParser v - in runExceptT thunk sec - | otherwise = go (Seq.viewl rs) - in go (Seq.viewl ini) - --- | A call to @sectionsOf f@ will apply @f@ to each section name and, --- if @f@ produces a @Just@ value, pass the extracted value in order --- to get the "SectionParser" to use for that section. This will --- return every section for which the call to @f@ produces a "Just" --- value. --- --- >>> parseIniFile "[FOO]\nx = hello\n[BOO]\nx = goodbye\n" $ sectionsOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x")) --- Right (fromList [("F","hello"),("B","goodbye")]) --- >>> parseIniFile "[BAR]\nx = hello\n" $ sectionsOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x")) --- Right (fromList []) -sectionsOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser (Seq a) -sectionsOf fn sectionParser = IniParser $ - ExceptT $ \(RawIni ini) -> - let go Seq.EmptyL = return Seq.empty - go ((t, sec) Seq.:< rs) - | Just v <- fn (actualText t) = - let SectionParser thunk = sectionParser v - in do - x <- runExceptT thunk sec - xs <- go (Seq.viewl rs) - return (x Seq.<| xs) - | otherwise = go (Seq.viewl rs) - in go (Seq.viewl ini) - --- | Find a named section in the INI file and parse it with the provided --- section parser, returning 'Nothing' if the section does not exist. --- In order to --- support classic INI files with capitalized section names, section --- lookup is __case-insensitive__. --- --- >>> parseIniFile "[ONE]\nx = hello\n" $ sectionMb "ONE" (field "x") --- Right (Just "hello") --- >>> parseIniFile "[ONE]\nx = hello\n" $ sectionMb "TWO" (field "x") --- Right Nothing -sectionMb :: Text -> SectionParser a -> IniParser (Maybe a) -sectionMb name (SectionParser thunk) = IniParser $ - ExceptT $ \(RawIni ini) -> - case lkp (normalize name) ini of - Nothing -> return Nothing - Just sec -> Just `fmap` runExceptT thunk sec - --- | Find a named section in the INI file and parse it with the provided --- section parser, returning a default value if the section does not exist. --- In order to --- support classic INI files with capitalized section names, section --- lookup is __case-insensitive__. --- --- >>> parseIniFile "[ONE]\nx = hello\n" $ sectionDef "ONE" "def" (field "x") --- Right "hello" --- >>> parseIniFile "[ONE]\nx = hello\n" $ sectionDef "TWO" "def" (field "x") --- Right "def" -sectionDef :: Text -> a -> SectionParser a -> IniParser a -sectionDef name def (SectionParser thunk) = IniParser $ - ExceptT $ \(RawIni ini) -> - case lkp (normalize name) ini of - Nothing -> return def - Just sec -> runExceptT thunk sec - ---- - -throw :: String -> StParser s a -throw msg = ExceptT (\_ -> Left msg) - -getSectionName :: StParser IniSection Text -getSectionName = ExceptT (return . isName) - -rawFieldMb :: Text -> StParser IniSection (Maybe IniValue) -rawFieldMb name = ExceptT $ \m -> - return (lkp (normalize name) (isVals m)) - -rawField :: Text -> StParser IniSection IniValue -rawField name = do - sec <- getSectionName - valMb <- rawFieldMb name - case valMb of - Nothing -> - throw - ( "Missing field " ++ show name - ++ " in section " - ++ show sec - ) - Just x -> return x - -getVal :: IniValue -> Text -getVal = T.strip . vValue - --- | Retrieve a field, failing if it doesn't exist, and return its raw value. --- --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (field "x") --- Right "hello" --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (field "y") --- Left "Missing field \"y\" in section \"MAIN\"" -field :: Text -> SectionParser Text -field name = SectionParser $ getVal `fmap` rawField name - --- | Retrieve a field and use the supplied parser to parse it as a value, --- failing if the field does not exist, or if the parser fails to --- produce a value. --- --- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldOf "x" number) --- Right 72 --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldOf "x" number) --- Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer" --- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldOf "y" number) --- Left "Missing field \"y\" in section \"MAIN\"" -fieldOf :: Text -> (Text -> Either String a) -> SectionParser a -fieldOf name parse = SectionParser $ do - sec <- getSectionName - val <- rawField name - case parse (getVal val) of - Left err -> addLineInformation (vLineNo val) sec (throw err) - Right x -> return x - --- | Retrieve a field, returning a @Nothing@ value if it does not exist. --- --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMb "x") --- Right (Just "hello") --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMb "y") --- Right Nothing -fieldMb :: Text -> SectionParser (Maybe Text) -fieldMb name = SectionParser $ fmap getVal `fmap` rawFieldMb name - --- | Retrieve a field and parse it according to the given parser, returning --- @Nothing@ if it does not exist. If the parser fails, then this will --- fail. --- --- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldMbOf "x" number) --- Right (Just 72) --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMbOf "x" number) --- Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer" --- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldMbOf "y" number) --- Right Nothing -fieldMbOf :: Text -> (Text -> Either String a) -> SectionParser (Maybe a) -fieldMbOf name parse = SectionParser $ do - sec <- getSectionName - mb <- rawFieldMb name - case mb of - Nothing -> return Nothing - Just v -> case parse (getVal v) of - Left err -> addLineInformation (vLineNo v) sec (throw err) - Right x -> return (Just x) - --- | Retrieve a field and supply a default value for if it doesn't exist. --- --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDef "x" "def") --- Right "hello" --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDef "y" "def") --- Right "def" -fieldDef :: Text -> Text -> SectionParser Text -fieldDef name def = SectionParser $ - ExceptT $ \m -> - case lkp (normalize name) (isVals m) of - Nothing -> return def - Just x -> return (getVal x) - --- | Retrieve a field, parsing it according to the given parser, and returning --- a default value if it does not exist. If the parser fails, then this will --- fail. --- --- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldDefOf "x" number 99) --- Right 72 --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDefOf "x" number 99) --- Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer" --- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldDefOf "y" number 99) --- Right 99 -fieldDefOf :: Text -> (Text -> Either String a) -> a -> SectionParser a -fieldDefOf name parse def = SectionParser $ do - sec <- getSectionName - mb <- rawFieldMb name - case mb of - Nothing -> return def - Just v -> case parse (getVal v) of - Left err -> addLineInformation (vLineNo v) sec (throw err) - Right x -> return x - --- | Retrieve a field and treat it as a boolean, failing if it --- does not exist. --- --- >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlag "x") --- Right True --- >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlag "y") --- Left "Missing field \"y\" in section \"MAIN\"" -fieldFlag :: Text -> SectionParser Bool -fieldFlag name = fieldOf name flag - --- | Retrieve a field and treat it as a boolean, subsituting --- a default value if it doesn't exist. --- --- >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlagDef "x" False) --- Right True --- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldFlagDef "x" False) --- Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a boolean" --- >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlagDef "y" False) --- Right False -fieldFlagDef :: Text -> Bool -> SectionParser Bool -fieldFlagDef name = fieldDefOf name flag - ---- - --- | Try to use the "Read" instance for a type to parse a value, failing --- with a human-readable error message if reading fails. --- --- >>> readable "(5, 7)" :: Either String (Int, Int) --- Right (5,7) --- >>> readable "hello" :: Either String (Int, Int) --- Left "Unable to parse \"hello\" as a value of type (Int,Int)" -readable :: forall a. (Read a, Typeable a) => Text -> Either String a -readable t = case readMaybe str of - Just v -> Right v - Nothing -> - Left - ( "Unable to parse " ++ show str - ++ " as a value of type " - ++ show typ - ) - where - str = T.unpack t - typ = typeRep prx - prx :: Proxy a - prx = Proxy - --- | Try to use the "Read" instance for a numeric type to parse a value, --- failing with a human-readable error message if reading fails. --- --- >>> number "5" :: Either String Int --- Right 5 --- >>> number "hello" :: Either String Int --- Left "Unable to parse \"hello\" as a value of type Int" -number :: (Num a, Read a, Typeable a) => Text -> Either String a -number = readable - --- | Convert a textual value to the appropriate string type. This will --- never fail. --- --- >>> string "foo" :: Either String String --- Right "foo" -string :: (IsString a) => Text -> Either String a -string = return . fromString . T.unpack - --- | Convert a string that represents a boolean to a proper boolean. This --- is case-insensitive, and matches the words @true@, @false@, @yes@, --- @no@, as well as single-letter abbreviations for all of the above. --- If the input does not match, then this will fail with a human-readable --- error message. --- --- >>> flag "TRUE" --- Right True --- >>> flag "y" --- Right True --- >>> flag "no" --- Right False --- >>> flag "F" --- Right False --- >>> flag "That's a secret!" --- Left "Unable to parse \"That's a secret!\" as a boolean" -flag :: Text -> Either String Bool -flag s = case T.toLower s of - "true" -> Right True - "yes" -> Right True - "t" -> Right True - "y" -> Right True - "false" -> Right False - "no" -> Right False - "f" -> Right False - "n" -> Right False - _ -> Left ("Unable to parse " ++ show s ++ " as a boolean") - --- | Convert a reader for a value into a reader for a list of those --- values, separated by a chosen separator. This will split apart --- the string on that separator, get rid of leading and trailing --- whitespace on the individual chunks, and then attempt to parse --- each of them according to the function provided, turning the --- result into a list. --- --- This is overloaded with the "IsList" typeclass, so it can be --- used transparently to parse other list-like types. --- --- >>> listWithSeparator "," number "2, 3, 4" :: Either String [Int] --- Right [2,3,4] --- >>> listWithSeparator " " number "7 8 9" :: Either String [Int] --- Right [7,8,9] --- >>> listWithSeparator ":" string "/bin:/usr/bin" :: Either String [FilePath] --- Right ["/bin","/usr/bin"] --- >>> listWithSeparator "," number "7 8 9" :: Either String [Int] --- Left "Unable to parse \"7 8 9\" as a value of type Int" -listWithSeparator :: - (IsList l) => - Text -> - (Text -> Either String (Item l)) -> - Text -> - Either String l -listWithSeparator sep rd = - fmap fromList . mapM (rd . T.strip) . T.splitOn sep - --- $setup --- --- >>> :{ --- data NetworkConfig = NetworkConfig --- { netHost :: String, netPort :: Int } --- deriving (Eq, Show) --- >>> :} --- --- >>> :{ --- data LocalConfig = LocalConfig --- { localUser :: Text } --- deriving (Eq, Show) --- >>> :} --- --- >>> :{ --- data Config = Config --- { cfNetwork :: NetworkConfig, cfLocal :: Maybe LocalConfig } --- deriving (Eq, Show) --- >>> :} --- --- >>> :{ --- let configParser = do --- netCf <- section "NETWORK" $ do --- host <- fieldOf "host" string --- port <- fieldOf "port" number --- return NetworkConfig { netHost = host, netPort = port } --- locCf <- sectionMb "LOCAL" $ --- LocalConfig <$> field "user" --- return Config { cfNetwork = netCf, cfLocal = locCf } --- >>> :} --- --- >>> :{ --- let example = "[NETWORK]\nhost = example.com\nport = 7878\n\n# here is a comment\n[LOCAL]\nuser = terry\n" --- >>> :} diff --git a/hs/Data/Ini/Config/KeyValue.hs b/hs/Data/Ini/Config/KeyValue.hs deleted file mode 100644 index d44dfc6..0000000 --- a/hs/Data/Ini/Config/KeyValue.hs +++ /dev/null @@ -1,15 +0,0 @@ -module Data.Ini.Config.KeyValue - ( kvParser - ) where - - -import Data.Ini.Config -import Data.Ini.Config.Raw -import Data.Text -import Control.Monad.Trans.Except -import Data.Sequence (Seq) - -kvParser :: SectionParser (Seq (Text,Text)) -kvParser = SectionParser $ - ExceptT $ \m -> - return $ (\(_, v) -> (vName v, vValue v)) <$> (isVals m) diff --git a/hs/Data/Ini/Config/URIDict.hs b/hs/Data/Ini/Config/URIDict.hs deleted file mode 100644 index 9f35b0e..0000000 --- a/hs/Data/Ini/Config/URIDict.hs +++ /dev/null @@ -1,27 +0,0 @@ -{-# LANGUAGE ImportQualifiedPost #-} - -module Data.Ini.Config.URIDict - ( uriDictParser - ) where - -import Control.Lens -import Data.Foldable (toList) -import Data.Ini.Config -import Data.Ini.Config.KeyValue -import Data.Map (Map) -import Data.Map qualified as Map -import Data.Maybe (isJust, fromJust) -import Data.Text hiding (filter) -import Network.URI (URI, parseURI) - -uriDictParser :: SectionParser (Map String URI) --- uriDictParser = fromList . filter (isJust . snd) . fmap (_2 %~ parseURI) $ kvParser --- uriDictParser = fmap kvParser -uriDictParser = fmap (Map.fromList) - $ fmap (fmap (_2 %~ fromJust)) - $ fmap (filter (isJust . snd)) - $ fmap toList - $ fmap (fmap (_1 %~ unpack . strip)) - $ fmap (fmap (_2 %~ parseURI . unpack . strip)) - -- $ fmap (fmap (_2 .~ Just nullURI)) - $ kvParser @@ -102,7 +102,7 @@ resolveInterwiki :: Map String URI -> String -> URI -> String resolveInterwiki wikilinks wikiname uri = case M.lookup wikiname wikilinks of Just uri' -> show $ uri' & uriPathLens %~ (<> fixExtension (uriPath uri)) - Nothing -> error $ "Unknown Wiki name: " <> wikiname <> "'" + Nothing -> error $ "Unknown Wiki name: " <> wikiname <> "" @@ -253,8 +253,9 @@ _ .~? Nothing = id lens .~? (Just x) = lens .~ x infixr 4 .~? -stripTrailing :: Eq a => a -> [a] -> [a] +stripTrailing :: (Show a, Eq a) => a -> [a] -> [a] stripTrailing c s + | null s = s | c == last s = init s | otherwise = s diff --git a/hs/vimwiki.cabal b/hs/vimwiki.cabal index 04d37c5..c52c479 100644 --- a/hs/vimwiki.cabal +++ b/hs/vimwiki.cabal @@ -32,29 +32,23 @@ executable Main Links, Html, Handlingar, - Data.Ini.Config, - Data.Ini.Config.KeyValue, - Data.Ini.Config.URIDict, Vimwiki.Man, System.Home, Util build-depends: + base >= 4.8, blaze-html >= 0.9, + containers >= 0.5, data-default >= 0.7, directory >= 1.3.6, filepath >= 1.4.2, + ini >= 0.4.2, lens >= 5.1, network-uri >= 2.6.4, optparse-applicative >= 0.17, pandoc >= 2.19, pandoc-types >= 1.22, + text >= 1.2.2, unix >= 2.7.2, - xdg-basedir >= 0.2, - -- These are locked by config-ini, since Data.Ini.Config - -- is a vendored (and barely changed) file - config-ini == 0.2.5.0, - base (>=4.8 && <5), - containers (>=0.5 && <0.7), - text (>=1.2.2 && <2.1), - transformers (>=0.4.1 && <0.6) + xdg-basedir >= 0.2 default-language: Haskell2010 |