Skip to content

Commit

Permalink
create: allow creating of exercise (#845)
Browse files Browse the repository at this point in the history
Make `configlet create` support creating a practice exercise or
concept exercise, using the new syntax:

    configlet create --practice-exercise <slug>
    configlet create --concept-exercise <slug>

This also supports the `--offline` flag.

Closes: #675
  • Loading branch information
ErikSchierboom authored Jan 19, 2024
1 parent 4eeb24f commit 8c3e63e
Show file tree
Hide file tree
Showing 16 changed files with 382 additions and 75 deletions.
55 changes: 29 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Usage:
Commands:
completion Output a completion script for a given shell
create Add a new approach or article
create Add a new exercise, approach or article
fmt Format the exercise 'config.json' files
generate Generate Concept Exercise 'introduction.md' files from 'introduction.md.tpl' files
info Print some information about the track
Expand All @@ -65,43 +65,46 @@ Commands:
uuid Output new (version 4) UUIDs, suitable for the value of a 'uuid' key
Options for completion:
-s, --shell <shell> Choose the shell type (required)
Allowed values: b[ash], f[ish], z[sh]
-s, --shell <shell> Choose the shell type (required)
Allowed values: b[ash], f[ish], z[sh]
Options for create:
--approach <slug> The slug of the approach
--article <slug> The slug of the article
-e, --exercise <slug> Only operate on this exercise
--approach <slug> The slug of the approach
--article <slug> The slug of the article
--practice-exercise <slug> The slug of the concept exercise
--concept-exercise <slug> The slug of the practice exercise
-e, --exercise <slug> Only operate on this exercise
-o, --offline Do not update the cached 'problem-specifications' data
Options for fmt:
-e, --exercise <slug> Only operate on this exercise
-u, --update Prompt to write formatted files
-y, --yes Auto-confirm the prompt from --update
-e, --exercise <slug> Only operate on this exercise
-u, --update Prompt to write formatted files
-y, --yes Auto-confirm the prompt from --update
Options for info:
-o, --offline Do not update the cached 'problem-specifications' data
-o, --offline Do not update the cached 'problem-specifications' data
Options for sync:
-e, --exercise <slug> Only operate on this exercise
-o, --offline Do not update the cached 'problem-specifications' data
-u, --update Prompt to update the unsynced track data
-y, --yes Auto-confirm prompts from --update for updating docs, filepaths, and metadata
--docs Sync Practice Exercise '.docs/introduction.md' and '.docs/instructions.md' files
--filepaths Populate empty 'files' values in Concept/Practice exercise '.meta/config.json' files
--metadata Sync Practice Exercise '.meta/config.json' metadata values
--tests [mode] Sync Practice Exercise '.meta/tests.toml' files.
The mode value specifies how missing tests are handled when using --update.
Allowed values: c[hoose], i[nclude], e[xclude] (default: choose)
-e, --exercise <slug> Only operate on this exercise
-o, --offline Do not update the cached 'problem-specifications' data
-u, --update Prompt to update the unsynced track data
-y, --yes Auto-confirm prompts from --update for updating docs, filepaths, and metadata
--docs Sync Practice Exercise '.docs/introduction.md' and '.docs/instructions.md' files
--filepaths Populate empty 'files' values in Concept/Practice exercise '.meta/config.json' files
--metadata Sync Practice Exercise '.meta/config.json' metadata values
--tests [mode] Sync Practice Exercise '.meta/tests.toml' files.
The mode value specifies how missing tests are handled when using --update.
Allowed values: c[hoose], i[nclude], e[xclude] (default: choose)
Options for uuid:
-n, --num <int> Number of UUIDs to output
-n, --num <int> Number of UUIDs to output
Global options:
-h, --help Show this help message and exit
--version Show this tool's version information and exit
-t, --track-dir <dir> Specify a track directory to use instead of the current directory
-v, --verbosity <verbosity> The verbosity of output.
Allowed values: q[uiet], n[ormal], d[etailed] (default: normal)
-h, --help Show this help message and exit
--version Show this tool's version information and exit
-t, --track-dir <dir> Specify a track directory to use instead of the current directory
-v, --verbosity <verbosity> The verbosity of output.
Allowed values: q[uiet], n[ormal], d[etailed] (default: normal)
```

## `configlet lint`
Expand Down
6 changes: 6 additions & 0 deletions completions/configlet.bash
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ _configlet_complete_create_() {
'-e' | '--exercise')
_configlet_complete_slugs_ "practice" "concept"
;;
'--concept-exercise')
_configlet_complete_slugs_ "concept"
;;
'--practice-exercise')
_configlet_complete_slugs_ "practice"
;;
*)
_configlet_complete_options_ "--approach --article -e --exercise $global_opts"
;;
Expand Down
2 changes: 1 addition & 1 deletion completions/configlet.fish
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ complete -c configlet -n "__fish_use_subcommand" -a lint -d "Check the tra

# subcommands with options
complete -c configlet -n "__fish_use_subcommand" -a completion -d "Output a completion script for a given shell"
complete -c configlet -n "__fish_use_subcommand" -a create -d "Add a new approach or article"
complete -c configlet -n "__fish_use_subcommand" -a create -d "Add a new exercise, approach or article"
complete -c configlet -n "__fish_use_subcommand" -a fmt -d "Format the exercise '.meta/config.json' files"
complete -c configlet -n "__fish_use_subcommand" -a info -d "Track info"
complete -c configlet -n "__fish_use_subcommand" -a sync -d "Check or update Practice Exercise docs, metadata, and tests"
Expand Down
2 changes: 1 addition & 1 deletion completions/configlet.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ _configlet_commands() {
"lint:Check the track configuration for correctness" \
# subcommands with options
"completion:Output a completion script for a given shell" \
"create:Add a new approach or article" \
"create:Add a new exercise, approach or article" \
"fmt:Format the exercise '.meta/config.json' files" \
"info:Print track information" \
"sync:Check or update Practice Exercise docs, metadata, and tests" \
Expand Down
40 changes: 31 additions & 9 deletions src/cli.nim
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@ type
of actCreate:
approachSlug*: string
articleSlug*: string
practiceExerciseSlug*: string
conceptExerciseSlug*: string
# We can't name this field `exercise` because we use that names
# in `actSync`, and Nim doesn't yet support duplicate field names
# in object variants.
exerciseCreate*: string
offlineCreate*: bool
of actFmt:
# We can't name these fields `exercise`, `update`, and `yes` because we
# use those names in `actSync`, and Nim doesn't yet support duplicate
Expand Down Expand Up @@ -83,6 +86,8 @@ type
# Options for `create`
optCreateApproach = "approach"
optCreateArticle = "article"
optCreateConceptExercise = "conceptExercise"
optCreatePracticeExercise = "practiceExercise"

# Options for `completion`
optCompletionShell = "shell"
Expand All @@ -94,8 +99,8 @@ type
optFmtSyncUpdate = "update"
optFmtSyncYes = "yes"

# Options for both `info` and `sync`
optInfoSyncOffline = "offline"
# Options for both `info`, `sync` and `create`
optInfoSyncCreateOffline = "offline"

# Scope to sync
optSyncDocs = "docs"
Expand All @@ -110,7 +115,8 @@ func genShortKeys: array[Opt, char] =
## Returns a lookup that gives the valid short option key for an `Opt`.
for opt in Opt:
if opt in {optVersion, optSyncDocs, optSyncFilepaths, optSyncMetadata,
optSyncTests, optCreateApproach, optCreateArticle}:
optSyncTests, optCreateApproach, optCreateArticle,
optCreateConceptExercise, optCreatePracticeExercise}:
result[opt] = '_' # No short option for these options.
else:
result[opt] = ($opt)[0]
Expand All @@ -119,7 +125,7 @@ const
configletVersion = staticRead("../configlet.version").strip()
short = genShortKeys()
optsNoVal = {optHelp, optVersion, optFmtSyncUpdate, optFmtSyncYes,
optInfoSyncOffline, optSyncDocs, optSyncFilepaths, optSyncMetadata}
optInfoSyncCreateOffline, optSyncDocs, optSyncFilepaths, optSyncMetadata}

func generateNoVals: tuple[shortNoVal: set[char], longNoVal: seq[string]] =
## Returns the short and long keys for the options in `optsNoVal`.
Expand Down Expand Up @@ -179,6 +185,8 @@ func genHelpText: string =
of optFmtSyncCreateExercise: "slug"
of optCreateApproach: "slug"
of optCreateArticle: "slug"
of optCreateConceptExercise: "slug"
of optCreatePracticeExercise: "slug"
of optSyncTests: "mode"
of optUuidNum: "int"
else: ""
Expand Down Expand Up @@ -206,7 +214,7 @@ func genHelpText: string =
const actionDescriptions: array[ActionKind, string] = [
actNil: "",
actCompletion: "Output a completion script for a given shell",
actCreate: "Add a new approach or article",
actCreate: "Add a new exercise, approach or article",
actFmt: "Format the exercise 'config.json' files",
actGenerate: "Generate Concept Exercise 'introduction.md' files from 'introduction.md.tpl' files",
actInfo: "Print some information about the track",
Expand Down Expand Up @@ -234,12 +242,14 @@ func genHelpText: string =
&"{paddingOpt}{allowedValues(Verbosity)} (default: normal)",
optCreateApproach: "The slug of the approach",
optCreateArticle: "The slug of the article",
optCreateConceptExercise: "The slug of the practice exercise",
optCreatePracticeExercise: "The slug of the concept exercise",
optCompletionShell: &"Choose the shell type (required)\n" &
&"{paddingOpt}{allowedValues(Shell)}",
optFmtSyncCreateExercise: "Only operate on this exercise",
optFmtSyncUpdate: "Prompt to update the unsynced track data",
optFmtSyncYes: &"Auto-confirm prompts from --{$optFmtSyncUpdate} for updating docs, filepaths, and metadata",
optInfoSyncOffline: "Do not update the cached 'problem-specifications' data",
optInfoSyncCreateOffline: "Do not update the cached 'problem-specifications' data",
optSyncDocs: "Sync Practice Exercise '.docs/introduction.md' and '.docs/instructions.md' files",
optSyncFilepaths: "Populate empty 'files' values in Concept/Practice exercise '.meta/config.json' files",
optSyncMetadata: "Sync Practice Exercise '.meta/config.json' metadata values",
Expand Down Expand Up @@ -291,6 +301,10 @@ func genHelpText: string =
optCreateApproach
of "articleSlug":
optCreateArticle
of "conceptExerciseSlug":
optCreateConceptExercise
of "practiceExerciseSlug":
optCreatePracticeExercise
of "exerciseCreate":
optFmtSyncCreateExercise
of "exerciseFmt":
Expand All @@ -300,7 +314,9 @@ func genHelpText: string =
of "yesFmt":
optFmtSyncYes
of "offlineInfo":
optInfoSyncOffline
optInfoSyncCreateOffline
of "offlineCreate":
optInfoSyncCreateOffline
else:
parseEnum[Opt](key)
# Set the description for `fmt` options.
Expand Down Expand Up @@ -530,6 +546,12 @@ proc handleOption(conf: var Conf; kind: CmdLineKind; key, val: string) =
setActionOpt(articleSlug, val)
of optFmtSyncCreateExercise:
setActionOpt(exerciseCreate, val)
of optCreateConceptExercise:
setActionOpt(conceptExerciseSlug, val)
of optCreatePracticeExercise:
setActionOpt(practiceExerciseSlug, val)
of optInfoSyncCreateOffline:
setActionOpt(offlineCreate, true)
else:
discard
of actFmt:
Expand All @@ -544,7 +566,7 @@ proc handleOption(conf: var Conf; kind: CmdLineKind; key, val: string) =
discard
of actInfo:
case opt
of optInfoSyncOffline:
of optInfoSyncCreateOffline:
setActionOpt(offlineInfo, true)
else:
discard
Expand All @@ -559,7 +581,7 @@ proc handleOption(conf: var Conf; kind: CmdLineKind; key, val: string) =
of optSyncTests:
setActionOpt(tests, parseVal[TestsMode](kind, key, val))
conf.action.scope.incl skTests
of optInfoSyncOffline:
of optInfoSyncCreateOffline:
setActionOpt(offline, true)
of optSyncDocs, optSyncMetadata, optSyncFilepaths:
conf.action.scope.incl parseEnum[SyncKind]($opt)
Expand Down
21 changes: 15 additions & 6 deletions src/create/create.nim
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import std/[os, strformat]
import ".."/[cli, helpers, sync/sync, types_track_config]
import "."/[approaches, articles]
import "."/[approaches, articles, exercises]

proc create*(conf: Conf) =
if conf.action.kind == actCreate:
if conf.action.exerciseCreate.len == 0:
let msg = "Please specify an exercise, using --exercise <slug>"
stderr.writeLine msg
quit QuitFailure
if conf.action.approachSlug.len > 0:
if conf.action.exerciseCreate.len == 0:
let msg = "Please specify an exercise to create an approach for, using --exercise <slug>"
stderr.writeLine msg
quit QuitFailure
if conf.action.articleSlug.len > 0:
let msg = &"Both --approach and --article were provided. Please specify only one."
stderr.writeLine msg
Expand All @@ -32,6 +32,10 @@ proc create*(conf: Conf) =

createApproach(Slug(conf.action.approachSlug), userExercise, exerciseDir)
elif conf.action.articleSlug.len > 0:
if conf.action.exerciseCreate.len == 0:
let msg = "Please specify an exercise to create an article for, using --exercise <slug>"
stderr.writeLine msg
quit QuitFailure
let trackConfigPath = conf.trackDir / "config.json"
let trackConfig = parseFile(trackConfigPath, TrackConfig)
let trackExerciseSlugs = getSlugs(trackConfig.exercises, conf, trackConfigPath)
Expand All @@ -50,8 +54,13 @@ proc create*(conf: Conf) =
quit QuitFailure

createArticle(Slug(conf.action.articleSlug), userExercise, exerciseDir)
elif conf.action.conceptExerciseSlug.len > 0:
createConceptExercise(conf)
elif conf.action.practiceExerciseSlug.len > 0:
createPracticeExercise(conf)
else:
let msg = "Please specify `--article <slug>` or `--approach <slug>`"
let msg = "Please specify `--practice-exercise <slug>`, `--concept-exercise <slug>`, " &
"`--article <slug>` or `--approach <slug>`"
stderr.writeLine msg
quit QuitFailure
else:
Expand Down
Loading

0 comments on commit 8c3e63e

Please sign in to comment.