blog/blog-meta/post-lists.hs

118 lines
3.5 KiB
Haskell
Raw Normal View History

2021-07-23 21:35:02 -04:00
import qualified Data.ByteString.Lazy as LazyBS
import Data.Char (toLower)
import Data.Function ((&))
import Data.List (sortBy)
import Data.Ord (comparing)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Time
2021-07-25 08:48:08 -04:00
import Misc
2021-07-23 21:35:02 -04:00
import qualified YAML
2024-09-15 11:46:06 -04:00
import YAML ((.:), (.:?), (.!=), (##=))
2021-07-23 21:35:02 -04:00
import qualified System.Console.GetOpt as GetOpt
import qualified System.FilePath.Find as Find
2021-07-25 08:48:08 -04:00
import qualified System.FilePath as Path
2021-07-23 21:35:02 -04:00
main :: IO ()
main = do
Opts title dir tag out <- getOptions
files <- Find.findL True (pure True) (Find.extension Find.==? ".md") dir
2021-07-25 08:48:08 -04:00
infos <- filter (checkTag tag) <$> traverse (getInfo dir) files
2021-07-23 21:35:02 -04:00
let content = makeContent title infos
case out of
Nothing -> LazyBS.putStr content
Just fn -> LazyBS.writeFile fn content
makeContent :: Text -> [PostInfo] -> LazyBS.ByteString
makeContent title is' = "---\n" <> YAML.encode1 val <> "...\n" where
is = sortBy (flip $ comparing infoDate) is'
val = YAML.obj [("title" ##= title), ("posts" ##= is)]
checkTag :: Maybe Text -> PostInfo -> Bool
checkTag Nothing _ = True
checkTag (Just t) i = t `elem` infoTags i
data Options =
Opts {
optsTitle :: !Text,
optsDir :: !FilePath,
optsTag :: !(Maybe Text),
optsOut :: !(Maybe FilePath)
}
getOptions :: IO Options
getOptions = getOptionsWith hdr defOpts optDescrs where
hdr prog = "usage: " <> prog <> " [OPTION...] DIR TITLE\n\
\ --- get info about posts in DIR and use given title"
optDescrs :: [GetOpt.OptDescr (Options -> Options)]
optDescrs =
[GetOpt.Option "t" ["tag"]
(GetOpt.ReqArg (\t o -> o {optsTag = Just $ Text.pack t}) "TAG")
"list only posts with the given tag",
GetOpt.Option "o" ["out"]
(GetOpt.ReqArg (\f o -> o {optsOut = Just f}) "FILE")
"write output to FILE"]
defOpts :: [String] -> Maybe Options
defOpts [dir, title] =
Just $ Opts {optsDir = dir, optsTitle = Text.pack title,
optsTag = Nothing, optsOut = Nothing}
defOpts _ = Nothing
2021-07-25 08:48:08 -04:00
getInfo :: FilePath -> FilePath -> IO PostInfo
getInfo dir file = do
2021-07-23 21:35:02 -04:00
yaml <- YAML.readHeader file
2021-07-25 08:48:08 -04:00
let dirs = Path.splitPath dir
let file' = Path.joinPath $ drop (length dirs) $ Path.splitPath file
2021-07-23 21:35:02 -04:00
unwrap file $ YAML.parseEither $
2024-09-15 11:46:06 -04:00
yaml & YAML.withMap "title, date, tags, summary?" \m ->
2021-07-25 08:48:08 -04:00
Info <$> pure file'
2024-09-15 11:46:06 -04:00
<*> m .: "title"
<*> m .: "date"
<*> m .: "tags" .!= []
<*> m .:? "summary"
2021-07-23 21:35:02 -04:00
-- | the front matter info we care about
data PostInfo =
Info {
2024-09-15 11:46:06 -04:00
infoFile :: FilePath,
infoTitle :: Text,
2021-07-23 21:35:02 -04:00
infoDate :: BlogDate,
2024-09-15 11:46:06 -04:00
infoTags :: [Text],
infoSummary :: Maybe Text
2021-07-23 21:35:02 -04:00
}
instance YAML.ToYAML PostInfo where
2024-09-15 11:46:06 -04:00
toYAML (Info file title date tags summary) = YAML.obj
[("date" ##= date),
2024-09-15 11:49:23 -04:00
("date-rss" ##= toRss date),
2024-09-15 11:46:06 -04:00
("title" ##= title),
("tags" ##= tags),
("file" ##= Text.pack (fixup file)),
("summary" ##= summary)]
2024-09-15 11:49:23 -04:00
where
fixup f = Path.replaceExtension f "html"
toRss (BD d) = RD d
2021-07-23 21:35:02 -04:00
2024-09-15 11:49:23 -04:00
newtype BlogDate = BD Day deriving (Eq, Ord)
newtype RssDate = RD Day deriving (Eq, Ord)
2021-07-23 21:35:02 -04:00
2024-09-15 11:49:23 -04:00
instance YAML.FromYAML RssDate where
2021-07-23 21:35:02 -04:00
parseYAML = YAML.withStr "YYYY-MM-DD" $
2024-09-15 11:49:23 -04:00
fmap RD . parseTimeM True defaultTimeLocale "%F" . Text.unpack
instance YAML.ToYAML RssDate where
toYAML (RD d) = YAML.str $ Text.pack $
formatTime defaultTimeLocale "%a, %d %b %Y 00:00:01 UT" d
instance YAML.FromYAML BlogDate where
parseYAML = fmap (\(RD d) -> BD d) . YAML.parseYAML
2021-07-23 21:35:02 -04:00
instance YAML.ToYAML BlogDate where
2024-09-15 11:49:23 -04:00
toYAML (BD d) = YAML.str $ Text.pack $ map toLower $
2021-07-23 21:35:02 -04:00
formatTime defaultTimeLocale "%a %-d %B %Y" d