packages feed

dot-linker-0.1.0.0: src/DotLinker/Parsers.hs

{-# LANGUAGE OverloadedStrings #-}
module DotLinker.Parsers
( lineMapParser
, envParser
, fileMapParser
, Entry
, EnvOrLit(..)
) where

import Control.Applicative
import Data.Attoparsec.ByteString.Char8
import Data.ByteString (ByteString)
import Data.Maybe (catMaybes)
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import Filesystem.Path.CurrentOS (FilePath, decode)
import Prelude hiding (takeWhile, FilePath)

type Entry = (Text, [FilePath])

-- | Parser for a single line in the file
--
-- Example:
--
-- vimrc: /home/rar/.vimrc,/home/rar/.config/nvim/init.vim
lineMapParser :: Parser Entry
lineMapParser = do
    src <- dotName <* char ':'
    ts <- filePath `sepBy1` char ','
    return (decodeUtf8 src, ts)
  where
    dotName = takeTill (== ':')
    filePath =
      strip $ decode <$> takeTill (inClass ",\n\r")

    strip str = many' space *> str <* many' space

-- | Parser for a whole file
fileMapParser :: Parser [Entry]
fileMapParser = catMaybes <$> optLineMapParser `manyTill` endOfInput
  where
    optLineMapParser = (string "-- " *> skip1 lineMapParser *> return Nothing) <|> (Just <$> lineMapParser)
    skip1 p = sc
      where sc = (p *> pure ()) <|> pure ()

data EnvOrLit = Env ByteString -- ^ Represents an enviroment variable
              | Lit ByteString -- ^ A literal path part

-- | Parser that parses a path with variables into parts
--
-- Example: $HOME/.dotfiles -> [Env HOME, Lit .dotfiles]
--
-- This will later be used by @expandPath@ to create:
--
-- [Env Home, Lit .dotfiles] -> /home/me/.dotfiles
envParser :: Parser [EnvOrLit]
envParser = secParser `sepBy1` char '/'
  where
    secParser =
      let env = char '$' *> takeWhile (inClass "a-zA-Z_") <?> "env variable"
          lit = takeTill (== '/') <?> "literal path part"
      in  (Env <$> env) <|> (Lit <$> lit)