Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 9 additions & 51 deletions .github/workflows/r.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,20 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
r: ['release', 'oldrel-1']

env:
R_KEEP_PKG_SOURCE: yes
# The engine is compiled from bundled source, so the check builds and
# links the native code on every OS. Point the shared conformance corpus
# at the repo checkout so the test-assets tests (including `pattern`
# cases) run inside R CMD check on all platforms instead of skipping.
JSONSTRUCTURE_TEST_ASSETS: ${{ github.workspace }}/test-assets

steps:
- uses: actions/checkout@v6

- name: Build C library (CI only - production uses pre-built release binaries)
shell: bash
run: |
cmake -S c -B c/build -DCMAKE_BUILD_TYPE=Release -DJS_BUILD_SHARED=ON -DJS_BUILD_TESTS=OFF -DJS_BUILD_EXAMPLES=OFF
cmake --build c/build --config Release
if [ "$RUNNER_OS" = "macOS" ]; then
echo "JSONSTRUCTURE_LIB_PATH=$GITHUB_WORKSPACE/c/build/libjson_structure.dylib" >> "$GITHUB_ENV"
else
echo "JSONSTRUCTURE_LIB_PATH=$GITHUB_WORKSPACE/c/build/libjson_structure.so" >> "$GITHUB_ENV"
fi

- uses: r-lib/actions/setup-r@v2
with:
r-version: ${{ matrix.r }}
Expand All @@ -62,46 +56,10 @@ jobs:
- uses: r-lib/actions/check-r-package@v2
with:
working-directory: r
error-on: '"error"'
args: 'c("--no-manual", "--as-cran")'
error-on: '"warning"'
upload-snapshots: true

- name: Conformance tests against shared test-assets
working-directory: r
shell: bash
env:
JSONSTRUCTURE_TEST_ASSETS: ${{ github.workspace }}/test-assets
run: |
R CMD INSTALL .
Rscript -e "library(testthat); library(jsonstructure); testthat::test_dir('tests/testthat', stop_on_failure = TRUE)"

windows:
name: Check R release on windows-latest (shim compile + pure-R tests)
runs-on: windows-latest
env:
R_KEEP_PKG_SOURCE: yes
steps:
- uses: actions/checkout@v6

- uses: r-lib/actions/setup-r@v2
with:
r-version: 'release'
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
working-directory: r
extra-packages: |
any::rcmdcheck
any::testthat

# No prebuilt library is provided here, so binding-dependent tests skip
# gracefully. This job verifies the compiled shim builds under Rtools and
# that the pure-R logic (augmentation, result objects) passes on Windows.
- uses: r-lib/actions/check-r-package@v2
with:
working-directory: r
error-on: '"error"'

lint:
name: Lint R code
runs-on: ubuntu-latest
Expand Down Expand Up @@ -129,7 +87,7 @@ jobs:
release:
name: Attach R source tarball to release
runs-on: ubuntu-latest
needs: [test, windows, lint]
needs: [test, lint]
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
Expand Down
10 changes: 7 additions & 3 deletions c/src/instance_validator.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,16 @@ static void push_path(validate_context_t* ctx, const char* segment) {
return; /* Path too long, skip appending */
}

/* Buffer space was validated above, so the segment is guaranteed to
* fit; copy directly to avoid -Wformat-truncation false positives that
* arise from snprintf's runtime bound. */
if (len == 0) {
snprintf(ctx->path, PATH_BUFFER_SIZE, "%s", segment);
memcpy(ctx->path, segment, seg_len + 1);
} else if (segment[0] == '[') {
snprintf(ctx->path + len, PATH_BUFFER_SIZE - len, "%s", segment);
memcpy(ctx->path + len, segment, seg_len + 1);
} else {
snprintf(ctx->path + len, PATH_BUFFER_SIZE - len, ".%s", segment);
ctx->path[len] = '.';
memcpy(ctx->path + len + 1, segment, seg_len + 1);
}
}

Expand Down
10 changes: 7 additions & 3 deletions c/src/schema_validator.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,16 @@ static void push_path(validate_context_t* ctx, const char* segment) {
return; /* Path too long, skip appending */
}

/* Buffer space was validated above, so the segment is guaranteed to
* fit; copy directly to avoid -Wformat-truncation false positives that
* arise from snprintf's runtime bound. */
if (len == 0) {
snprintf(ctx->path, PATH_BUFFER_SIZE, "%s", segment);
memcpy(ctx->path, segment, seg_len + 1);
} else if (segment[0] == '[') {
snprintf(ctx->path + len, PATH_BUFFER_SIZE - len, "%s", segment);
memcpy(ctx->path + len, segment, seg_len + 1);
} else {
snprintf(ctx->path + len, PATH_BUFFER_SIZE - len, ".%s", segment);
ctx->path[len] = '.';
memcpy(ctx->path + len + 1, segment, seg_len + 1);
}
}

Expand Down
1 change: 1 addition & 0 deletions r/.Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
^\.gitignore$
^\.gitattributes$
^cran-comments\.md$
^tools$
^doc$
^Meta$
^src/.*\.(o|so|dll|dylib|a)$
Expand Down
25 changes: 8 additions & 17 deletions r/DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,26 @@ Title: JSON Structure Schema and Instance Validation
Version: 0.1.0
Authors@R: c(
person("Clemens", "Vasters", email = "clemensv@microsoft.com", role = c("aut", "cre")),
person("JSON Structure Contributors", role = "cph"))
person("JSON Structure Contributors", role = "cph"),
person("Dave", "Gamble", role = c("ctb", "cph"),
comment = "Author of the bundled cJSON library (src/cJSON.c, src/cjson/cJSON.h)"))
Description: R bindings for the JSON Structure validator, providing schema
validation and instance validation against JSON Structure schemas. The
package binds to the JSON Structure C library ('json_structure') for the
performance-critical validation work: a small compiled shim loads a
prebuilt shared library. That library is obtained on demand via
install_jsonstructure_binary() (downloaded from GitHub Releases with an
integrity check) or supplied through the JSONSTRUCTURE_LIB_PATH environment
variable; it is never downloaded without the user's action or consent.
Additional extension-keyword checks (units, relations, alternate names and
identifier rules) are performed in R.
performance-critical validation is carried out by the JSON Structure C
engine, which is compiled from bundled source directly into the package
together with its 'cJSON' dependency; nothing is downloaded at install or
run time. Additional extension-keyword checks (units, relations, alternate
names and identifier rules) are performed in R.
License: MIT + file LICENSE
URL: https://github.com/json-structure/sdk, https://json-structure.org
BugReports: https://github.com/json-structure/sdk/issues
Encoding: UTF-8
Depends: R (>= 4.0)
Imports:
jsonlite,
tools,
utils
Suggests:
digest,
openssl,
testthat (>= 3.0.0)
SystemRequirements: GNU make. A C compiler is required to build the package.
The prebuilt 'json_structure' shared library is obtained separately, either
by calling install_jsonstructure_binary() (which downloads it from GitHub
Releases; network access required) or by setting the JSONSTRUCTURE_LIB_PATH
environment variable to a local build.
Config/testthat/edition: 3
Roxygen: list(markdown = TRUE)
Config/roxygen2/version: 8.0.0
9 changes: 1 addition & 8 deletions r/NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,16 @@ S3method(is_valid,default)
S3method(is_valid,js_validation_result)
S3method(print,js_validation_error)
S3method(print,js_validation_result)
export(install_jsonstructure_binary)
export(is_valid)
export(js_error_messages)
export(js_validate_instance)
export(js_validate_instance_strict)
export(js_validate_schema)
export(js_validate_schema_strict)
export(js_warning_messages)
export(jsonstructure_binary_path)
export(jsonstructure_binary_version)
export(jsonstructure_engine_version)
export(jsonstructure_version)
importFrom(jsonlite,fromJSON)
importFrom(jsonlite,toJSON)
importFrom(tools,R_user_dir)
importFrom(utils,askYesNo)
importFrom(utils,download.file)
importFrom(utils,packageVersion)
importFrom(utils,untar)
importFrom(utils,unzip)
useDynLib(jsonstructure, .registration = TRUE, .fixes = "C_")
46 changes: 36 additions & 10 deletions r/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
# jsonstructure 0.1.0

* Initial release of the JSON Structure R SDK.
* Schema validation via `js_validate_schema()`.
* Instance validation via `js_validate_instance()`.
* Bindings to the JSON Structure C library through a small compiled shim that
loads a prebuilt `json_structure` shared library. The library is never
downloaded implicitly: point `JSONSTRUCTURE_LIB_PATH` at a local build, or run
`install_jsonstructure_binary()` (or consent to the one-time interactive
prompt) to fetch it from GitHub Releases. Downloads are checksum-verified when
an expected SHA-256 is supplied via `sha256=`, `JSONSTRUCTURE_BINARY_SHA256`,
or a shipped `checksums.dcf` manifest.
## Native engine compiled from bundled source (CRAN-conformant)

The JSON Structure C validation engine and its `cJSON` dependency are now
**bundled in the package and compiled from source at install time**. Nothing is
downloaded at install or run time, so the package installs from source on any
platform with a C toolchain and is conformant with CRAN policy. The package is
pure C, with no C++ runtime dependency.

This replaces the earlier prototype, which loaded a prebuilt `json_structure`
shared library downloaded from GitHub Releases on first use.

### Breaking changes

* Removed `install_jsonstructure_binary()` and `jsonstructure_binary_path()` —
there is no longer a separate native library to download or locate.
* Removed the `JSONSTRUCTURE_LIB_PATH` environment-variable override.
* Renamed `jsonstructure_binary_version()` to `jsonstructure_engine_version()`,
which reports the version of the bundled engine.

### Features

* Schema validation via `js_validate_schema()` / `js_validate_schema_strict()`.
* Instance validation via `js_validate_instance()` /
`js_validate_instance_strict()`.
* Tidy S3 results with `is_valid()`, `js_error_messages()`,
`js_warning_messages()` and `as.data.frame()`.
* Additional extension-keyword checks (units, relations, alternate identifier
rules, `$extends`, enum value typing) performed in R, matching the Ruby SDK.

### Notes

* `pattern` constraints are matched by a small embedded ECMAScript-subset
regular-expression engine (pure C), giving identical behaviour on every
platform with no C++/`std::regex` dependency. Constructs outside the subset
(lookbehind, named groups, inline flags, Unicode property escapes) are treated
as invalid patterns, and backtracking is bounded against pathological inputs.
* The vendored `cJSON` sources use bounded `snprintf()` (in place of the
upstream `sprintf()`) so the package passes the CRAN "compiled code" check.
Loading
Loading