-
-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
lint: begin linting concept exercise config.json
#169
Changes from 3 commits
41f7e4c
ea4f61e
b7f56e7
3a2041b
965cd23
b2e8ca6
f06c578
7e6f2ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import std/[json, os, terminal] | ||
import ".."/helpers | ||
import "."/validators | ||
|
||
proc isValidAuthorOrContributor(data: JsonNode, key: string, path: string): bool = | ||
if isObject(data, "", path): | ||
result = true | ||
checkString("github_username") | ||
checkString("exercism_username") | ||
|
||
template checkFiles(data: JsonNode, context, path: string) = | ||
if isObject(data, context, path): | ||
checkArrayOfStrings(context, "solution") | ||
checkArrayOfStrings(context, "test") | ||
checkArrayOfStrings(context, "exemplar") | ||
else: | ||
result = false | ||
|
||
proc isValidConceptExerciseConfig(data: JsonNode, path: string): bool = | ||
if isObject(data, "", path): | ||
result = true | ||
checkArrayOf("authors", isValidAuthorOrContributor) | ||
checkArrayOf("contributors", isValidAuthorOrContributor, isRequired = false) | ||
checkFiles(data, "files", path) | ||
checkArrayOfStrings("", "forked_from", isRequired = false) | ||
checkString("language_versions", isRequired = false) | ||
|
||
proc isEveryConceptExerciseConfigValid*(trackDir: string): bool = | ||
let conceptExercisesDir = trackDir / "exercises" / "concept" | ||
result = true | ||
if dirExists(conceptExercisesDir): | ||
for exerciseDir in getSortedSubdirs(conceptExercisesDir): | ||
let configPath = exerciseDir / ".meta" / "config.json" | ||
if fileExists(configPath): | ||
let j = | ||
try: | ||
parseFile(configPath) | ||
except: | ||
writeError("JSON parsing error", getCurrentExceptionMsg()) | ||
continue | ||
if not isValidConceptExerciseConfig(j, configPath): | ||
result = false | ||
else: | ||
writeWarning("Directory does not exist", conceptExercisesDir) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import std/[json, terminal] | ||
import ".."/helpers | ||
|
||
proc q(s: string): string = | ||
"'" & s & "'" | ||
|
||
proc isObject*(data: JsonNode, key: string, path: string, | ||
isRequired = true): bool = | ||
result = true | ||
if key.len == 0: | ||
if data.kind != JObject: | ||
writeError("JSON root is not an object", path) | ||
elif data.hasKey(key): | ||
if data[key].kind != JObject: | ||
writeError("Not an object: " & q(key), path) | ||
elif isRequired: | ||
writeError("Missing key: " & q(key), path) | ||
|
||
template checkString*(key: string, isRequired = true) = | ||
if data.hasKey(key): | ||
if data[key].kind == JString: | ||
if data[key].getStr().len == 0: | ||
writeError("String is zero-length: " & q(key), path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed that the spec did not indicate that non-empty strings also not be non-blank (as in: not consist of only white space). I've just updated the spec. Could you update this check to reflect that the string not be empty after trimming? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
else: | ||
writeError("Not a string: " & q(key) & ": " & $data[key], path) | ||
elif isRequired: | ||
writeError("Missing key: " & q(key), path) | ||
|
||
proc format(context, key: string): string = | ||
if context.len > 0: | ||
q(context & "." & key) | ||
else: | ||
q(key) | ||
|
||
template checkArrayOfStrings*(context, key: string; isRequired = true) = | ||
var d = if context.len == 0: data else: data[context] | ||
if d.hasKey(key): | ||
if d[key].kind == JArray: | ||
if d[key].len == 0: | ||
writeError("Array is empty: " & format(context, key), path) | ||
else: | ||
for item in d[key]: | ||
if item.kind != JString: | ||
writeError("Array contains non-string: " & format(context, key) & ": " & $item, path) | ||
elif item.getStr().len == 0: | ||
writeError("Array contains zero-length string: " & format(context, key), path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment regarding non-blank strings. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
else: | ||
writeError("Not an array: " & format(context, key), path) | ||
elif isRequired: | ||
writeError("Missing key: " & format(context, key), path) | ||
|
||
template checkArrayOf*(key: string, | ||
call: proc(d: JsonNode; key, path: string): bool, | ||
isRequired = true) = | ||
if data.hasKey(key): | ||
if data[key].kind == JArray: | ||
if data[key].len == 0: | ||
writeError("Array is empty: " & q(key), path) | ||
else: | ||
for item in data[key]: | ||
if not call(item, key, path): | ||
result = false | ||
else: | ||
writeError("Not an array: " & q(key), path) | ||
elif isRequired: | ||
writeError("Missing key: " & q(key), path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about allowing the key parameter to be empty. I did not understand what the empty string did in
if isObject(data, "", path):
. I came up with two alternatives:I personally prefer the second option, as the
checkArrayOfStrings
template has the same property (and empty context being specified).