From 21ce69ef376153efbe1308e466a9df795712a13b Mon Sep 17 00:00:00 2001 From: dacian Date: Fri, 25 Oct 2024 00:05:11 +0300 Subject: [PATCH] feat: unstable-json-schema generation for Cargo.toml --- Cargo.lock | 74 +- Cargo.toml | 1 + crates/cargo-util-schemas/Cargo.toml | 5 + .../cargo-util-schemas/manifest.schema.json | 1485 +++++++++++++++++ crates/cargo-util-schemas/src/lib.rs | 2 + crates/cargo-util-schemas/src/manifest/mod.rs | 64 + crates/cargo-util-schemas/src/schema.rs | 54 + 7 files changed, 1679 insertions(+), 6 deletions(-) create mode 100644 crates/cargo-util-schemas/manifest.schema.json create mode 100644 crates/cargo-util-schemas/src/schema.rs diff --git a/Cargo.lock b/Cargo.lock index a943b8b8c40..dd5ff342bbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -327,7 +327,7 @@ dependencies = [ "humantime", "ignore", "im-rc", - "indexmap", + "indexmap 2.3.0", "itertools 0.13.0", "jobserver", "lazycell", @@ -492,10 +492,12 @@ dependencies = [ name = "cargo-util-schemas" version = "0.7.1" dependencies = [ + "schemars", "semver", "serde", "serde-untagged", "serde-value", + "serde_json", "snapbox", "thiserror", "toml", @@ -902,6 +904,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "ecdsa" version = "0.16.9" @@ -1466,7 +1474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ddf80e16f3c19ac06ce415a38b8591993d3f73aede049cb561becb5b3a8e242" dependencies = [ "gix-hash", - "hashbrown", + "hashbrown 0.14.5", "parking_lot", ] @@ -1502,7 +1510,7 @@ dependencies = [ "gix-traverse", "gix-utils", "gix-validate", - "hashbrown", + "hashbrown 0.14.5", "itoa 1.0.11", "libc", "memmap2", @@ -1959,6 +1967,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.5" @@ -1975,7 +1989,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -2088,6 +2102,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.3.0" @@ -2095,7 +2120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -3077,6 +3102,32 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "schemars" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "semver", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.72", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -3177,6 +3228,17 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "serde_ignored" version = "0.1.10" @@ -3557,7 +3619,7 @@ version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap", + "indexmap 2.3.0", "serde", "serde_spanned", "toml_datetime", diff --git a/Cargo.toml b/Cargo.toml index 18b5015a0e6..1d5c83bd2c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ rusqlite = { version = "0.32.0", features = ["bundled"] } rustc-hash = "2.0.0" rustfix = { version = "0.8.2", path = "crates/rustfix" } same-file = "1.0.6" +schemars = "0.8.21" security-framework = "2.11.1" semver = { version = "1.0.23", features = ["serde"] } serde = "1.0.204" diff --git a/crates/cargo-util-schemas/Cargo.toml b/crates/cargo-util-schemas/Cargo.toml index 964d9e34a39..d0b38904d70 100644 --- a/crates/cargo-util-schemas/Cargo.toml +++ b/crates/cargo-util-schemas/Cargo.toml @@ -9,8 +9,10 @@ repository.workspace = true description = "Deserialization schemas for Cargo" [dependencies] +schemars = { workspace = true, features = ["preserve_order","semver"], optional = true } semver.workspace = true serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true, optional = true } serde-untagged.workspace = true serde-value.workspace = true thiserror.workspace = true @@ -23,3 +25,6 @@ workspace = true [dev-dependencies] snapbox.workspace = true + +[features] +unstable-schema = ["dep:schemars", "dep:serde_json"] diff --git a/crates/cargo-util-schemas/manifest.schema.json b/crates/cargo-util-schemas/manifest.schema.json new file mode 100644 index 00000000000..000f15f2300 --- /dev/null +++ b/crates/cargo-util-schemas/manifest.schema.json @@ -0,0 +1,1485 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TomlManifest", + "description": "This type is used to deserialize `Cargo.toml` files.", + "type": "object", + "properties": { + "cargo-features": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "package": { + "anyOf": [ + { + "$ref": "#/definitions/TomlPackage" + }, + { + "type": "null" + } + ] + }, + "project": { + "anyOf": [ + { + "$ref": "#/definitions/TomlPackage" + }, + { + "type": "null" + } + ] + }, + "profile": { + "anyOf": [ + { + "$ref": "#/definitions/TomlProfiles" + }, + { + "type": "null" + } + ] + }, + "lib": { + "anyOf": [ + { + "$ref": "#/definitions/TomlTarget" + }, + { + "type": "null" + } + ] + }, + "bin": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/TomlTarget" + } + }, + "example": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/TomlTarget" + } + }, + "test": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/TomlTarget" + } + }, + "bench": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/TomlTarget" + } + }, + "dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "dev-dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "dev_dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "build-dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "build_dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "features": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "target": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/TomlPlatform" + } + }, + "replace": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/TomlDependency_for_String" + } + }, + "patch": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/TomlDependency_for_String" + } + } + }, + "workspace": { + "anyOf": [ + { + "$ref": "#/definitions/TomlWorkspace" + }, + { + "type": "null" + } + ] + }, + "badges": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "lints": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableLints" + }, + { + "type": "null" + } + ] + } + }, + "definitions": { + "TomlPackage": { + "description": "Represents the `package`/`project` sections of a `Cargo.toml`./n/nNote that the order of the fields matters, since this is the order they are serialized to a TOML file. For example, you cannot have values after the field `metadata`, since it is a table and values cannot appear after tables.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "edition": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_String" + }, + { + "type": "null" + } + ] + }, + "rust-version": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "version": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_Version" + }, + { + "type": "null" + } + ] + }, + "authors": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_Array_of_String" + }, + { + "type": "null" + } + ] + }, + "build": { + "anyOf": [ + { + "$ref": "#/definitions/StringOrBool" + }, + { + "type": "null" + } + ] + }, + "metabuild": { + "anyOf": [ + { + "$ref": "#/definitions/StringOrVec" + }, + { + "type": "null" + } + ] + }, + "default-target": { + "type": [ + "string", + "null" + ] + }, + "forced-target": { + "type": [ + "string", + "null" + ] + }, + "links": { + "type": [ + "string", + "null" + ] + }, + "exclude": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_Array_of_String" + }, + { + "type": "null" + } + ] + }, + "include": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_Array_of_String" + }, + { + "type": "null" + } + ] + }, + "publish": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_VecStringOrBool" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "type": [ + "string", + "null" + ] + }, + "im-a-teapot": { + "type": [ + "boolean", + "null" + ] + }, + "autolib": { + "type": [ + "boolean", + "null" + ] + }, + "autobins": { + "type": [ + "boolean", + "null" + ] + }, + "autoexamples": { + "type": [ + "boolean", + "null" + ] + }, + "autotests": { + "type": [ + "boolean", + "null" + ] + }, + "autobenches": { + "type": [ + "boolean", + "null" + ] + }, + "default-run": { + "type": [ + "string", + "null" + ] + }, + "description": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_String" + }, + { + "type": "null" + } + ] + }, + "homepage": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_String" + }, + { + "type": "null" + } + ] + }, + "documentation": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_String" + }, + { + "type": "null" + } + ] + }, + "readme": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_StringOrBool" + }, + { + "type": "null" + } + ] + }, + "keywords": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_Array_of_String" + }, + { + "type": "null" + } + ] + }, + "categories": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_Array_of_String" + }, + { + "type": "null" + } + ] + }, + "license": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_String" + }, + { + "type": "null" + } + ] + }, + "license-file": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_String" + }, + { + "type": "null" + } + ] + }, + "repository": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableField_for_String" + }, + { + "type": "null" + } + ] + }, + "resolver": { + "type": [ + "string", + "null" + ] + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/definitions/TomlValue" + }, + { + "type": "null" + } + ] + } + } + }, + "InheritableField_for_String": { + "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", + "anyOf": [ + { + "description": "The type that is used when not inheriting from a workspace.", + "type": "string" + }, + { + "description": "The type when inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/TomlInheritedField" + } + ] + } + ] + }, + "TomlInheritedField": { + "type": "object", + "required": [ + "workspace" + ], + "properties": { + "workspace": { + "$ref": "#/definitions/WorkspaceValue" + } + } + }, + "WorkspaceValue": { + "type": "null" + }, + "InheritableField_for_Version": { + "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", + "anyOf": [ + { + "description": "The type that is used when not inheriting from a workspace.", + "type": "string", + "pattern": "^(0|[1-9]//d*)//.(0|[1-9]//d*)//.(0|[1-9]//d*)(?:-((?:0|[1-9]//d*|//d*[a-zA-Z-][0-9a-zA-Z-]*)(?://.(?:0|[1-9]//d*|//d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?://+([0-9a-zA-Z-]+(?://.[0-9a-zA-Z-]+)*))?$" + }, + { + "description": "The type when inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/TomlInheritedField" + } + ] + } + ] + }, + "InheritableField_for_Array_of_String": { + "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", + "anyOf": [ + { + "description": "The type that is used when not inheriting from a workspace.", + "type": "array", + "items": { + "type": "string" + } + }, + { + "description": "The type when inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/TomlInheritedField" + } + ] + } + ] + }, + "StringOrBool": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ] + }, + "StringOrVec": { + "description": "A StringOrVec can be parsed from either a TOML string or array, but is always stored as a vector.", + "type": "array", + "items": { + "type": "string" + } + }, + "InheritableField_for_VecStringOrBool": { + "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", + "anyOf": [ + { + "description": "The type that is used when not inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/VecStringOrBool" + } + ] + }, + { + "description": "The type when inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/TomlInheritedField" + } + ] + } + ] + }, + "VecStringOrBool": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "boolean" + } + ] + }, + "InheritableField_for_StringOrBool": { + "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", + "anyOf": [ + { + "description": "The type that is used when not inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/StringOrBool" + } + ] + }, + { + "description": "The type when inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/TomlInheritedField" + } + ] + } + ] + }, + "TomlValue": { + "type": "object", + "properties": { + "string": { + "type": "string" + }, + "integer": { + "type": "integer", + "format": "int64" + }, + "float": { + "type": "number", + "format": "double" + }, + "boolean": { + "type": "boolean" + }, + "datetime": { + "type": "string" + }, + "array": { + "type": "array", + "items": { + "$ref": "#/definitions/TomlValue" + } + }, + "table": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/TomlValue" + } + } + } + }, + "TomlProfiles": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/TomlProfile" + } + }, + "TomlProfile": { + "type": "object", + "properties": { + "opt-level": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/TomlOptLevel" + }, + { + "type": "null" + } + ] + }, + "lto": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/StringOrBool" + }, + { + "type": "null" + } + ] + }, + "codegen-backend": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "codegen-units": { + "default": null, + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "debug": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/TomlDebugInfo" + }, + { + "type": "null" + } + ] + }, + "split-debuginfo": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "debug-assertions": { + "default": null, + "type": [ + "boolean", + "null" + ] + }, + "rpath": { + "default": null, + "type": [ + "boolean", + "null" + ] + }, + "panic": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "overflow-checks": { + "default": null, + "type": [ + "boolean", + "null" + ] + }, + "incremental": { + "default": null, + "type": [ + "boolean", + "null" + ] + }, + "dir-name": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "inherits": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "strip": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/StringOrBool" + }, + { + "type": "null" + } + ] + }, + "rustflags": { + "default": null, + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "package": { + "default": null, + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/TomlProfile" + } + }, + "build-override": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/TomlProfile" + }, + { + "type": "null" + } + ] + }, + "trim-paths": { + "description": "Unstable feature `-Ztrim-paths`.", + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/TomlTrimPaths" + }, + { + "type": "null" + } + ] + } + } + }, + "TomlOptLevel": { + "type": "string" + }, + "TomlDebugInfo": { + "type": "string", + "enum": [ + "None", + "LineDirectivesOnly", + "LineTablesOnly", + "Limited", + "Full" + ] + }, + "TomlTrimPaths": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/TomlTrimPathsValue" + } + }, + { + "type": "null" + } + ] + }, + "TomlTrimPathsValue": { + "type": "string", + "enum": [ + "diagnostics", + "macro", + "object" + ] + }, + "TomlTarget": { + "type": "object", + "properties": { + "name": { + "type": [ + "string", + "null" + ] + }, + "crate-type": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "crate_type": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "filename": { + "type": [ + "string", + "null" + ] + }, + "test": { + "type": [ + "boolean", + "null" + ] + }, + "doctest": { + "type": [ + "boolean", + "null" + ] + }, + "bench": { + "type": [ + "boolean", + "null" + ] + }, + "doc": { + "type": [ + "boolean", + "null" + ] + }, + "doc-scrape-examples": { + "type": [ + "boolean", + "null" + ] + }, + "proc-macro": { + "type": [ + "boolean", + "null" + ] + }, + "proc_macro": { + "type": [ + "boolean", + "null" + ] + }, + "harness": { + "type": [ + "boolean", + "null" + ] + }, + "required-features": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "edition": { + "type": [ + "string", + "null" + ] + } + } + }, + "InheritableDependency": { + "anyOf": [ + { + "description": "The type that is used when not inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/TomlDependency_for_String" + } + ] + }, + { + "description": "The type when inheriting from a workspace.", + "allOf": [ + { + "$ref": "#/definitions/TomlInheritedDependency" + } + ] + } + ] + }, + "TomlDependency_for_String": { + "anyOf": [ + { + "description": "In the simple format, only a version is specified, eg. `package = /"/"`", + "type": "string" + }, + { + "description": "The simple format is equivalent to a detailed dependency specifying only a version, eg. `package = { version = /"/" }`", + "allOf": [ + { + "$ref": "#/definitions/TomlDetailedDependency_for_String" + } + ] + } + ] + }, + "TomlDetailedDependency_for_String": { + "type": "object", + "properties": { + "version": { + "type": [ + "string", + "null" + ] + }, + "registry": { + "type": [ + "string", + "null" + ] + }, + "registry-index": { + "description": "The URL of the `registry` field. This is an internal implementation detail. When Cargo creates a package, it replaces `registry` with `registry-index` so that the manifest contains the correct URL. All users won't have the same registry names configured, so Cargo can't rely on just the name for crates published by other users.", + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "base": { + "type": [ + "string", + "null" + ] + }, + "git": { + "type": [ + "string", + "null" + ] + }, + "branch": { + "type": [ + "string", + "null" + ] + }, + "tag": { + "type": [ + "string", + "null" + ] + }, + "rev": { + "type": [ + "string", + "null" + ] + }, + "features": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "optional": { + "type": [ + "boolean", + "null" + ] + }, + "default-features": { + "type": [ + "boolean", + "null" + ] + }, + "default_features": { + "type": [ + "boolean", + "null" + ] + }, + "package": { + "type": [ + "string", + "null" + ] + }, + "public": { + "type": [ + "boolean", + "null" + ] + }, + "artifact": { + "description": "One or more of `bin`, `cdylib`, `staticlib`, `bin:`.", + "anyOf": [ + { + "$ref": "#/definitions/StringOrVec" + }, + { + "type": "null" + } + ] + }, + "lib": { + "description": "If set, the artifact should also be a dependency", + "type": [ + "boolean", + "null" + ] + }, + "target": { + "description": "A platform name, like `x86_64-apple-darwin`", + "type": [ + "string", + "null" + ] + } + } + }, + "TomlInheritedDependency": { + "type": "object", + "required": [ + "workspace" + ], + "properties": { + "workspace": { + "type": "boolean" + }, + "features": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "default-features": { + "type": [ + "boolean", + "null" + ] + }, + "default_features": { + "type": [ + "boolean", + "null" + ] + }, + "optional": { + "type": [ + "boolean", + "null" + ] + }, + "public": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "TomlPlatform": { + "description": "Corresponds to a `target` entry, but `TomlTarget` is already used.", + "type": "object", + "properties": { + "dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "build-dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "build_dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "dev-dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + }, + "dev_dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/InheritableDependency" + } + } + } + }, + "TomlWorkspace": { + "type": "object", + "properties": { + "members": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "exclude": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "default-members": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "resolver": { + "type": [ + "string", + "null" + ] + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/definitions/TomlValue" + }, + { + "type": "null" + } + ] + }, + "package": { + "anyOf": [ + { + "$ref": "#/definitions/InheritablePackage" + }, + { + "type": "null" + } + ] + }, + "dependencies": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/TomlDependency_for_String" + } + }, + "lints": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/TomlLint" + } + } + } + } + }, + "InheritablePackage": { + "description": "A group of fields that are inheritable by members of the workspace", + "type": "object", + "properties": { + "version": { + "type": [ + "string", + "null" + ], + "pattern": "^(0|[1-9]//d*)//.(0|[1-9]//d*)//.(0|[1-9]//d*)(?:-((?:0|[1-9]//d*|//d*[a-zA-Z-][0-9a-zA-Z-]*)(?://.(?:0|[1-9]//d*|//d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?://+([0-9a-zA-Z-]+(?://.[0-9a-zA-Z-]+)*))?$" + }, + "authors": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "homepage": { + "type": [ + "string", + "null" + ] + }, + "documentation": { + "type": [ + "string", + "null" + ] + }, + "readme": { + "anyOf": [ + { + "$ref": "#/definitions/StringOrBool" + }, + { + "type": "null" + } + ] + }, + "keywords": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "categories": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "license": { + "type": [ + "string", + "null" + ] + }, + "license-file": { + "type": [ + "string", + "null" + ] + }, + "repository": { + "type": [ + "string", + "null" + ] + }, + "publish": { + "anyOf": [ + { + "$ref": "#/definitions/VecStringOrBool" + }, + { + "type": "null" + } + ] + }, + "edition": { + "type": [ + "string", + "null" + ] + }, + "badges": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "exclude": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "include": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "rust-version": { + "type": [ + "string", + "null" + ] + } + } + }, + "TomlLint": { + "anyOf": [ + { + "$ref": "#/definitions/TomlLintLevel" + }, + { + "$ref": "#/definitions/TomlLintConfig" + } + ] + }, + "TomlLintLevel": { + "type": "string", + "enum": [ + "forbid", + "deny", + "warn", + "allow" + ] + }, + "TomlLintConfig": { + "type": "object", + "required": [ + "level" + ], + "properties": { + "level": { + "$ref": "#/definitions/TomlLintLevel" + }, + "priority": { + "default": 0, + "type": "integer", + "format": "int8" + } + } + }, + "InheritableLints": { + "type": "object", + "required": [ + "workspace" + ], + "properties": { + "workspace": { + "type": "boolean" + } + } + } + } +} \ No newline at end of file diff --git a/crates/cargo-util-schemas/src/lib.rs b/crates/cargo-util-schemas/src/lib.rs index 910438155e9..e3a51284f07 100644 --- a/crates/cargo-util-schemas/src/lib.rs +++ b/crates/cargo-util-schemas/src/lib.rs @@ -10,5 +10,7 @@ pub mod core; pub mod manifest; +#[cfg(feature = "unstable-schema")] +pub mod schema; mod restricted_names; diff --git a/crates/cargo-util-schemas/src/manifest/mod.rs b/crates/cargo-util-schemas/src/manifest/mod.rs index 0fa5be5c12d..8442e62cc52 100644 --- a/crates/cargo-util-schemas/src/manifest/mod.rs +++ b/crates/cargo-util-schemas/src/manifest/mod.rs @@ -7,6 +7,8 @@ use std::collections::BTreeMap; use std::collections::BTreeSet; +#[cfg(feature = "unstable-schema")] +use std::collections::HashMap; use std::fmt::{self, Display, Write}; use std::path::PathBuf; use std::str; @@ -25,9 +27,13 @@ pub use crate::restricted_names::NameValidationError; pub use rust_version::RustVersion; pub use rust_version::RustVersionError; +#[cfg(feature = "unstable-schema")] +use crate::schema::TomlValueWrapper; + /// This type is used to deserialize `Cargo.toml` files. #[derive(Default, Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlManifest { // when adding new fields, be sure to check whether `requires_package` should disallow them pub cargo_features: Option>, @@ -113,11 +119,17 @@ impl TomlManifest { #[derive(Debug, Default, Deserialize, Serialize, Clone)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlWorkspace { pub members: Option>, pub exclude: Option>, pub default_members: Option>, pub resolver: Option, + + #[cfg_attr( + feature = "unstable-schema", + schemars(with = "Option") + )] pub metadata: Option, // Properties that can be inherited by members. @@ -129,6 +141,7 @@ pub struct TomlWorkspace { /// A group of fields that are inheritable by members of the workspace #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct InheritablePackage { pub version: Option, pub authors: Option>, @@ -146,6 +159,7 @@ pub struct InheritablePackage { pub badges: Option>>, pub exclude: Option>, pub include: Option>, + #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub rust_version: Option, } @@ -157,9 +171,12 @@ pub struct InheritablePackage { /// tables. #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlPackage { pub edition: Option, + #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub rust_version: Option, + #[cfg_attr(feature = "unstable-schema", schemars(with = "String"))] pub name: PackageName, pub version: Option, pub authors: Option, @@ -192,10 +209,15 @@ pub struct TomlPackage { pub repository: Option, pub resolver: Option, + #[cfg_attr( + feature = "unstable-schema", + schemars(with = "Option") + )] pub metadata: Option, /// Provide a helpful error message for a common user error. #[serde(rename = "cargo-features", skip_serializing)] + #[cfg_attr(feature = "unstable-schema", schemars(skip))] pub _invalid_cargo_features: Option, } @@ -333,6 +355,7 @@ impl TomlPackage { /// An enum that allows for inheriting keys from a workspace in a Cargo.toml. #[derive(Serialize, Copy, Clone, Debug)] #[serde(untagged)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum InheritableField { /// The type that is used when not inheriting from a workspace. Value(T), @@ -588,6 +611,7 @@ impl<'de> de::Deserialize<'de> for InheritableBtreeMap { #[derive(Deserialize, Serialize, Copy, Clone, Debug)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlInheritedField { workspace: WorkspaceValue, } @@ -609,6 +633,7 @@ impl Default for TomlInheritedField { #[derive(Deserialize, Serialize, Copy, Clone, Debug)] #[serde(try_from = "bool")] #[serde(into = "bool")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] struct WorkspaceValue; impl TryFrom for WorkspaceValue { @@ -630,6 +655,7 @@ impl From for bool { #[derive(Serialize, Clone, Debug)] #[serde(untagged)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum InheritableDependency { /// The type that is used when not inheriting from a workspace. Value(TomlDependency), @@ -677,6 +703,7 @@ impl<'de> de::Deserialize<'de> for InheritableDependency { #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlInheritedDependency { pub workspace: bool, pub features: Option>, @@ -689,6 +716,7 @@ pub struct TomlInheritedDependency { /// This is here to provide a way to see the "unused manifest keys" when deserializing #[serde(skip_serializing)] #[serde(flatten)] + #[cfg_attr(feature = "unstable-schema", schemars(skip))] pub _unused_keys: BTreeMap, } @@ -700,6 +728,7 @@ impl TomlInheritedDependency { #[derive(Clone, Debug, Serialize)] #[serde(untagged)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlDependency { /// In the simple format, only a version is specified, eg. /// `package = ""` @@ -765,8 +794,11 @@ impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency

{ pub version: Option, + + #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub registry: Option, /// The URL of the `registry` field. /// This is an internal implementation detail. When Cargo creates a @@ -778,6 +810,7 @@ pub struct TomlDetailedDependency { // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file. pub path: Option

, + #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub base: Option, pub git: Option, pub branch: Option, @@ -788,6 +821,7 @@ pub struct TomlDetailedDependency { pub default_features: Option, #[serde(rename = "default_features")] pub default_features2: Option, + #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub package: Option, pub public: Option, @@ -801,6 +835,7 @@ pub struct TomlDetailedDependency { /// This is here to provide a way to see the "unused manifest keys" when deserializing #[serde(skip_serializing)] #[serde(flatten)] + #[cfg_attr(feature = "unstable-schema", schemars(skip))] pub _unused_keys: BTreeMap, } @@ -838,6 +873,7 @@ impl Default for TomlDetailedDependency

{ } #[derive(Deserialize, Serialize, Clone, Debug, Default)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlProfiles(pub BTreeMap); impl TomlProfiles { @@ -852,6 +888,7 @@ impl TomlProfiles { #[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)] #[serde(default, rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlProfile { pub opt_level: Option, pub lto: Option, @@ -1010,6 +1047,7 @@ impl<'de> de::Deserialize<'de> for ProfilePackageSpec { } #[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlOptLevel(pub String); impl ser::Serialize for TomlOptLevel { @@ -1049,6 +1087,7 @@ impl<'de> de::Deserialize<'de> for TomlOptLevel { } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlDebugInfo { None, LineDirectivesOnly, @@ -1136,6 +1175,7 @@ impl<'de> de::Deserialize<'de> for TomlDebugInfo { #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)] #[serde(untagged, rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlTrimPaths { Values(Vec), All, @@ -1228,6 +1268,7 @@ impl From> for TomlTrimPaths { #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlTrimPathsValue { Diagnostics, Macro, @@ -1258,6 +1299,7 @@ pub type TomlBenchTarget = TomlTarget; #[derive(Default, Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlTarget { pub name: Option, @@ -1267,6 +1309,7 @@ pub struct TomlTarget { #[serde(rename = "crate_type")] pub crate_type2: Option>, + #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub path: Option, // Note that `filename` is used for the cargo-feature `different_binary_name` pub filename: Option, @@ -1430,6 +1473,7 @@ impl> PathBaseName { /// Corresponds to a `target` entry, but `TomlTarget` is already used. #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlPlatform { pub dependencies: Option>, pub build_dependencies: Option>, @@ -1455,6 +1499,7 @@ impl TomlPlatform { } #[derive(Serialize, Debug, Clone)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct InheritableLints { #[serde(skip_serializing_if = "is_false")] pub workspace: bool, @@ -1530,6 +1575,7 @@ pub type TomlToolLints = BTreeMap; #[derive(Serialize, Debug, Clone)] #[serde(untagged)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlLint { Level(TomlLintLevel), Config(TomlLintConfig), @@ -1574,16 +1620,22 @@ impl TomlLint { #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlLintConfig { pub level: TomlLintLevel, #[serde(default)] pub priority: i8, #[serde(flatten)] + #[cfg_attr( + feature = "unstable-schema", + schemars(with = "HashMap") + )] pub config: toml::Table, } #[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlLintLevel { Forbid, Deny, @@ -1610,6 +1662,7 @@ impl<'de> de::Deserialize<'de> for InvalidCargoFeatures { /// A StringOrVec can be parsed from either a TOML string or array, /// but is always stored as a vector. #[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct StringOrVec(pub Vec); impl StringOrVec { @@ -1633,6 +1686,7 @@ impl<'de> de::Deserialize<'de> for StringOrVec { #[derive(Clone, Debug, Serialize, Eq, PartialEq)] #[serde(untagged)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum StringOrBool { String(String), Bool(bool), @@ -1652,6 +1706,7 @@ impl<'de> Deserialize<'de> for StringOrBool { #[derive(PartialEq, Clone, Debug, Serialize)] #[serde(untagged)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum VecStringOrBool { VecString(Vec), Bool(bool), @@ -1701,4 +1756,13 @@ impl<'de> de::Deserialize<'de> for PathValue { #[derive(Debug, thiserror::Error)] #[error("manifest field was not resolved")] #[non_exhaustive] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct UnresolvedError; + +#[cfg(feature = "unstable-schema")] +#[test] +fn dump_manifest_schema() { + let schema = schemars::schema_for!(crate::manifest::TomlManifest); + let dump = serde_json::to_string_pretty(&schema).unwrap(); + snapbox::assert_data_eq!(dump, snapbox::file!("../../manifest.schema.json")); +} diff --git a/crates/cargo-util-schemas/src/schema.rs b/crates/cargo-util-schemas/src/schema.rs new file mode 100644 index 00000000000..f9ce16baa9c --- /dev/null +++ b/crates/cargo-util-schemas/src/schema.rs @@ -0,0 +1,54 @@ +use schemars::JsonSchema; + +use serde::{Deserialize, Serialize}; + +use std::collections::HashMap; +use std::string::String; + +use toml::Value as TomlValue; + +#[derive(Serialize, Deserialize)] +pub struct TomlValueWrapper(pub TomlValue); + +impl JsonSchema for TomlValueWrapper { + fn schema_name() -> String { + "TomlValue".to_string() + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + use schemars::schema::*; + + SchemaObject { + instance_type: Some(InstanceType::Object.into()), + object: Some(Box::new(ObjectValidation { + properties: [ + ( + "string".to_string(), + gen.subschema_for::(), + ), + ("integer".to_string(), gen.subschema_for::()), + ("float".to_string(), gen.subschema_for::()), + ("boolean".to_string(), gen.subschema_for::()), + ( + "datetime".to_string(), + gen.subschema_for::(), + ), // Assuming datetime is represented as a string + ( + "array".to_string(), + gen.subschema_for::>(), + ), + ( + "table".to_string(), + gen.subschema_for::>(), + ), + ] + .iter() + .cloned() + .collect(), + ..Default::default() + })), + ..Default::default() + } + .into() + } +}