openapi-spec-validator-rs 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/Cargo.lock +3 -3
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/Cargo.toml +3 -3
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/PKG-INFO +14 -5
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/README.md +13 -4
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/README.md +13 -4
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/document.rs +13 -6
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/formats.rs +1 -1
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/keywords.rs +3 -5
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/lib.rs +14 -2
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/libyaml.rs +33 -26
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/scalars.rs +8 -4
- openapi_spec_validator_rs-0.2.2/crates/openapi-spec-validator-rs/src/loader/stream.rs +242 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader.rs +3 -2
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/meta.rs +167 -35
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyjsonschema.rs +15 -15
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyobj.rs +52 -4
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyrepr.rs +109 -4
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/retrieve.rs +8 -10
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/values.rs +19 -16
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/version.rs +9 -2
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/README.md +13 -4
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/src/lib.rs +17 -12
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-py/src/convert.rs +111 -15
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-py/src/errors.rs +20 -9
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-py/src/lib.rs +32 -17
- openapi_spec_validator_rs-0.2.2/crates/openapi-spec-validator-rs-py/src/stack.rs +62 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/__main__.py +3 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/handlers.py +42 -6
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/readers.py +53 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/shortcuts.py +2 -2
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/validation/__init__.py +37 -0
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/validation/exceptions.py +21 -0
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/validation/proxies.py +23 -0
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/validation/validators.py +19 -0
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/versions/consts.py +11 -0
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/versions/datatypes.py +5 -0
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/versions/exceptions.py +5 -0
- openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/versions/shortcuts.py +5 -0
- openapi_spec_validator_rs-0.2.1/crates/openapi-spec-validator-rs-py/src/stack.rs +0 -33
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/LICENSE +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/NOTICE +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/Cargo.toml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/LICENSE +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/NOTICE +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/dialects/oas3.1/metaschema.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/dialects/oas3.1/vocabularies/base.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/dialects/oas3.2/dialect/2025-09-17.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/dialects/oas3.2/meta/2025-09-17.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/metaschemas/draft-04.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/metaschemas/draft-07.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/schemas/v2.0/schema.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/schemas/v3.0/schema.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/schemas/v3.1/schema.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/resources/schemas/v3.2/schema.json +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/error.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/bigint.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/compose.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/construct.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/dump.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/fast_json.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/pyfmt.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/resolve.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/loader/text_input.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pycompat_tests.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyformat.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyregex/ast.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyregex/parser.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyregex/translate.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyregex/unicode.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyregex.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyregex_tests.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/pyurl.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs/src/schema_path.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/Cargo.toml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/LICENSE +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/NOTICE +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/src/main.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/bad-regex.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/broken.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/crash-path-template.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/crash-version.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/many-errors.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/missing-response-description.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/oas31-defaults.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/oas31-malformed-ref-target.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/oas31-nested-schema-error.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/split/openapi.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/split/schemas/base.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/split/schemas/pet.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/swagger-parameter.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/swagger.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/fixtures/valid.yaml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/output.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__all_validation_errors.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__best_match_hint_in_python_order.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__deprecated_errors_flag_warns.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__exception_from_a_malformed_keyword.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__exception_from_path_template.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__exception_from_unquoted_version.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__explicit_schema_version.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__first_error_before_an_exception.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__first_error_only.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__meta_schema_error_with_all_subschema_errors.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__meta_schema_error_with_best_match.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__meta_schema_path_through_dynamic_ref.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__missing_file.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__oas31_default_values.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__python_regex_rules.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__refs_across_files.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__remote_ref_invalid_default.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__remote_ref_unresolvable.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__remote_ref_valid.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__stops_at_first_failing_file.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__valid_openapi_3.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__valid_swagger_2.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-cli/tests/snapshots/output__yaml_syntax_error.snap +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-py/Cargo.toml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/crates/openapi-spec-validator-rs-py/src/retrieve.rs +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/pyproject.toml +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/__init__.py +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/_core.pyi +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/exceptions.py +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/proxies.py +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/py.typed +0 -0
- {openapi_spec_validator_rs-0.2.1 → openapi_spec_validator_rs-0.2.2}/python/openapi_spec_validator_rs/validators.py +0 -0
- /openapi_spec_validator_rs-0.2.1/python/openapi_spec_validator_rs/versions.py → /openapi_spec_validator_rs-0.2.2/python/openapi_spec_validator_rs/versions/__init__.py +0 -0
|
@@ -848,7 +848,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
|
|
848
848
|
|
|
849
849
|
[[package]]
|
|
850
850
|
name = "openapi-spec-validator-rs"
|
|
851
|
-
version = "0.2.
|
|
851
|
+
version = "0.2.2"
|
|
852
852
|
dependencies = [
|
|
853
853
|
"base64",
|
|
854
854
|
"fancy-regex",
|
|
@@ -864,7 +864,7 @@ dependencies = [
|
|
|
864
864
|
|
|
865
865
|
[[package]]
|
|
866
866
|
name = "openapi-spec-validator-rs-cli"
|
|
867
|
-
version = "0.2.
|
|
867
|
+
version = "0.2.2"
|
|
868
868
|
dependencies = [
|
|
869
869
|
"clap",
|
|
870
870
|
"insta",
|
|
@@ -875,7 +875,7 @@ dependencies = [
|
|
|
875
875
|
|
|
876
876
|
[[package]]
|
|
877
877
|
name = "openapi-spec-validator-rs-py"
|
|
878
|
-
version = "0.2.
|
|
878
|
+
version = "0.2.2"
|
|
879
879
|
dependencies = [
|
|
880
880
|
"clap",
|
|
881
881
|
"mimalloc",
|
|
@@ -3,7 +3,7 @@ resolver = "3"
|
|
|
3
3
|
members = ["crates/*"]
|
|
4
4
|
|
|
5
5
|
[workspace.package]
|
|
6
|
-
version = "0.2.
|
|
6
|
+
version = "0.2.2"
|
|
7
7
|
edition = "2024"
|
|
8
8
|
rust-version = "1.85"
|
|
9
9
|
license = "Apache-2.0"
|
|
@@ -14,8 +14,8 @@ keywords = ["openapi", "swagger", "validation", "jsonschema"]
|
|
|
14
14
|
categories = ["command-line-utilities", "development-tools", "web-programming"]
|
|
15
15
|
|
|
16
16
|
[workspace.dependencies]
|
|
17
|
-
openapi-spec-validator-rs = { path = "crates/openapi-spec-validator-rs", version = "0.2.
|
|
18
|
-
openapi-spec-validator-rs-cli = { path = "crates/openapi-spec-validator-rs-cli", version = "0.2.
|
|
17
|
+
openapi-spec-validator-rs = { path = "crates/openapi-spec-validator-rs", version = "0.2.2", default-features = false }
|
|
18
|
+
openapi-spec-validator-rs-cli = { path = "crates/openapi-spec-validator-rs-cli", version = "0.2.2", default-features = false }
|
|
19
19
|
|
|
20
20
|
anyhow = "1.0.104"
|
|
21
21
|
base64 = "0.23.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openapi-spec-validator-rs
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: Operating System :: OS Independent
|
|
@@ -113,14 +113,20 @@ The package mirrors openapi-spec-validator's API:
|
|
|
113
113
|
```python
|
|
114
114
|
from openapi_spec_validator_rs import validate, validate_path, validate_url
|
|
115
115
|
from openapi_spec_validator_rs import OpenAPIV31SpecValidator, OpenAPIValidationError
|
|
116
|
+
from openapi_spec_validator_rs.readers import read_from_filename
|
|
116
117
|
|
|
117
118
|
validate(spec_dict) # raises the first error
|
|
118
119
|
validate_path("openapi.yaml") # reads the file like the command line
|
|
119
120
|
validate_url("https://example.com/openapi.json")
|
|
120
121
|
|
|
121
|
-
|
|
122
|
+
spec_dict, base_uri = read_from_filename("openapi.yaml")
|
|
123
|
+
errors = list(OpenAPIV31SpecValidator(spec_dict, base_uri=base_uri).iter_errors())
|
|
122
124
|
```
|
|
123
125
|
|
|
126
|
+
`spec_dict` can be anything `json.load` or `yaml.safe_load` returns, dates and all: a
|
|
127
|
+
`datetime.date` from an unquoted `2020-01-01` is treated as jsonschema treats it (of no
|
|
128
|
+
type, shown as `datetime.date(2020, 1, 1)` in messages).
|
|
129
|
+
|
|
124
130
|
Errors are `OpenAPIValidationError` (or one of its subclasses: `DuplicateOperationIDError`,
|
|
125
131
|
`ParameterDuplicateError`, `UnresolvableParameterError`, `ExtraParametersError`) with the
|
|
126
132
|
same attributes and messages as upstream's. Moving over is usually a matter of changing the
|
|
@@ -165,7 +171,7 @@ repos:
|
|
|
165
171
|
name: openapi-spec-validator-rs
|
|
166
172
|
entry: openapi-spec-validator-rs
|
|
167
173
|
language: python
|
|
168
|
-
additional_dependencies: [openapi-spec-validator-rs==0.2.
|
|
174
|
+
additional_dependencies: [openapi-spec-validator-rs==0.2.2]
|
|
169
175
|
files: .*(openapi|swagger).*\.(json|yaml|yml)$
|
|
170
176
|
```
|
|
171
177
|
|
|
@@ -204,8 +210,10 @@ The validator is a Rust library (`crates/openapi-spec-validator-rs`) with a CLI
|
|
|
204
210
|
PyO3 bindings on top. It follows the reference implementation step by step:
|
|
205
211
|
|
|
206
212
|
1. **Load**: JSON goes through serde_json when libyaml would read it the same way; anything
|
|
207
|
-
else goes through libyaml (the parser inside PyYAML
|
|
208
|
-
|
|
213
|
+
else goes through libyaml (the parser inside PyYAML, as the [unsafe-libyaml] crate's
|
|
214
|
+
mechanical translation of its C code: archived upstream, which is also what keeps it
|
|
215
|
+
identical to the parser PyYAML bundles) with PyYAML's YAML 1.1 rules, then through
|
|
216
|
+
the same JSON round trip. Streams are read only as far as that parser would get.
|
|
209
217
|
2. **Meta-schema**: the whole document is validated against the official schema for its
|
|
210
218
|
version with the [`jsonschema`](https://crates.io/crates/jsonschema) crate.
|
|
211
219
|
3. **Walk**: the document is walked like `keywords.py`, following `$ref`s (into other
|
|
@@ -233,4 +241,5 @@ and [NOTICE][notice].
|
|
|
233
241
|
[compatibility]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/COMPATIBILITY.md
|
|
234
242
|
[license]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/LICENSE
|
|
235
243
|
[notice]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/NOTICE
|
|
244
|
+
[unsafe-libyaml]: https://crates.io/crates/unsafe-libyaml
|
|
236
245
|
|
|
@@ -88,14 +88,20 @@ The package mirrors openapi-spec-validator's API:
|
|
|
88
88
|
```python
|
|
89
89
|
from openapi_spec_validator_rs import validate, validate_path, validate_url
|
|
90
90
|
from openapi_spec_validator_rs import OpenAPIV31SpecValidator, OpenAPIValidationError
|
|
91
|
+
from openapi_spec_validator_rs.readers import read_from_filename
|
|
91
92
|
|
|
92
93
|
validate(spec_dict) # raises the first error
|
|
93
94
|
validate_path("openapi.yaml") # reads the file like the command line
|
|
94
95
|
validate_url("https://example.com/openapi.json")
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
spec_dict, base_uri = read_from_filename("openapi.yaml")
|
|
98
|
+
errors = list(OpenAPIV31SpecValidator(spec_dict, base_uri=base_uri).iter_errors())
|
|
97
99
|
```
|
|
98
100
|
|
|
101
|
+
`spec_dict` can be anything `json.load` or `yaml.safe_load` returns, dates and all: a
|
|
102
|
+
`datetime.date` from an unquoted `2020-01-01` is treated as jsonschema treats it (of no
|
|
103
|
+
type, shown as `datetime.date(2020, 1, 1)` in messages).
|
|
104
|
+
|
|
99
105
|
Errors are `OpenAPIValidationError` (or one of its subclasses: `DuplicateOperationIDError`,
|
|
100
106
|
`ParameterDuplicateError`, `UnresolvableParameterError`, `ExtraParametersError`) with the
|
|
101
107
|
same attributes and messages as upstream's. Moving over is usually a matter of changing the
|
|
@@ -140,7 +146,7 @@ repos:
|
|
|
140
146
|
name: openapi-spec-validator-rs
|
|
141
147
|
entry: openapi-spec-validator-rs
|
|
142
148
|
language: python
|
|
143
|
-
additional_dependencies: [openapi-spec-validator-rs==0.2.
|
|
149
|
+
additional_dependencies: [openapi-spec-validator-rs==0.2.2]
|
|
144
150
|
files: .*(openapi|swagger).*\.(json|yaml|yml)$
|
|
145
151
|
```
|
|
146
152
|
|
|
@@ -179,8 +185,10 @@ The validator is a Rust library (`crates/openapi-spec-validator-rs`) with a CLI
|
|
|
179
185
|
PyO3 bindings on top. It follows the reference implementation step by step:
|
|
180
186
|
|
|
181
187
|
1. **Load**: JSON goes through serde_json when libyaml would read it the same way; anything
|
|
182
|
-
else goes through libyaml (the parser inside PyYAML
|
|
183
|
-
|
|
188
|
+
else goes through libyaml (the parser inside PyYAML, as the [unsafe-libyaml] crate's
|
|
189
|
+
mechanical translation of its C code: archived upstream, which is also what keeps it
|
|
190
|
+
identical to the parser PyYAML bundles) with PyYAML's YAML 1.1 rules, then through
|
|
191
|
+
the same JSON round trip. Streams are read only as far as that parser would get.
|
|
184
192
|
2. **Meta-schema**: the whole document is validated against the official schema for its
|
|
185
193
|
version with the [`jsonschema`](https://crates.io/crates/jsonschema) crate.
|
|
186
194
|
3. **Walk**: the document is walked like `keywords.py`, following `$ref`s (into other
|
|
@@ -208,3 +216,4 @@ and [NOTICE][notice].
|
|
|
208
216
|
[compatibility]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/COMPATIBILITY.md
|
|
209
217
|
[license]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/LICENSE
|
|
210
218
|
[notice]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/NOTICE
|
|
219
|
+
[unsafe-libyaml]: https://crates.io/crates/unsafe-libyaml
|
|
@@ -88,14 +88,20 @@ The package mirrors openapi-spec-validator's API:
|
|
|
88
88
|
```python
|
|
89
89
|
from openapi_spec_validator_rs import validate, validate_path, validate_url
|
|
90
90
|
from openapi_spec_validator_rs import OpenAPIV31SpecValidator, OpenAPIValidationError
|
|
91
|
+
from openapi_spec_validator_rs.readers import read_from_filename
|
|
91
92
|
|
|
92
93
|
validate(spec_dict) # raises the first error
|
|
93
94
|
validate_path("openapi.yaml") # reads the file like the command line
|
|
94
95
|
validate_url("https://example.com/openapi.json")
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
spec_dict, base_uri = read_from_filename("openapi.yaml")
|
|
98
|
+
errors = list(OpenAPIV31SpecValidator(spec_dict, base_uri=base_uri).iter_errors())
|
|
97
99
|
```
|
|
98
100
|
|
|
101
|
+
`spec_dict` can be anything `json.load` or `yaml.safe_load` returns, dates and all: a
|
|
102
|
+
`datetime.date` from an unquoted `2020-01-01` is treated as jsonschema treats it (of no
|
|
103
|
+
type, shown as `datetime.date(2020, 1, 1)` in messages).
|
|
104
|
+
|
|
99
105
|
Errors are `OpenAPIValidationError` (or one of its subclasses: `DuplicateOperationIDError`,
|
|
100
106
|
`ParameterDuplicateError`, `UnresolvableParameterError`, `ExtraParametersError`) with the
|
|
101
107
|
same attributes and messages as upstream's. Moving over is usually a matter of changing the
|
|
@@ -140,7 +146,7 @@ repos:
|
|
|
140
146
|
name: openapi-spec-validator-rs
|
|
141
147
|
entry: openapi-spec-validator-rs
|
|
142
148
|
language: python
|
|
143
|
-
additional_dependencies: [openapi-spec-validator-rs==0.2.
|
|
149
|
+
additional_dependencies: [openapi-spec-validator-rs==0.2.2]
|
|
144
150
|
files: .*(openapi|swagger).*\.(json|yaml|yml)$
|
|
145
151
|
```
|
|
146
152
|
|
|
@@ -179,8 +185,10 @@ The validator is a Rust library (`crates/openapi-spec-validator-rs`) with a CLI
|
|
|
179
185
|
PyO3 bindings on top. It follows the reference implementation step by step:
|
|
180
186
|
|
|
181
187
|
1. **Load**: JSON goes through serde_json when libyaml would read it the same way; anything
|
|
182
|
-
else goes through libyaml (the parser inside PyYAML
|
|
183
|
-
|
|
188
|
+
else goes through libyaml (the parser inside PyYAML, as the [unsafe-libyaml] crate's
|
|
189
|
+
mechanical translation of its C code: archived upstream, which is also what keeps it
|
|
190
|
+
identical to the parser PyYAML bundles) with PyYAML's YAML 1.1 rules, then through
|
|
191
|
+
the same JSON round trip. Streams are read only as far as that parser would get.
|
|
184
192
|
2. **Meta-schema**: the whole document is validated against the official schema for its
|
|
185
193
|
version with the [`jsonschema`](https://crates.io/crates/jsonschema) crate.
|
|
186
194
|
3. **Walk**: the document is walked like `keywords.py`, following `$ref`s (into other
|
|
@@ -208,3 +216,4 @@ and [NOTICE][notice].
|
|
|
208
216
|
[compatibility]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/COMPATIBILITY.md
|
|
209
217
|
[license]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/LICENSE
|
|
210
218
|
[notice]: https://github.com/Arjun-Aravind/openapi-spec-validator-rs/blob/main/NOTICE
|
|
219
|
+
[unsafe-libyaml]: https://crates.io/crates/unsafe-libyaml
|
|
@@ -19,6 +19,7 @@ use serde_json::Value;
|
|
|
19
19
|
use url::Url;
|
|
20
20
|
|
|
21
21
|
use crate::loader;
|
|
22
|
+
use crate::pyobj;
|
|
22
23
|
use crate::pyrepr;
|
|
23
24
|
use crate::pyurl;
|
|
24
25
|
use crate::retrieve::Retrieve;
|
|
@@ -227,7 +228,7 @@ impl<'a> Documents<'a> {
|
|
|
227
228
|
let path = url
|
|
228
229
|
.to_file_path()
|
|
229
230
|
.map_err(|()| format!("not a file path: {uri}"))?;
|
|
230
|
-
let bytes =
|
|
231
|
+
let bytes = crate::read_file(&path).map_err(|error| error.to_string())?;
|
|
231
232
|
// Upstream reads files through `urlopen`, which is the stream name
|
|
232
233
|
// PyYAML reports.
|
|
233
234
|
loader::load(&bytes, "<urllib response>").map_err(|error| error.message)
|
|
@@ -345,7 +346,7 @@ impl<'a> Documents<'a> {
|
|
|
345
346
|
Value::Array(items) => position(segment, items.len())?
|
|
346
347
|
.map(|i| &items[i])
|
|
347
348
|
.ok_or_else(nowhere)?,
|
|
348
|
-
Value::Object(map) => {
|
|
349
|
+
Value::Object(map) if pyrepr::foreign(contents).is_none() => {
|
|
349
350
|
let key = segment.replace("~1", "/").replace("~0", "~");
|
|
350
351
|
map.get(&key).ok_or_else(nowhere)?
|
|
351
352
|
}
|
|
@@ -370,8 +371,8 @@ impl<'a> Documents<'a> {
|
|
|
370
371
|
|
|
371
372
|
/// The child `key` of a mapping node, resolved (`path / key`).
|
|
372
373
|
pub fn get<'d>(&'d self, node: Node<'d>, key: &str) -> Result<Option<Node<'d>>, Crash> {
|
|
373
|
-
match node.value {
|
|
374
|
-
|
|
374
|
+
match pyobj::as_dict(node.value) {
|
|
375
|
+
Some(map) => match map.get(key) {
|
|
375
376
|
Some(value) => self
|
|
376
377
|
.resolve(Node {
|
|
377
378
|
value,
|
|
@@ -410,7 +411,7 @@ impl<'a> Documents<'a> {
|
|
|
410
411
|
) -> Result<Vec<(Key<'d>, Node<'d>)>, Crash> {
|
|
411
412
|
let doc = node.doc;
|
|
412
413
|
match node.value {
|
|
413
|
-
Value::Object(map) => Ok(map
|
|
414
|
+
Value::Object(map) if pyrepr::foreign(node.value).is_none() => Ok(map
|
|
414
415
|
.iter()
|
|
415
416
|
.map(|(key, value)| (Key::Str(key), Node { value, doc }))
|
|
416
417
|
.collect()),
|
|
@@ -433,11 +434,12 @@ pub(crate) enum Key<'d> {
|
|
|
433
434
|
|
|
434
435
|
/// `is_ref` from `jsonschema-path`: a mapping whose `$ref` is a string.
|
|
435
436
|
pub(crate) fn as_ref(value: &Value) -> Option<&str> {
|
|
436
|
-
value
|
|
437
|
+
pyobj::as_dict(value)?.get("$ref")?.as_str()
|
|
437
438
|
}
|
|
438
439
|
|
|
439
440
|
/// Python `int(s)` for base-10 literals (ASCII digits, sign, underscores, whitespace).
|
|
440
441
|
fn python_int(s: &str) -> Option<i128> {
|
|
442
|
+
let s = pyobj::decimal_to_ascii(s)?;
|
|
441
443
|
let s = s.trim();
|
|
442
444
|
let (negative, digits) = match s.as_bytes().first()? {
|
|
443
445
|
b'-' => (true, &s[1..]),
|
|
@@ -594,5 +596,10 @@ mod tests {
|
|
|
594
596
|
assert_eq!(python_int("1_0"), Some(10));
|
|
595
597
|
assert_eq!(python_int("1__0"), None);
|
|
596
598
|
assert_eq!(python_int("x"), None);
|
|
599
|
+
// Python's `int()` reads every script's decimal digits and whitespace.
|
|
600
|
+
assert_eq!(python_int("\u{660}\u{661}"), Some(1));
|
|
601
|
+
assert_eq!(python_int("\u{3000}-\u{ff11}\u{ff12} "), Some(-12));
|
|
602
|
+
assert_eq!(python_int("\u{660}x"), None);
|
|
603
|
+
assert_eq!(python_int("\u{2160}"), None);
|
|
597
604
|
}
|
|
598
605
|
}
|
|
@@ -682,7 +682,7 @@ fn py_int_base16(s: &str) -> Option<bool> {
|
|
|
682
682
|
}
|
|
683
683
|
|
|
684
684
|
/// Value of a Unicode decimal digit (`unicodedata.decimal`), Unicode 15.1.
|
|
685
|
-
fn decimal_value(c: char) -> Option<u32> {
|
|
685
|
+
pub(crate) fn decimal_value(c: char) -> Option<u32> {
|
|
686
686
|
let cp = c as u32;
|
|
687
687
|
let i = DECIMAL_ZEROS.partition_point(|&z| z <= cp);
|
|
688
688
|
let zero = DECIMAL_ZEROS[..i].last()?;
|
|
@@ -347,7 +347,7 @@ impl<'d, 'a> Walker<'d, 'a> {
|
|
|
347
347
|
let location = self.item(parameter, "in")?;
|
|
348
348
|
// jsonschema-path hands out lists and mappings as `SchemaPath`s, which are
|
|
349
349
|
// hashable and equal only to the same path: such a key is never a duplicate.
|
|
350
|
-
let container = |v: &Value| v.is_array() || v.
|
|
350
|
+
let container = |v: &Value| v.is_array() || pyobj::as_dict(v).is_some();
|
|
351
351
|
if container(name) || container(location) {
|
|
352
352
|
continue;
|
|
353
353
|
}
|
|
@@ -451,7 +451,7 @@ impl<'d, 'a> Walker<'d, 'a> {
|
|
|
451
451
|
/// as a nested subschema never gets the `required` check.
|
|
452
452
|
fn schema(&mut self, schema: Node<'d>, require_properties: bool, meta_checked: bool) -> Step {
|
|
453
453
|
let value = schema.value;
|
|
454
|
-
if
|
|
454
|
+
if pyobj::as_dict(value).is_none() && !value.is_boolean() {
|
|
455
455
|
return self.emit_new(
|
|
456
456
|
ErrorKind::OpenApiValidation,
|
|
457
457
|
format!("{} is not of type 'object', 'boolean'", repr(value)),
|
|
@@ -675,9 +675,7 @@ impl<'d, 'a> Walker<'d, 'a> {
|
|
|
675
675
|
|
|
676
676
|
/// `"key" in path` on a resolved node.
|
|
677
677
|
fn contains(node: Node<'_>, key: &str) -> bool {
|
|
678
|
-
node.value
|
|
679
|
-
.as_object()
|
|
680
|
-
.is_some_and(|map| map.contains_key(key))
|
|
678
|
+
pyobj::as_dict(node.value).is_some_and(|map| map.contains_key(key))
|
|
681
679
|
}
|
|
682
680
|
|
|
683
681
|
/// `list(set(required) - set(properties) - set(nested_properties))`.
|
|
@@ -62,7 +62,7 @@ use crate::document::Documents;
|
|
|
62
62
|
use crate::keywords::{Stop, Walker};
|
|
63
63
|
|
|
64
64
|
pub use crate::error::{ErrorKind, Outcome, SchemaErrorDetails, ValidationError, best_match};
|
|
65
|
-
pub use crate::loader::{LoadError, TextErrors, load, load_text};
|
|
65
|
+
pub use crate::loader::{LoadError, TextErrors, load, load_text, read_until_unreadable};
|
|
66
66
|
pub use crate::pyrepr::PathItem;
|
|
67
67
|
#[cfg(feature = "http")]
|
|
68
68
|
pub use crate::retrieve::HttpRetriever;
|
|
@@ -188,6 +188,18 @@ pub fn validate(spec: &Value, options: &Options) -> Outcome {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
/// Reads a document file as PyYAML reads the stream `urlopen` gives upstream: only as far
|
|
192
|
+
/// as the parser could get (see [`read_until_unreadable`]).
|
|
193
|
+
pub(crate) fn read_file(path: &Path) -> std::io::Result<Vec<u8>> {
|
|
194
|
+
let mut file = std::fs::File::open(path)?;
|
|
195
|
+
let mut bytes = Vec::new();
|
|
196
|
+
if let Ok(metadata) = file.metadata() {
|
|
197
|
+
bytes.reserve_exact(usize::try_from(metadata.len()).unwrap_or(0));
|
|
198
|
+
}
|
|
199
|
+
read_until_unreadable(&mut file, &mut bytes)?;
|
|
200
|
+
Ok(bytes)
|
|
201
|
+
}
|
|
202
|
+
|
|
191
203
|
/// Stack size for validation threads: enough for documents nested as deeply as the
|
|
192
204
|
/// loader allows (pages are only committed as they are used).
|
|
193
205
|
const STACK_SIZE: usize = 256 << 20;
|
|
@@ -291,7 +303,7 @@ fn read_and_validate(path: &Path, options: &Options) -> Result<Outcome, LoadErro
|
|
|
291
303
|
return Err(not_found());
|
|
292
304
|
}
|
|
293
305
|
let absolute = std::path::absolute(path).map_err(|_| not_found())?;
|
|
294
|
-
let bytes =
|
|
306
|
+
let bytes = read_file(&absolute).map_err(|error| LoadError {
|
|
295
307
|
message: error.to_string(),
|
|
296
308
|
})?;
|
|
297
309
|
// Upstream reads files through `urlopen`, which is the stream name PyYAML reports.
|
|
@@ -45,23 +45,26 @@ pub(super) enum Source<'a> {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
pub(super) struct Parser<'a> {
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
|
|
48
|
+
// Heap-allocated and only ever touched through this raw pointer: libyaml's string reader
|
|
49
|
+
// keeps a pointer to the parser inside the parser itself (`read_handler_data`), which a
|
|
50
|
+
// `Box` or `&mut` reborrow would invalidate under Rust's aliasing rules. Freed in `drop`.
|
|
51
|
+
raw: *mut unsafe_yaml::yaml_parser_t,
|
|
52
|
+
// The read handler's data, registered with libyaml and read through that pointer only;
|
|
53
|
+
// owned here so it outlives the parser. Freed in `drop`.
|
|
54
|
+
feed: Option<*mut TextFeed<'a>>,
|
|
52
55
|
stream_name: &'a str,
|
|
53
56
|
_input: PhantomData<&'a [u8]>,
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
impl<'a> Parser<'a> {
|
|
57
60
|
pub fn new(source: Source<'a>, stream_name: &'a str) -> Self {
|
|
58
|
-
let
|
|
61
|
+
let raw = Box::into_raw(Box::<unsafe_yaml::yaml_parser_t>::new_uninit())
|
|
62
|
+
.cast::<unsafe_yaml::yaml_parser_t>();
|
|
59
63
|
// SAFETY: `yaml_parser_initialize` zeroes the whole struct and allocates its buffers,
|
|
60
64
|
// leaving it fully initialised. With Rust's allocator it only fails by aborting.
|
|
61
|
-
|
|
62
|
-
let _ = unsafe_yaml::yaml_parser_initialize(
|
|
63
|
-
|
|
64
|
-
};
|
|
65
|
+
unsafe {
|
|
66
|
+
let _ = unsafe_yaml::yaml_parser_initialize(raw);
|
|
67
|
+
}
|
|
65
68
|
let mut feed = None;
|
|
66
69
|
match source {
|
|
67
70
|
Source::Bytes(bytes) => {
|
|
@@ -69,25 +72,20 @@ impl<'a> Parser<'a> {
|
|
|
69
72
|
// parser (lifetime `'a`), and libyaml only reads `bytes.len()` bytes from it.
|
|
70
73
|
unsafe {
|
|
71
74
|
unsafe_yaml::yaml_parser_set_input_string(
|
|
72
|
-
|
|
75
|
+
raw,
|
|
73
76
|
bytes.as_ptr(),
|
|
74
77
|
bytes.len() as u64,
|
|
75
78
|
);
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
Source::Text(text) => {
|
|
79
|
-
let
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
// for as long as libyaml can call `read_handler`.
|
|
82
|
+
let data = Box::into_raw(Box::new(text));
|
|
83
|
+
// SAFETY: the feed is heap-allocated and owned by `self` (freed in `drop`), so
|
|
84
|
+
// `data` stays valid for as long as libyaml can call `read_handler`.
|
|
83
85
|
unsafe {
|
|
84
|
-
unsafe_yaml::yaml_parser_set_input(
|
|
85
|
-
&mut *raw,
|
|
86
|
-
read_handler,
|
|
87
|
-
data.cast::<c_void>(),
|
|
88
|
-
);
|
|
86
|
+
unsafe_yaml::yaml_parser_set_input(raw, read_handler, data.cast::<c_void>());
|
|
89
87
|
}
|
|
90
|
-
feed = Some(
|
|
88
|
+
feed = Some(data);
|
|
91
89
|
}
|
|
92
90
|
}
|
|
93
91
|
Self {
|
|
@@ -103,8 +101,7 @@ impl<'a> Parser<'a> {
|
|
|
103
101
|
let mut event = MaybeUninit::<unsafe_yaml::yaml_event_t>::uninit();
|
|
104
102
|
// SAFETY: the parser is initialised with an input; `yaml_parser_parse` zero-fills the
|
|
105
103
|
// event before doing anything else, so it is initialised whatever the outcome.
|
|
106
|
-
let failed =
|
|
107
|
-
unsafe { unsafe_yaml::yaml_parser_parse(&mut *self.raw, event.as_mut_ptr()).fail };
|
|
104
|
+
let failed = unsafe { unsafe_yaml::yaml_parser_parse(self.raw, event.as_mut_ptr()).fail };
|
|
108
105
|
if failed {
|
|
109
106
|
return Err(self.error());
|
|
110
107
|
}
|
|
@@ -123,11 +120,13 @@ impl<'a> Parser<'a> {
|
|
|
123
120
|
}
|
|
124
121
|
|
|
125
122
|
fn error(&self) -> String {
|
|
126
|
-
|
|
123
|
+
// SAFETY: the feed is live until `drop`, and libyaml is not running.
|
|
124
|
+
if let Some(message) = self.feed.and_then(|f| unsafe { (*f).failure.clone() }) {
|
|
127
125
|
// The Python exception raised inside the stream's read() propagates unchanged.
|
|
128
126
|
return message;
|
|
129
127
|
}
|
|
130
|
-
|
|
128
|
+
// SAFETY: the parser is live until `drop`, and libyaml is not running.
|
|
129
|
+
let p: &unsafe_yaml::yaml_parser_t = unsafe { &*self.raw };
|
|
131
130
|
// SAFETY: libyaml sets `problem`/`context` to static NUL-terminated strings or null.
|
|
132
131
|
let problem = unsafe { c_str(p.problem) }.map(|problem| {
|
|
133
132
|
// unsafe-libyaml fixed a typo that libyaml 0.2.5 (bundled with PyYAML) still prints.
|
|
@@ -165,8 +164,16 @@ impl<'a> Parser<'a> {
|
|
|
165
164
|
|
|
166
165
|
impl Drop for Parser<'_> {
|
|
167
166
|
fn drop(&mut self) {
|
|
168
|
-
// SAFETY: the parser was initialised in `new` and is deleted exactly once
|
|
169
|
-
|
|
167
|
+
// SAFETY: the parser was initialised in `new` and is deleted exactly once; the parser
|
|
168
|
+
// and the feed were allocated with `Box::into_raw` in `new` and are freed exactly once,
|
|
169
|
+
// after libyaml can no longer reach them.
|
|
170
|
+
unsafe {
|
|
171
|
+
unsafe_yaml::yaml_parser_delete(self.raw);
|
|
172
|
+
drop(Box::from_raw(self.raw));
|
|
173
|
+
if let Some(feed) = self.feed {
|
|
174
|
+
drop(Box::from_raw(feed));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
170
177
|
}
|
|
171
178
|
}
|
|
172
179
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
use super::bigint::BigInt;
|
|
7
7
|
use super::pyfmt::{lower, repr_str, repr_str_200, strip, unicode_encode_error};
|
|
8
|
+
use crate::pyobj::decimal_to_ascii;
|
|
8
9
|
|
|
9
10
|
/// `sys.get_int_max_str_digits()` default.
|
|
10
11
|
const MAX_STR_DIGITS: usize = 4300;
|
|
@@ -73,7 +74,8 @@ pub(super) fn py_int(s: &str, base: u32) -> Result<BigInt, String> {
|
|
|
73
74
|
repr_str_200(s)
|
|
74
75
|
)
|
|
75
76
|
};
|
|
76
|
-
let
|
|
77
|
+
let ascii = decimal_to_ascii(s).ok_or_else(invalid)?;
|
|
78
|
+
let t = strip(&ascii);
|
|
77
79
|
let (negative, t) = match t.as_bytes().first() {
|
|
78
80
|
Some(b'-') => (true, &t[1..]),
|
|
79
81
|
Some(b'+') => (false, &t[1..]),
|
|
@@ -147,9 +149,11 @@ pub(super) fn float_value(s: &str) -> Result<FloatValue, String> {
|
|
|
147
149
|
Ok(FloatValue::Num(sign * py_float(value)?))
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
/// Python's `float(s)
|
|
152
|
+
/// Python's `float(s)`.
|
|
151
153
|
pub(super) fn py_float(s: &str) -> Result<f64, String> {
|
|
152
|
-
let
|
|
154
|
+
let invalid = || format!("could not convert string to float: {}", repr_str(s));
|
|
155
|
+
let ascii = decimal_to_ascii(s).ok_or_else(invalid)?;
|
|
156
|
+
let t = strip(&ascii);
|
|
153
157
|
// Rust's grammar matches Python's for these strings: optional sign, digits with an optional
|
|
154
158
|
// point and exponent, or inf/infinity/nan in any case. Underscores are already gone.
|
|
155
159
|
let plausible = !t.is_empty()
|
|
@@ -158,7 +162,7 @@ pub(super) fn py_float(s: &str) -> Result<f64, String> {
|
|
|
158
162
|
plausible
|
|
159
163
|
.then(|| t.parse::<f64>().ok())
|
|
160
164
|
.flatten()
|
|
161
|
-
.ok_or_else(
|
|
165
|
+
.ok_or_else(invalid)
|
|
162
166
|
}
|
|
163
167
|
|
|
164
168
|
/// `construct_yaml_binary`'s checks; the bytes themselves are never needed because
|