Skip to content

Commit 99ced02

Browse files
Configure sections and queues in a single YAML file
1 parent 762ac5b commit 99ced02

File tree

5 files changed

+148
-109
lines changed

5 files changed

+148
-109
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,14 @@ following conventions:
2626

2727
## Unreleased
2828

29+
### Breaking
30+
31+
* Configure sections and queues in a single YAML file
32+
2933
### Non-Breaking
3034

35+
* Add a template option
36+
* Add an output option
3137
* Refactor `Makefile`, add `STACK_NIX_PATH` support
3238
* Add `test-all` command to `Makefile`
3339
* Add Nix configuration

example/podcasts.pdf

-214 Bytes
Binary file not shown.

example/podcasts.yaml

Lines changed: 57 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,59 @@
11
---
22

3-
- name: AI Podcast
4-
section: Other
5-
date: 2020-01-17
6-
prev: 72
7-
8-
- name: Changelog
9-
section: Programming
10-
tags:
11-
- partial
12-
date: 2020-01-20
13-
prev: 377
14-
15-
- name: Co-Recursive
16-
section: Functional
17-
date: 2020-01-16
18-
prev: 44
19-
20-
- name: Functional Geekery
21-
section: Functional
22-
date: 2019-12-24
23-
prev: 129
24-
25-
- name: Haskell Weekly
26-
section: Functional
27-
date: 2019-11-08
28-
prev: 24
29-
30-
- name: LambdaCast
31-
section: Functional
32-
date: 2019-06-14
33-
prev: 22
34-
35-
- name: Mindscape
36-
section: Other
37-
date: 2020-01-20
38-
next:
39-
- 78
40-
- 79
41-
- 80
42-
43-
- name: Quanta
44-
section: Other
45-
prev: 128
46-
47-
- name: Software Engineering Radio
48-
section: Programming
49-
tags:
50-
- partial
51-
date: 2020-01-10
52-
prev: 395
3+
sections:
4+
- Functional
5+
- Programming
6+
- Other
7+
8+
queues:
9+
10+
- name: AI Podcast
11+
section: Other
12+
date: 2020-01-17
13+
prev: 72
14+
15+
- name: Changelog
16+
section: Programming
17+
tags:
18+
- partial
19+
date: 2020-01-20
20+
prev: 377
21+
22+
- name: Co-Recursive
23+
section: Functional
24+
date: 2020-01-16
25+
prev: 44
26+
27+
- name: Functional Geekery
28+
section: Functional
29+
date: 2019-12-24
30+
prev: 129
31+
32+
- name: Haskell Weekly
33+
section: Functional
34+
date: 2019-11-08
35+
prev: 24
36+
37+
- name: LambdaCast
38+
section: Functional
39+
date: 2019-06-14
40+
prev: 22
41+
42+
- name: Mindscape
43+
section: Other
44+
date: 2020-01-20
45+
next:
46+
- 78
47+
- 79
48+
- 80
49+
50+
- name: Quanta
51+
section: Other
52+
prev: 128
53+
54+
- name: Software Engineering Radio
55+
section: Programming
56+
tags:
57+
- partial
58+
date: 2020-01-10
59+
prev: 395

example/sections.yaml

Lines changed: 0 additions & 5 deletions
This file was deleted.

queue-sheet.hs

Lines changed: 85 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,10 @@ import qualified Data.Aeson as A
2525
import Data.Aeson (FromJSON(parseJSON), (.:), (.:?), (.!=))
2626
import qualified Data.Aeson.Types as AT
2727

28-
-- https://hackage.haskell.org/package/ansi-wl-pprint
29-
import Text.PrettyPrint.ANSI.Leijen (Doc)
30-
3128
-- https://hackage.haskell.org/package/base
29+
import Control.Applicative (optional)
3230
import Control.Monad (forM_, unless, when)
31+
import Data.Maybe (fromMaybe)
3332
#if !MIN_VERSION_base (4,11,0)
3433
import Data.Monoid ((<>))
3534
#endif
@@ -86,6 +85,10 @@ import qualified LibOA
8685
buildDir :: FilePath
8786
buildDir = "queue-sheet-build"
8887

88+
-- | Default template file
89+
defaultTemplate :: FilePath
90+
defaultTemplate = "template.tex"
91+
8992
------------------------------------------------------------------------------
9093
-- $Types
9194

@@ -180,6 +183,29 @@ instance FromJSON Queue where
180183
(Nothing, Nothing) -> Nothing
181184
return Queue{..}
182185

186+
-- | Queues file
187+
data QueuesFile
188+
= QueuesFile
189+
{ qfSections :: ![Section]
190+
, qfQueues :: ![Queue]
191+
}
192+
deriving Show
193+
194+
instance FromJSON QueuesFile where
195+
parseJSON = A.withObject "QueuesFile" $ \o ->
196+
QueuesFile
197+
<$> o .: "sections"
198+
<*> o .: "queues"
199+
200+
------------------------------------------------------------------------------
201+
-- $Library
202+
203+
-- | Display an error and exit the program
204+
errorExit :: String -> IO a
205+
errorExit msg = do
206+
putStrLn $ "error: " ++ msg
207+
exitFailure
208+
183209
-- | Parse any scalar value as a string
184210
--
185211
-- Strings, numbers, booleans, and null are parsed as a string. Arrays and
@@ -215,31 +241,19 @@ escapeTeX = T.foldl go ""
215241
------------------------------------------------------------------------------
216242
-- $Yaml
217243

218-
-- | Load @sections.yaml@
219-
loadSectionsYaml :: FilePath -> IO [Section]
220-
loadSectionsYaml = loadYamlFile
221-
222244
-- | Load @queues.yaml@
223-
loadQueuesYaml :: FilePath -> [Section] -> IO [Queue]
224-
loadQueuesYaml path sections = do
225-
queues <- loadYamlFile path
226-
forM_ queues $ \Queue{..} ->
227-
unless (queueSection `elem` sections) . errorExit . unwords $
245+
loadQueuesYaml :: FilePath -> IO QueuesFile
246+
loadQueuesYaml path = do
247+
let yamlError = errorExit
248+
. (("error loading " ++ path ++ ": ") ++)
249+
. Yaml.prettyPrintParseException
250+
qf@QueuesFile{..} <- either yamlError pure =<< Yaml.decodeFileEither path
251+
forM_ qfQueues $ \Queue{..} ->
252+
unless (queueSection `elem` qfSections) . errorExit . unwords $
228253
[ "queue", TTC.render queueName
229254
, "has unknown section", TTC.render queueSection
230255
]
231-
return queues
232-
233-
-- | Load a YAML file
234-
--
235-
-- If there is an error, the error is displayed and the program is exited.
236-
loadYamlFile :: FromJSON a => FilePath -> IO [a]
237-
loadYamlFile path = do
238-
eer <- Yaml.decodeFileEither path
239-
case eer of
240-
Right result -> return result
241-
Left err -> errorExit $
242-
"error loading " ++ path ++ ": " ++ Yaml.prettyPrintParseException err
256+
return qf
243257

244258
------------------------------------------------------------------------------
245259
-- $Template
@@ -361,54 +375,71 @@ build path = Proc.callProcess "xelatex" ["-halt-on-error", path]
361375
------------------------------------------------------------------------------
362376
-- $CLI
363377

364-
-- | Display an error and exit the program
365-
errorExit :: String -> IO a
366-
errorExit msg = do
367-
putStrLn $ "error: " ++ msg
368-
exitFailure
378+
-- | Program options
379+
data Options
380+
= Options
381+
{ optTemplate :: !FilePath
382+
, optOutput :: !(Maybe FilePath)
383+
, optQueues :: !FilePath
384+
}
385+
deriving Show
369386

370-
-- | CLI parser information
371-
pinfo :: OA.ParserInfo FilePath
372-
pinfo
373-
= OA.info (LibOA.helper <*> LibOA.versioner version <*> queuesYaml)
387+
-- Parse program options
388+
parseOptions :: IO Options
389+
parseOptions = OA.execParser
390+
$ OA.info (LibOA.helper <*> LibOA.versioner version <*> options)
374391
$ mconcat
375392
[ OA.fullDesc
376393
, OA.progDesc "queue sheet utility"
377394
, OA.failureCode 2
378-
, OA.footerDoc $ Just filesHelp
379395
]
380396
where
381397
version :: String
382398
version = "queue-sheet-haskell " ++ showVersion Project.version
383399

384-
queuesYaml :: OA.Parser FilePath
385-
queuesYaml = OA.strArgument $ mconcat
386-
[ OA.metavar "QUEUES.yaml"
387-
, OA.help "YAML file specifying queue information"
400+
options :: OA.Parser Options
401+
options = Options
402+
<$> templateOption
403+
<*> optional outputOption
404+
<*> queuesArgument
405+
406+
templateOption :: OA.Parser FilePath
407+
templateOption = OA.strOption $ mconcat
408+
[ OA.long "template"
409+
, OA.short 't'
410+
, OA.metavar "TEMPLATE.tex"
411+
, OA.value defaultTemplate
412+
, OA.showDefaultWith id
413+
, OA.help "template file"
388414
]
389415

390-
filesHelp :: Doc
391-
filesHelp = LibOA.section "Files:" $ LibOA.table
392-
[ ("sections.yaml", "specify section names and order")
393-
, ("QUEUES.yaml", "specify queues information")
394-
, ("template.tex", "queue sheet template")
395-
, ("QUEUES.pdf", "output queue sheet")
416+
outputOption :: OA.Parser FilePath
417+
outputOption = OA.strOption $ mconcat
418+
[ OA.long "output"
419+
, OA.short 'o'
420+
, OA.metavar "QUEUES.pdf"
421+
, OA.help "output file"
422+
]
423+
424+
queuesArgument :: OA.Parser FilePath
425+
queuesArgument = OA.strArgument $ mconcat
426+
[ OA.metavar "QUEUES.yaml"
427+
, OA.help "YAML file specifying queue information"
396428
]
397429

398430
-- | Main function
399431
main :: IO ()
400432
main = do
401-
queuesYaml <- OA.execParser pinfo
402-
sections <- loadSectionsYaml "sections.yaml"
403-
queues <- loadQueuesYaml queuesYaml sections
404-
template <- loadTemplate "template.tex"
405-
exists <- doesPathExist buildDir
433+
Options{..} <- parseOptions
434+
QueuesFile{..} <- loadQueuesYaml optQueues
435+
template <- loadTemplate optTemplate
436+
exists <- doesPathExist buildDir
406437
when exists . errorExit $ "directory already exists: " ++ buildDir
407-
let queuesTex = replaceExtension queuesYaml "tex"
408-
queuesPdf = replaceExtension queuesYaml "pdf"
438+
let pdfFile = fromMaybe (replaceExtension optQueues "pdf") optOutput
439+
texFile = replaceExtension pdfFile "tex"
409440
createDirectory buildDir
410441
withCurrentDirectory buildDir $ do
411-
renderTemplate queuesTex template $ context sections queues
412-
build queuesTex
413-
renameFile (buildDir </> queuesPdf) queuesPdf
442+
renderTemplate texFile template $ context qfSections qfQueues
443+
build texFile
444+
renameFile (buildDir </> pdfFile) pdfFile
414445
removeDirectoryRecursive buildDir

0 commit comments

Comments
 (0)