PureYAMLJSON is a separate library product layered on the core engine. JSON is a subset of YAML 1.2, so the existing parser already reads both into one value tree; this product adds the missing half and the value-level policy: - JSON.Writer: a pure-Swift RFC-8259 serializer (no Foundation; WASI-clean), pretty or compact, rejecting non-string keys and non-finite numbers. - JSON representability rules expressed in PureYAML's own Validation framework: mappingKeysAreStrings and numbersAreFinite join the core mappingKeysAreUnique. - JSON.Validator: the representability rule set plus issues(for:)/isRepresentable. - JSON.Convert: standalone convert(_:to:) with the round-trip law (parse -> validate representability -> emit -> re-parse must equal the source). To JSON rejects complex keys and non-finite numbers; to YAML rejects duplicate keys, which are legal in JSON but not in YAML. 19 tests cover the writer, the rules, and both conversion directions including the dual-rejection edges. Carved out of the catch-all Sources/Tests paths via exclude so the core PureYAML target is unchanged.
97 lines
4.0 KiB
Bash
97 lines
4.0 KiB
Bash
#!/usr/bin/env bash
|
|
# Repo contract gate for PureYAML "never" rules.
|
|
#
|
|
# These checks cover patterns that Swift unit tests cannot see reliably:
|
|
# package dependencies, source imports, C/generated parser files, XCTest-style
|
|
# tests, platform branches, and singleton/service-locator wiring.
|
|
|
|
set -u
|
|
|
|
FAIL=0
|
|
|
|
fail() {
|
|
echo "forbidden-patterns: $1" >&2
|
|
FAIL=1
|
|
}
|
|
|
|
report_matches() {
|
|
message="$1"
|
|
matches="$2"
|
|
if [ -n "$matches" ]; then
|
|
fail "$message"
|
|
printf '%s\n' "$matches" | sed 's/^/ /' >&2
|
|
fi
|
|
}
|
|
|
|
if ! grep -qE '^[[:space:]]*dependencies:[[:space:]]*\[[[:space:]]*\],[[:space:]]*$' Package.swift; then
|
|
fail "Package.swift must keep dependencies: []"
|
|
fi
|
|
|
|
package_forbidden=$(
|
|
grep -nE '\.package[[:space:]]*\(|\.binaryTarget[[:space:]]*\(|\.systemLibrary[[:space:]]*\(' Package.swift 2>/dev/null || true
|
|
)
|
|
report_matches "Package.swift must not declare external, binary, or system-library targets" "$package_forbidden"
|
|
|
|
# The core PureYAML target stays standard-library-only and imports nothing. The
|
|
# layered PureYAMLJSON target builds on the core, so it may import the first-party
|
|
# PureYAML module (and only that); no Foundation, no external, no system modules.
|
|
source_imports=$(
|
|
grep -RInE '^[[:space:]]*import[[:space:]]+[A-Za-z_][A-Za-z0-9_]*' Sources --include '*.swift' 2>/dev/null \
|
|
| grep -vE '^Sources/PureYAMLJSON/[^:]+:[0-9]+:[[:space:]]*import[[:space:]]+PureYAML[[:space:]]*$' || true
|
|
)
|
|
report_matches "Sources must stay standard-library-only (only PureYAMLJSON may import the core PureYAML module)" "$source_imports"
|
|
|
|
source_platform_branches=$(
|
|
grep -RInE '^[[:space:]]*#if[[:space:]]+(os|canImport)[[:space:]]*\(' Sources --include '*.swift' 2>/dev/null || true
|
|
)
|
|
report_matches "Sources must not add platform-specific branches without an explicit rule change" "$source_platform_branches"
|
|
|
|
source_runtimes=$(
|
|
find Sources -type f \( \
|
|
-name '*.c' -o \
|
|
-name '*.cc' -o \
|
|
-name '*.cpp' -o \
|
|
-name '*.h' -o \
|
|
-name '*.hpp' -o \
|
|
-name '*.m' -o \
|
|
-name '*.mm' -o \
|
|
-name '*.js' -o \
|
|
-name '*.ts' -o \
|
|
-name '*.wasm' -o \
|
|
-name '*.y' -o \
|
|
-name '*.l' -o \
|
|
-name '*.g4' \
|
|
\) -print 2>/dev/null
|
|
)
|
|
report_matches "Sources must not contain C, JavaScript, WebAssembly blobs, or generated parser runtime files" "$source_runtimes"
|
|
|
|
source_singletons=$(
|
|
grep -RInE 'static[[:space:]]+(let|var)[[:space:]]+shared\b|^[[:space:]]*(public[[:space:]]+)?(final[[:space:]]+)?(class|struct|enum)[[:space:]]+ServiceLocator\b|[A-Za-z0-9_]*(Resolver|Container|Environment)[[:space:]]*\.[[:space:]]*shared\b' Sources --include '*.swift' 2>/dev/null || true
|
|
)
|
|
report_matches "Sources must not introduce singleton or service-locator access patterns" "$source_singletons"
|
|
|
|
source_traps=$(
|
|
grep -RInE '\bfatalError[[:space:]]*\(|\bpreconditionFailure[[:space:]]*\(|\bassertionFailure[[:space:]]*\(|\btry!|\bas!|[A-Za-z0-9_)\]}][[:space:]]*![[:space:]]*([).,\];:]|$)' Sources --include '*.swift' 2>/dev/null || true
|
|
)
|
|
report_matches "Sources must not use traps, forced casts, try!, or force unwraps" "$source_traps"
|
|
|
|
test_imports=$(
|
|
grep -RInE '^[[:space:]]*(@testable[[:space:]]+)?import[[:space:]]+[A-Za-z_][A-Za-z0-9_]*' Tests --include '*.swift' 2>/dev/null \
|
|
| grep -vE '^[^:]+:[0-9]+:[[:space:]]*@testable[[:space:]]+import[[:space:]]+PureYAML(JSON)?[[:space:]]*$' \
|
|
| grep -vE '^[^:]+:[0-9]+:[[:space:]]*import[[:space:]]+PureYAML[[:space:]]*$' \
|
|
| grep -vE '^[^:]+:[0-9]+:[[:space:]]*import[[:space:]]+Foundation[[:space:]]*$' \
|
|
| grep -vE '^[^:]+:[0-9]+:[[:space:]]*import[[:space:]]+Testing[[:space:]]*$' || true
|
|
)
|
|
report_matches "Tests may import only Foundation, Testing, PureYAML, and @testable PureYAML/PureYAMLJSON" "$test_imports"
|
|
|
|
test_xctest=$(
|
|
grep -RInE '\bXCTest(Case|Expectation)?\b|\bXCTAssert[A-Za-z]*\b|^[[:space:]]*import[[:space:]]+XCTest[[:space:]]*$' Tests --include '*.swift' 2>/dev/null || true
|
|
)
|
|
report_matches "Tests must stay on Swift Testing and avoid XCTest-style APIs" "$test_xctest"
|
|
|
|
if [ "$FAIL" -eq 0 ]; then
|
|
echo "forbidden-patterns: OK"
|
|
fi
|
|
|
|
exit "$FAIL"
|