Skip to content

Commit

Permalink
fmt: support formatting track config (#772)
Browse files Browse the repository at this point in the history
And add integration tests for `fmt`.

Refs: #478
Closes: #479
  • Loading branch information
ErikSchierboom authored Oct 5, 2023
1 parent 4015ff9 commit 6691484
Show file tree
Hide file tree
Showing 8 changed files with 685 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ jobs:
- name: Checkout code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608

- name: Configure the git user # Required to create a commit in our binary tests
run: |
git config --global user.email "[email protected]"
git config --global user.name "Exercism Bot"
- name: On Linux, install musl
if: matrix.os == 'linux'
run: ./.github/bin/linux-install-musl
Expand Down
19 changes: 19 additions & 0 deletions src/exec.nim
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ proc gitCheckout(dir, hash: string) =
let args = ["-C", dir, "checkout", "--force", hash]
discard gitCheck(0, args)

proc fixAverageRunTimeInConfigJson(file, oldValue, newValue: string) =
## For testing purposes, we clone track repos and checkout commits
## where the `average_run_time` value in the `config.json` file
## is a float, which has later since changed to an int.
## In order to not break existing tests, we manually change the value
## from a float to an int.
let configJson = readFile(file)
.replace(
&""""average_run_time": {oldValue}""",
&""""average_run_time": {newValue}""")
writeFile(file, configJson)
let args = ["-C", parentDir(file), "commit", "-a", "-m", "config: convert `average_run_time` to int"]
discard gitCheck(0, args)

proc setupExercismRepo*(repoName, dest, hash: string; shallow = false) =
## If there is no directory at `dest`, clones the Exercism repo named
## `repoName` to `dest`.
Expand All @@ -117,6 +131,11 @@ proc setupExercismRepo*(repoName, dest, hash: string; shallow = false) =
cloneExercismRepo(repoName, dest, shallow)
gitCheckout(dest, hash)

if repoName == "nim":
fixAverageRunTimeInConfigJson(dest / "config.json", "3.0", "3")
elif repoName == "elixir":
fixAverageRunTimeInConfigJson(dest / "config.json", "4.1", "4")

func conciseDiff(s: string): string =
## Returns the lines of `s` that begin with a `+` or `-` character.
result = newStringOfCap(s.len)
Expand Down
49 changes: 33 additions & 16 deletions src/fmt/fmt.nim
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import std/[os, strformat, strutils]
import "."/[approaches, articles, exercises]
import "."/[approaches, articles, exercises, track_config]
import ".."/[cli, helpers, logger, sync/sync_common, sync/sync,
types_exercise_config, types_track_config]

type
DocumentKind* = enum
dkExerciseConfig,
dkTrackConfig,
dkConceptExerciseConfig,
dkPracticeExerciseConfig,
dkApproachesConfig,
dkArticlesConfig

Expand All @@ -15,11 +17,23 @@ type
formattedDocument: string

iterator getConfigPaths(trackExerciseSlugs: TrackExerciseSlugs,
trackExercisesDir: string): (ExerciseKind, DocumentKind, string) =
conf: Conf): (DocumentKind, string) =
let trackDir = conf.trackDir

## Yield the track's `config.json` file, but only when the user
## is not formatting a single exercise
if conf.action.exerciseFmt.len == 0:
yield (dkTrackConfig, trackDir / "config.json")

## Yields the `.meta/config.json`, `.approaches/config.json` and
## `.articles/config.json` paths for each exercise in
## `trackExerciseSlugs` in `trackExercisesDir`.
let trackExercisesDir = trackDir / "exercises"
for exerciseKind in [ekConcept, ekPractice]:
let documentKind =
case exerciseKind
of ekConcept: dkConceptExerciseConfig
of ekPractice: dkPracticeExerciseConfig
let slugs =
case exerciseKind
of ekConcept: trackExerciseSlugs.`concept`
Expand All @@ -34,34 +48,37 @@ iterator getConfigPaths(trackExerciseSlugs: TrackExerciseSlugs,
for slug in slugs:
trackExerciseConfigPath.truncateAndAdd(startLen, slug)
trackExerciseConfigPath.addExerciseConfigPath()
yield (exerciseKind, dkExerciseConfig, trackExerciseConfigPath)
yield (documentKind, trackExerciseConfigPath)

trackExerciseConfigPath.truncateAndAdd(startLen, slug)
trackExerciseConfigPath.addApproachesConfigPath()
if fileExists(trackExerciseConfigPath):
yield (exerciseKind, dkApproachesConfig, trackExerciseConfigPath)
yield (dkApproachesConfig, trackExerciseConfigPath)

trackExerciseConfigPath.truncateAndAdd(startLen, slug)
trackExerciseConfigPath.addArticlesConfigPath()
if fileExists(trackExerciseConfigPath):
yield (exerciseKind, dkArticlesConfig, trackExerciseConfigPath)
yield (dkArticlesConfig, trackExerciseConfigPath)

proc fmtImpl(trackExerciseSlugs: TrackExerciseSlugs,
trackDir: string): seq[PathAndFormattedDocument] =
## Reads the config files for every slug in `trackExerciseSlugs`
## in `trackExerciseDir`.
conf: Conf): seq[PathAndFormattedDocument] =
## Reads the track config file and all exercise config files
## for every slug in `trackExerciseSlugs` in `trackExerciseDir`.
## This includes `.meta/config.json`, `.approaches/config.json`
## and `.articles/config.json`.
## and `.articles/config.json` for each exercise, and `config.json`
## for the track.
##
## Returns a seq of (document kind, path, formatted document) objects
## containing every exercise's configs that are not already formatted.
let trackExercisesDir = trackDir / "exercises"
## containing every document that is not already formatted.
var seenUnformatted = false
for (exerciseKind, documentKind, configPath) in getConfigPaths(trackExerciseSlugs,
trackExercisesDir):
let trackDir = conf.trackDir
for (documentKind, configPath) in getConfigPaths(trackExerciseSlugs,
conf):
let formatted =
case documentKind
of dkExerciseConfig: formatExerciseConfigFile(exerciseKind, configPath)
of dkTrackConfig: formatTrackConfigFile(configPath)
of dkConceptExerciseConfig: formatExerciseConfigFile(ekConcept, configPath)
of dkPracticeExerciseConfig: formatExerciseConfigFile(ekPractice, configPath)
of dkApproachesConfig: formatApproachesConfigFile(configPath)
of dkArticlesConfig: formatArticlesConfigFile(configPath)

Expand Down Expand Up @@ -116,7 +133,7 @@ proc fmt*(conf: Conf) =
logNormal("Looking for exercises that lack a formatted '.meta/config.json', " &
"'.approaches/config.json'\nor '.articles/config.json' file...")

let pairs = fmtImpl(trackExerciseSlugs, conf.trackDir)
let pairs = fmtImpl(trackExerciseSlugs, conf)

let userExercise = conf.action.exerciseFmt
if pairs.len > 0:
Expand Down
Loading

0 comments on commit 6691484

Please sign in to comment.