Skip to content
Draft
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
128 changes: 128 additions & 0 deletions lib/dsc-lib-jsonschema/src/dsc_repo/dsc_repo_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,134 @@ pub trait DscRepoSchema : JsonSchema {
///
/// Returns a [`DscRepoSchemaMissingTranslationError`] error if the translation key doesn't exist.
fn schema_i18n(suffix: &str) -> Result<String, DscRepoSchemaMissingTranslationError>;

/// Transforms the `$schema` and `$id` fields of the given schema to use the default export URIs.
///
/// This associated function modifies the given schema in-place to use the default export URIs for the `$schema`
/// and `$id` fields.
///
/// # Arguments
///
/// - `schema` - The mutable reference to the schema to be transformed.
///
/// # Example
///
/// The first definition shows how you must manually define the `$schema` and `$id` fields
/// without this transformer.
///
/// ```
/// # use schemars::JsonSchema;
/// # use dsc_lib_jsonschema::dsc_repo::DscRepoSchema;
///
/// #[derive(Debug, Clone, JsonSchema, DscRepoSchema)]
/// #[dsc_repo_schema(base_name = "struct", folder_path = "examples")]
/// #[schemars(
/// extend(
/// "$schema" = ExampleStruct::default_export_meta_schema_uri(),
/// "$id" = ExampleStruct::default_export_schema_id_uri(),
/// )
/// )]
/// struct ExampleStruct {
/// pub field: String,
/// }
///
/// let schema = schema_for!(ExampleStruct);
///
/// assert_eq!(schema.get_meta_schema(), Some(ExampleStruct::default_export_meta_schema_uri()));
/// assert_eq!(schema.get_id(), Some(ExampleStruct::default_export_schema_id_uri()));
/// ```
///
/// With this transform, the definition is much shorter:
///
/// ```
/// # use schemars::JsonSchema;
/// # use dsc_lib_jsonschema::dsc_repo::DscRepoSchema;
///
/// #[derive(Debug, Clone, JsonSchema, DscRepoSchema)]
/// #[dsc_repo_schema(base_name = "struct", folder_path = "examples")]
/// #[schemars(transform = "ExampleStruct::transform_export_schema_uris")]
/// struct ExampleStruct {
/// pub field: String,
/// }
///
/// let schema = schema_for!(ExampleStruct);
///
/// assert_eq!(schema.get_meta_schema(), Some(ExampleStruct::default_export_meta_schema_uri()));
/// assert_eq!(schema.get_id(), Some(ExampleStruct::default_export_schema_id_uri()));
/// ```
fn transform_export_schema_uris(schema: &mut schemars::Schema) {
schema.set_meta_schema(&Self::default_export_meta_schema_uri());
schema.set_id(&Self::default_export_schema_id_uri());
}

/// Inserts the localized documentation (title, description, and markdown description) into the
/// schema if available.
///
/// This transform overrides the following keywords in the schema if the matching localized
/// documentation is available:
///
/// - `title`
/// - `description`
/// - `markdownDescription`
///
/// To panic on missing translations, use the [`transform_schema_docs_strict`] associated
/// function instead.
///
/// [`transform_schema_docs_strict`]: Self::transform_schema_docs_strict
fn transform_schema_docs(schema: &mut schemars::Schema) {
use super::super::vscode::VSCodeSchemaExtensions;

if let Ok(title) = Self::schema_i18n("title") {
schema.set_title(&title);
}
if let Ok(description) = Self::schema_i18n("description") {
schema.set_description(&description);
}
if let Ok(markdown_description) = Self::schema_i18n("markdownDescription") {
schema.set_markdown_description(&markdown_description);
}
}

/// Inserts the localized documentation (title, description, and markdown description) into the
/// schema and panics if any translations are missing.
///
/// This transform overrides the following keywords in the schema if the matching localized
/// documentation is available:
///
/// - `title`
/// - `description`
/// - `markdownDescription`
///
/// To silently ignore missing translations, use the [`transform_schema_docs`] associated
/// function instead.
///
/// # Panics
///
/// This function will panic if any of the localized documentation translations are missing for
/// any of the keywords.
///
/// [`transform_schema_docs`]: Self::transform_schema_docs
fn transform_schema_docs_strict(schema: &mut schemars::Schema) {
use super::super::vscode::VSCodeSchemaExtensions;
let mut missing_translation_errors: Vec<DscRepoSchemaMissingTranslationError> = Vec::new();
match Self::schema_i18n("title") {
Ok(title) => { schema.set_title(&title); },
Err(e) => missing_translation_errors.push(e),
}
match Self::schema_i18n("description") {
Ok(description) => { schema.set_description(&description); },
Err(e) => missing_translation_errors.push(e),
}
match Self::schema_i18n("markdownDescription") {
Ok(markdown_description) => { schema.set_markdown_description(&markdown_description); },
Err(e) => missing_translation_errors.push(e),
}

assert!(
missing_translation_errors.is_empty(),
"missing translation errors: {missing_translation_errors:?}"
);
}
}

/// Defines the error when a user-defined JSON Schema references an unrecognized schema URI.
Expand Down
209 changes: 209 additions & 0 deletions lib/dsc-lib-jsonschema/src/schema_utility_extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,105 @@ pub trait SchemaUtilityExtensions {
/// ```
fn set_id(&mut self, id_uri: &str) -> Option<String>;

//********************** $schema keyword functions ***********************//
/// Retrieves the `$schema` keyword and returns it as a string if it exists.
///
/// # Examples
///
/// ```rust
/// use schemars::json_schema;
/// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
///
/// let ref schema = json_schema!({
/// "$schema": "https://json-schema.org/draft/2020-12/schema"
/// });
///
/// assert_eq!(
/// schema.get_meta_schema(),
/// Some("https://json-schema.org/draft/2020-12/schema")
/// );
/// ```
fn get_meta_schema(&self) -> Option<&str>;
/// Retrieves the `$schema` keyword and returns it as a [`Url`] if it exists.
///
/// # Examples
///
/// ```rust
/// use schemars::json_schema;
/// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
/// use url::Url;
///
/// let ref schema = json_schema!({
/// "$schema": "https://json-schema.org/draft/2020-12/schema",
/// "type": "string"
/// });
///
/// assert_eq!(
/// schema.get_meta_schema_as_url(),
/// Some(Url::parse("https://json-schema.org/draft/2020-12/schema").unwrap())
/// );
/// ```
///
/// ```rust
/// use schemars::json_schema;
/// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
///
/// let ref schema = json_schema!({ "type": "string" });
///
/// assert_eq!(
/// schema.get_meta_schema_as_url(),
/// None)
/// );
/// ```
fn get_meta_schema_as_url(&self) -> Option<Url>;
/// Checks if the `$schema` keyword is present in the schema.
///
/// # Examples
///
/// ```rust
/// use schemars::json_schema;
/// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
///
/// let ref schema = json_schema!({
/// "$schema": "https://json-schema.org/draft/2020-12/schema",
/// "type": "string"
/// });
///
/// assert_eq!(
/// schema.has_meta_schema_keyword(),
/// true
/// );
/// ```
///
/// ```rust
/// # use schemars::json_schema;
/// # use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
///
/// let ref schema = json_schema!({"type": "string"});
///
/// assert_eq!(
/// schema.has_meta_schema_keyword(),
/// false
/// );
/// ```
fn has_meta_schema_keyword(&self) -> bool;
/// Sets the `$schema` keyword to the provided URI and returns the previous value if it existed.
///
/// # Examples
///
/// ```rust
/// use schemars::json_schema;
/// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
///
/// let mut schema = json_schema!({ "type": "string" });
/// let previous = schema.set_meta_schema("https://json-schema.org/draft/2020-12/schema");
/// assert_eq!(previous, None);
/// assert_eq!(
/// schema.get_meta_schema_as_url(),
/// Some(url::Url::parse("https://json-schema.org/draft/2020-12/schema").unwrap())
/// );
/// ```
fn set_meta_schema(&mut self, meta_schema_uri: &str) -> Option<String>;
//*********************** $defs keyword functions ************************//
/// Retrieves the `$defs` keyword and returns the object if it exists.
///
Expand Down Expand Up @@ -1785,6 +1884,88 @@ pub trait SchemaUtilityExtensions {
/// assert_eq!(actual, expected);
/// ```
fn canonicalize_refs_and_defs_for_bundled_resources(&mut self);
//************************ title keyword functions ***********************//
/// Retrieves the value of the `title` keyword from the schema, if it exists.
///
/// # Returns
///
/// An [`Option<&str>`] containing the value of the `title` keyword if it exists, or [`None`]
/// otherwise.
fn get_title(&self) -> Option<&str>;
/// Sets the value of the `title` keyword in the schema.
///
/// This function sets the value of the `title` keyword in the schema and returns the previous
/// value as a [`String`], if it was already defined.
///
/// # Arguments
///
/// - `title` - The value to set for the `title` keyword.
///
/// # Returns
///
/// An [`Option<String>`] containing the previous value of the `title` keyword if it was
/// already defined, or [`None`] otherwise.
///
/// # Example
///
/// This example shows how you can use this method to define and override the `title` keyword
/// in a schema.
///
/// ```
/// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
/// use schemars::json_schema;
///
/// let ref mut schema = json_schema!({"type": "string"});
/// assert_eq!(schema.set_title("Example Title"), None);
/// assert_eq!(schema.set_title("New example title"), Some("Example Title".to_string()));
/// ```
fn set_title(&mut self, title: &str) -> Option<String>;
//********************* description keyword functions ********************//
/// Retrieves the value of the `description` keyword from the schema, if it exists.
///
/// # Returns
///
/// An [`Option<&str>`] containing the value of the `description` keyword if it exists, or
/// [`None`] otherwise.
///
/// # Example
///
/// This example shows how you can use this method to retrieve the `description` keyword from a
/// schema.
///
/// ```
/// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
/// use schemars::json_schema;
///
/// let schema = json_schema!({"type": "string", "description": "An example description"});
/// assert_eq!(schema.get_description(), Some("An example description"));
/// ```
fn get_description(&self) -> Option<&str>;
/// Sets the value of the `description` keyword in the schema.
///
/// # Arguments
///
/// - `description` - The value to set for the `description` keyword.
///
/// # Returns
///
/// An [`Option<String>`] containing the previous value of the `description` keyword if it was
/// already defined, or [`None`] otherwise.
///
/// # Example
///
/// This example shows how you can use this method to define and override the `description`
/// keyword in a schema.
///
/// ```
/// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions;
/// use schemars::json_schema;
///
/// let ref mut schema = json_schema!({"type": "string"});
/// assert_eq!(schema.set_description("An example description"), None);
/// assert_eq!(schema.set_description("A new description"), Some("An example description".to_string()));
/// ```
fn set_description(&mut self, description: &str) -> Option<String>;
}

impl SchemaUtilityExtensions for Schema {
Expand Down Expand Up @@ -2067,6 +2248,19 @@ impl SchemaUtilityExtensions for Schema {
self.insert("$id".to_string(), Value::String(id_uri.to_string()))
.and(old_id)
}
fn get_meta_schema(&self) -> Option<&str> {
self.get_keyword_as_str("$schema")
}
fn get_meta_schema_as_url(&self) -> Option<Url> {
self.get_meta_schema().and_then(|s| Url::parse(s).ok())
}
fn has_meta_schema_keyword(&self) -> bool {
self.get("$schema").is_some()
}
fn set_meta_schema(&mut self, meta_schema_uri: &str) -> Option<String> {
self.insert("$schema".to_string(), Value::String(meta_schema_uri.to_string()))
.and_then(|v| v.as_str().map(std::string::ToString::to_string))
}
fn get_properties(&self) -> Option<&Map<String, Value>> {
self.get_keyword_as_object("properties")
}
Expand Down Expand Up @@ -2251,4 +2445,19 @@ impl SchemaUtilityExtensions for Schema {
self.rename_defs_subschema_for_reference(&reference_lookup, bundled_id);
}
}
fn get_title(&self) -> Option<&str> {
self.get_keyword_as_str("title")
}
fn set_title(&mut self, title: &str) -> Option<String> {
self
.insert("title".to_string(), Value::String(title.to_string()))
.and_then(|v| v.as_str().map(std::string::ToString::to_string))
}
fn get_description(&self) -> Option<&str> {
self.get_keyword_as_str("description")
}
fn set_description(&mut self, description: &str) -> Option<String> {
self.insert("description".to_string(), Value::String(description.to_string()))
.and_then(|v| v.as_str().map(std::string::ToString::to_string))
}
}
Loading
Loading