From e8983241bb72757cc4a0d0f5aff96dfc47f69fa0 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 9 Apr 2021 12:12:04 +0200 Subject: [PATCH 01/11] lint: add kebab-case checks This commit implements these checks for a track `config.json`: - The `"slug"` value must be a non-empty, non-blank, lowercased string using kebab-case - The `"exercises.concept[].slug"` value must be a non-empty, non-blank, lowercased string using kebab-case - The `"exercises.concept[].concepts"` values must be non-empty, non-blank, lowercased strings using kebab-case - The `"exercises.concept[].prerequisites"` values must be non-empty, non-blank, lowercased strings using kebab-case - The `"exercises.practice[].slug"` value must be a non-empty, non-blank, lowercased string using kebab-case - The `"exercises.practice[].practices"` values must be non-empty, non-blank, lowercased strings using kebab-case - The `"exercises.practice[].prerequisites"` values must be non-empty, non-blank, lowercased strings using kebab-case - The `"exercises.foregone"` values must be non-empty, non-blank, lowercased strings using kebab-case - The `"concepts[].slug"` value must be a non-empty, non-blank, lowercased string using kebab-case --- src/lint/track_config.nim | 19 ++++++++++--------- src/lint/validators.nim | 34 +++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/lint/track_config.nim b/src/lint/track_config.nim index 75e733a6..7092194e 100644 --- a/src/lint/track_config.nim +++ b/src/lint/track_config.nim @@ -74,14 +74,14 @@ const proc isValidConceptExercise(data: JsonNode; context: string; path: Path): bool = if isObject(data, context, path): let checks = [ - hasString(data, "slug", path, context), + hasString(data, "slug", path, context, checkIsKebab = true), hasString(data, "name", path, context), hasString(data, "uuid", path, context), hasBoolean(data, "deprecated", path, context, isRequired = false), hasArrayOfStrings(data, "concepts", path, context, - allowedArrayLen = 0..int.high), + allowedArrayLen = 0..int.high, checkIsKebab = true), hasArrayOfStrings(data, "prerequisites", path, context, - allowedArrayLen = 0..int.high), + allowedArrayLen = 0..int.high, checkIsKebab = true), hasString(data, "status", path, context, isRequired = false, allowed = statuses), ] @@ -91,15 +91,15 @@ proc isValidPracticeExercise(data: JsonNode; context: string; path: Path): bool = if isObject(data, context, path): let checks = [ - hasString(data, "slug", path, context), + hasString(data, "slug", path, context, checkIsKebab = true), hasString(data, "name", path, context), hasString(data, "uuid", path, context), hasBoolean(data, "deprecated", path, context, isRequired = false), hasInteger(data, "difficulty", path, context, allowed = 0..10), hasArrayOfStrings(data, "practices", path, context, - allowedArrayLen = 0..int.high), + allowedArrayLen = 0..int.high, checkIsKebab = true), hasArrayOfStrings(data, "prerequisites", path, context, - allowedArrayLen = 0..int.high), + allowedArrayLen = 0..int.high, checkIsKebab = true), hasString(data, "status", path, context, isRequired = false, allowed = statuses), ] @@ -114,7 +114,8 @@ proc hasValidExercises(data: JsonNode; path: Path): bool = allowedLength = 0..int.high), hasArrayOf(exercises, "practice", path, isValidPracticeExercise, k, allowedLength = 0..int.high), - hasArrayOfStrings(exercises, "foregone", path, k, isRequired = false), + hasArrayOfStrings(exercises, "foregone", path, k, isRequired = false, + checkIsKebab = true), ] result = allTrue(checks) @@ -122,7 +123,7 @@ proc isValidConcept(data: JsonNode; context: string; path: Path): bool = if isObject(data, context, path): let checks = [ hasString(data, "uuid", path, context), - hasString(data, "slug", path, context), + hasString(data, "slug", path, context, checkIsKebab = true), hasString(data, "name", path, context), ] result = allTrue(checks) @@ -153,7 +154,7 @@ proc isValidTrackConfig(data: JsonNode; path: Path): bool = if isObject(data, "", path): let checks = [ hasString(data, "language", path), - hasString(data, "slug", path), + hasString(data, "slug", path, checkIsKebab = true), hasBoolean(data, "active", path), hasString(data, "blurb", path, maxLen = 400), hasInteger(data, "version", path, allowed = 3..3), diff --git a/src/lint/validators.nim b/src/lint/validators.nim index 18d75227..71d191db 100644 --- a/src/lint/validators.nim +++ b/src/lint/validators.nim @@ -75,9 +75,16 @@ proc isUrlLike(s: string): bool = const emptySetOfStrings = initHashSet[string](0) +func isLowerKebab(s: string): bool = + ## Returns true if `s` is a lowercase and kebab-case string. + result = true + for c in s: + if c notin {'a'..'z', '0'..'9', '-'}: + return false + proc isString*(data: JsonNode; key: string; path: Path; context: string; isRequired = true; allowed = emptySetOfStrings; - checkIsUrlLike = false; maxLen = int.high; + checkIsUrlLike = false; maxLen = int.high; checkIsKebab = false; isInArray = false): bool = result = true case data.kind @@ -101,6 +108,16 @@ proc isString*(data: JsonNode; key: string; path: Path; context: string; result.setFalseAndPrint(&"Not a valid URL: {q s}", path) elif s.len > 0: if not isEmptyOrWhitespace(s): + if checkIsKebab: + if not isLowerKebab(s): + let msg = + if isInArray: + &"The {format(context, key)} array contains {s}, but every " & + "value must be lowercase and kebab-case" + else: + &"The {format(context, key)} value is {s}, but it must be a " & + "lowercase and kebab-case string" + result.setFalseAndPrint(msg, path) if not hasValidRuneLength(s, key, path, context, maxLen): result = false else: @@ -131,10 +148,11 @@ proc isString*(data: JsonNode; key: string; path: Path; context: string; proc hasString*(data: JsonNode; key: string; path: Path; context = ""; isRequired = true; allowed = emptySetOfStrings; - checkIsUrlLike = false; maxLen = int.high): bool = + checkIsUrlLike = false; maxLen = int.high; + checkIsKebab = false): bool = if data.hasKey(key, path, context, isRequired): result = isString(data[key], key, path, context, isRequired, allowed, - checkIsUrlLike, maxLen) + checkIsUrlLike, maxLen, checkIsKebab = checkIsKebab) elif not isRequired: result = true @@ -143,7 +161,8 @@ proc isArrayOfStrings*(data: JsonNode; path: Path; isRequired = true; allowed: HashSet[string]; - allowedArrayLen: Slice): bool = + allowedArrayLen: Slice; + checkIsKebab: bool): bool = ## Returns true in any of these cases: ## - `data` is a `JArray` with length in `allowedArrayLen` that contains only ## non-empty, non-blank strings. @@ -157,7 +176,7 @@ proc isArrayOfStrings*(data: JsonNode; if arrayLen in allowedArrayLen: for item in data: if not isString(item, context, path, "", isRequired, allowed, - isInArray = true): + checkIsKebab = checkIsKebab, isInArray = true): result = false else: let msgStart = &"The {q context} array has length {arrayLen}, " & @@ -184,14 +203,15 @@ proc hasArrayOfStrings*(data: JsonNode; context = ""; isRequired = true; allowed = emptySetOfStrings; - allowedArrayLen = 1..int.high): bool = + allowedArrayLen = 1..int.high; + checkIsKebab = false): bool = ## Returns true in any of these cases: ## - `isArrayOfStrings` returns true for `data[key]`. ## - `data` lacks the key `key` and `isRequired` is false. if data.hasKey(key, path, context, isRequired): let contextAndKey = joinWithDot(context, key) result = isArrayOfStrings(data[key], contextAndKey, path, isRequired, - allowed, allowedArrayLen) + allowed, allowedArrayLen, checkIsKebab = checkIsKebab) elif not isRequired: result = true From 54101ef4e8bd7a17e84dfa7c7e88f6111c51bd15 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 9 Apr 2021 13:22:05 +0200 Subject: [PATCH 02/11] lint(validators): fix long line (appease `nimpretty`) --- src/lint/validators.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lint/validators.nim b/src/lint/validators.nim index 71d191db..2ae53cd8 100644 --- a/src/lint/validators.nim +++ b/src/lint/validators.nim @@ -211,7 +211,8 @@ proc hasArrayOfStrings*(data: JsonNode; if data.hasKey(key, path, context, isRequired): let contextAndKey = joinWithDot(context, key) result = isArrayOfStrings(data[key], contextAndKey, path, isRequired, - allowed, allowedArrayLen, checkIsKebab = checkIsKebab) + allowed, allowedArrayLen, + checkIsKebab = checkIsKebab) elif not isRequired: result = true From 5419326aedb9e537982188a2bd3c9b501f8ce4c3 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 9 Apr 2021 17:39:17 +0200 Subject: [PATCH 03/11] lint(validators): make "kebab string" definition stricter --- src/lint/validators.nim | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/lint/validators.nim b/src/lint/validators.nim index 2ae53cd8..f975a0c5 100644 --- a/src/lint/validators.nim +++ b/src/lint/validators.nim @@ -1,4 +1,4 @@ -import std/[json, os, sets, streams, strformat, strutils, unicode] +import std/[json, os, parseutils, sets, streams, strformat, strutils, unicode] import ".."/helpers func allTrue*(bools: openArray[bool]): bool = @@ -75,12 +75,29 @@ proc isUrlLike(s: string): bool = const emptySetOfStrings = initHashSet[string](0) -func isLowerKebab(s: string): bool = - ## Returns true if `s` is a lowercase and kebab-case string. - result = true - for c in s: - if c notin {'a'..'z', '0'..'9', '-'}: - return false +func isKebabCase*(s: string): bool = + ## Returns true if `s` satisfies these rules: + ## - Has a length > 0 + ## - Begins and ends with a character in [a-z0-9] + ## - Consists only of characters in [a-z0-9-] + ## - Does not contain two adjacent `-` characters + ## This corresponds to the regex pattern `^[a-z0-9]+(-[a-z0-9]+)*$`. + const lowerAndDigits = {'a'..'z', '0'..'9'} + let L = s.len + + if L > 0 and s[0] in lowerAndDigits and s[^1] in lowerAndDigits: + var i = 0 + while true: + i += s.skipWhile(lowerAndDigits, start = i) + if i == L: + return true + elif s[i] == '-': + if s[i-1] == '-': + return false + else: + inc i + else: + return false proc isString*(data: JsonNode; key: string; path: Path; context: string; isRequired = true; allowed = emptySetOfStrings; @@ -109,7 +126,7 @@ proc isString*(data: JsonNode; key: string; path: Path; context: string; elif s.len > 0: if not isEmptyOrWhitespace(s): if checkIsKebab: - if not isLowerKebab(s): + if not isKebabCase(s): let msg = if isInArray: &"The {format(context, key)} array contains {s}, but every " & From 2fc0a75711590491ec5df51068bfe447b3c5a839 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 9 Apr 2021 17:40:29 +0200 Subject: [PATCH 04/11] tests: add test for isKebabCase --- tests/all_tests.nim | 1 + tests/test_lint.nim | 63 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 tests/test_lint.nim diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 18e31e4d..aca202bf 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -1,5 +1,6 @@ import "."/[ test_binary, + test_lint, test_probspecs, test_uuid, ] diff --git a/tests/test_lint.nim b/tests/test_lint.nim new file mode 100644 index 00000000..4321c83e --- /dev/null +++ b/tests/test_lint.nim @@ -0,0 +1,63 @@ +import std/unittest +import "."/[lint/validators] + +proc main = + suite "isKebabCase": + test "invalid kebab strings": + check: + # Some short, bad strings + not isKebabCase("") + not isKebabCase(" ") + not isKebabCase("-") + not isKebabCase("_") + not isKebabCase("a ") + not isKebabCase(" a") + not isKebabCase("a-") + not isKebabCase("-a") + not isKebabCase("--a") + not isKebabCase("a--") + not isKebabCase("-a-") + not isKebabCase("a--b") + # With symbols + not isKebabCase("&") + not isKebabCase("&str") + not isKebabCase("hello!") + # Bad dash usage + not isKebabCase("hello-world-") + not isKebabCase("-hello-world") + not isKebabCase("-hello-world-") + not isKebabCase("hello--world") + not isKebabCase("hello---world") + # With space + not isKebabCase("hello world") + not isKebabCase("hello World") + not isKebabCase("Hello world") + not isKebabCase("Hello World") + not isKebabCase("HELLO WORLD") + # With underscore + not isKebabCase("hello_world") + not isKebabCase("hello_World") + not isKebabCase("Hello_world") + not isKebabCase("Hello_World") + not isKebabCase("HELLO_WORLD") + # With dash + not isKebabCase("hello-World") + not isKebabCase("Hello-world") + not isKebabCase("Hello-World") + not isKebabCase("HELLO-WORLD") + # No spaces, but with capitals + not isKebabCase("helloWorld") + not isKebabCase("Helloworld") + not isKebabCase("HelloWorld") + not isKebabCase("HELLOWORLD") + + test "valid kebab strings": + check: + isKebabCase("a") + isKebabCase("hello") + isKebabCase("hello-world") + isKebabCase("hello-world-hello") + isKebabCase("hello-world-hello-world") + +main() +{.used.} From d5b3b0a2ef0be8e34656a8909b9dcc2af23e22aa Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 10 Apr 2021 10:02:55 +0200 Subject: [PATCH 05/11] tests(test_lint): add more `isKebabCase` tests --- tests/test_lint.nim | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/test_lint.nim b/tests/test_lint.nim index 4321c83e..2b5b709a 100644 --- a/tests/test_lint.nim +++ b/tests/test_lint.nim @@ -3,13 +3,15 @@ import "."/[lint/validators] proc main = suite "isKebabCase": - test "invalid kebab strings": + test "invalid kebab-case strings": check: # Some short, bad strings not isKebabCase("") not isKebabCase(" ") not isKebabCase("-") not isKebabCase("_") + not isKebabCase("--") + not isKebabCase("---") not isKebabCase("a ") not isKebabCase(" a") not isKebabCase("a-") @@ -45,15 +47,20 @@ proc main = not isKebabCase("Hello-world") not isKebabCase("Hello-World") not isKebabCase("HELLO-WORLD") - # No spaces, but with capitals + # No separator, but with capitals not isKebabCase("helloWorld") not isKebabCase("Helloworld") not isKebabCase("HelloWorld") not isKebabCase("HELLOWORLD") - test "valid kebab strings": + test "valid kebab-case strings": check: isKebabCase("a") + isKebabCase("1") + isKebabCase("123") + isKebabCase("123-456") + isKebabCase("hello-123") + isKebabCase("123-hello") isKebabCase("hello") isKebabCase("hello-world") isKebabCase("hello-world-hello") From f691276bdd6f6cfc60c05bddbf8b4bbb31eabfc4 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 10 Apr 2021 10:02:56 +0200 Subject: [PATCH 06/11] lint(validators): tweak `isKebabCase` implementation --- src/lint/validators.nim | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/lint/validators.nim b/src/lint/validators.nim index f975a0c5..816fe6e2 100644 --- a/src/lint/validators.nim +++ b/src/lint/validators.nim @@ -83,21 +83,18 @@ func isKebabCase*(s: string): bool = ## - Does not contain two adjacent `-` characters ## This corresponds to the regex pattern `^[a-z0-9]+(-[a-z0-9]+)*$`. const lowerAndDigits = {'a'..'z', '0'..'9'} - let L = s.len - - if L > 0 and s[0] in lowerAndDigits and s[^1] in lowerAndDigits: - var i = 0 - while true: - i += s.skipWhile(lowerAndDigits, start = i) - if i == L: - return true - elif s[i] == '-': - if s[i-1] == '-': - return false - else: - inc i - else: - return false + let sLen = s.len + var i = 0 + while i < sLen: + if s[i] == '-': + return false + i += s.skipWhile(lowerAndDigits, start = i) + if i == sLen: + return true + elif s[i] == '-': + inc i + else: + return false proc isString*(data: JsonNode; key: string; path: Path; context: string; isRequired = true; allowed = emptySetOfStrings; From 6b5252dca94c4cefeeeeedefcabb903052a48ff8 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 10 Apr 2021 10:02:57 +0200 Subject: [PATCH 07/11] lint(validators): tweak `isKebabCase` doc comment --- src/lint/validators.nim | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/lint/validators.nim b/src/lint/validators.nim index 816fe6e2..b899a936 100644 --- a/src/lint/validators.nim +++ b/src/lint/validators.nim @@ -76,12 +76,23 @@ const emptySetOfStrings = initHashSet[string](0) func isKebabCase*(s: string): bool = - ## Returns true if `s` satisfies these rules: - ## - Has a length > 0 - ## - Begins and ends with a character in [a-z0-9] - ## - Consists only of characters in [a-z0-9-] - ## - Does not contain two adjacent `-` characters - ## This corresponds to the regex pattern `^[a-z0-9]+(-[a-z0-9]+)*$`. + ## Returns true if `s` is a kebab-case string. By our definition, `s` must: + ## - Have a non-zero length + ## - Begin and end with a character in [a-z0-9] + ## - Consist only of characters in [a-z0-9-] + ## - Not contain consecutive `-` characters + ## This corresponds to the regex pattern `^[a-z0-9]+(?:-[a-z0-9]+)*$`. + runnableExamples: + assert isKebabCase("hello") + assert isKebabCase("hello-world") + assert isKebabCase("123") # Can contain only digits. + assert not isKebabCase("") # Cannot be the empty string. + assert not isKebabCase("hello world") # Cannot contain a space. + assert not isKebabCase("hello_world") # Cannot contain an underscore. + assert not isKebabCase("helloWorld") # Cannot contain an uppercase letter. + assert not isKebabCase("hello--world") # Cannot contain consecutive dashes. + assert not isKebabCase("hello!") # Cannot contain a special character. + const lowerAndDigits = {'a'..'z', '0'..'9'} let sLen = s.len var i = 0 From 09a4a160c6cc9347b26638e4689d1b0e3f112bdb Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:00:20 +0200 Subject: [PATCH 08/11] tests(test_lint): nitpick comments --- tests/test_lint.nim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_lint.nim b/tests/test_lint.nim index 2b5b709a..f9b5ad74 100644 --- a/tests/test_lint.nim +++ b/tests/test_lint.nim @@ -5,7 +5,7 @@ proc main = suite "isKebabCase": test "invalid kebab-case strings": check: - # Some short, bad strings + # Some short, invalid strings not isKebabCase("") not isKebabCase(" ") not isKebabCase("-") @@ -20,34 +20,34 @@ proc main = not isKebabCase("a--") not isKebabCase("-a-") not isKebabCase("a--b") - # With symbols + # Containing character not in [a-z0-9] not isKebabCase("&") not isKebabCase("&str") not isKebabCase("hello!") - # Bad dash usage + # Invalid dash usage not isKebabCase("hello-world-") not isKebabCase("-hello-world") not isKebabCase("-hello-world-") not isKebabCase("hello--world") not isKebabCase("hello---world") - # With space + # Invalid separator: space not isKebabCase("hello world") not isKebabCase("hello World") not isKebabCase("Hello world") not isKebabCase("Hello World") not isKebabCase("HELLO WORLD") - # With underscore + # Invalid separator: underscore not isKebabCase("hello_world") not isKebabCase("hello_World") not isKebabCase("Hello_world") not isKebabCase("Hello_World") not isKebabCase("HELLO_WORLD") - # With dash + # Containing uppercase, with dash not isKebabCase("hello-World") not isKebabCase("Hello-world") not isKebabCase("Hello-World") not isKebabCase("HELLO-WORLD") - # No separator, but with capitals + # Containing uppercase, with no separator not isKebabCase("helloWorld") not isKebabCase("Helloworld") not isKebabCase("HelloWorld") From 8edfc551cdbf4bfddde8f4dff94d310d09c002da Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:00:21 +0200 Subject: [PATCH 09/11] lint(validators): appease `nimpretty` again It doesn't like aligned comments. --- src/lint/validators.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lint/validators.nim b/src/lint/validators.nim index b899a936..c90d80b7 100644 --- a/src/lint/validators.nim +++ b/src/lint/validators.nim @@ -85,13 +85,13 @@ func isKebabCase*(s: string): bool = runnableExamples: assert isKebabCase("hello") assert isKebabCase("hello-world") - assert isKebabCase("123") # Can contain only digits. - assert not isKebabCase("") # Cannot be the empty string. - assert not isKebabCase("hello world") # Cannot contain a space. - assert not isKebabCase("hello_world") # Cannot contain an underscore. - assert not isKebabCase("helloWorld") # Cannot contain an uppercase letter. + assert isKebabCase("123") # Can contain only digits. + assert not isKebabCase("") # Cannot be the empty string. + assert not isKebabCase("hello world") # Cannot contain a space. + assert not isKebabCase("hello_world") # Cannot contain an underscore. + assert not isKebabCase("helloWorld") # Cannot contain an uppercase letter. assert not isKebabCase("hello--world") # Cannot contain consecutive dashes. - assert not isKebabCase("hello!") # Cannot contain a special character. + assert not isKebabCase("hello!") # Cannot contain a special character. const lowerAndDigits = {'a'..'z', '0'..'9'} let sLen = s.len From 638971c4b7b0ca6b444dd6abbf877cbbf55b45e3 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:00:22 +0200 Subject: [PATCH 10/11] lint(validators): nitpick doc comment This fixes the formatting of the doc comment when it appears as a pop-up in a text editor. --- src/lint/validators.nim | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lint/validators.nim b/src/lint/validators.nim index c90d80b7..dcfcf519 100644 --- a/src/lint/validators.nim +++ b/src/lint/validators.nim @@ -78,10 +78,16 @@ const func isKebabCase*(s: string): bool = ## Returns true if `s` is a kebab-case string. By our definition, `s` must: ## - Have a non-zero length - ## - Begin and end with a character in [a-z0-9] - ## - Consist only of characters in [a-z0-9-] + ## - Start and end with a character in `[a-z0-9]` + ## - Consist only of characters in `[a-z0-9-]` ## - Not contain consecutive `-` characters - ## This corresponds to the regex pattern `^[a-z0-9]+(?:-[a-z0-9]+)*$`. + ## + ## This is equivalent to matching the below regular expression: + ## + ## `^[a-z0-9]+(?:-[a-z0-9]+)*$`. + ## + ## However, this func's implementation is faster than one that uses + ## `re.match`, and doesn't add a PCRE dependency. runnableExamples: assert isKebabCase("hello") assert isKebabCase("hello-world") From 28727b27a6fc65f57f9c153d7046329c4d4c1361 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:00:23 +0200 Subject: [PATCH 11/11] lint(validators): quote string value in error message --- src/lint/validators.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lint/validators.nim b/src/lint/validators.nim index dcfcf519..6d9a2bab 100644 --- a/src/lint/validators.nim +++ b/src/lint/validators.nim @@ -143,11 +143,11 @@ proc isString*(data: JsonNode; key: string; path: Path; context: string; if not isKebabCase(s): let msg = if isInArray: - &"The {format(context, key)} array contains {s}, but every " & + &"The {format(context, key)} array contains {q s}, but every " & "value must be lowercase and kebab-case" else: - &"The {format(context, key)} value is {s}, but it must be a " & - "lowercase and kebab-case string" + &"The {format(context, key)} value is {q s}, but it must be " & + "a lowercase and kebab-case string" result.setFalseAndPrint(msg, path) if not hasValidRuneLength(s, key, path, context, maxLen): result = false