use super::*;
pub fn compare_single_type_schemas<'s, S: CustomSchema>(
comparison_settings: &SchemaComparisonSettings,
base: &'s SingleTypeSchema<S>,
compared: &'s SingleTypeSchema<S>,
) -> SchemaComparisonResult<'s, S> {
base.compare_with(compared, comparison_settings)
}
pub fn compare_type_collection_schemas<'s, S: CustomSchema>(
comparison_settings: &SchemaComparisonSettings,
base: &'s TypeCollectionSchema<S>,
compared: &'s TypeCollectionSchema<S>,
) -> SchemaComparisonResult<'s, S> {
base.compare_with(compared, comparison_settings)
}
pub struct TypeCompatibilityParameters<S: CustomSchema, C: ComparableSchema<S>> {
pub comparison_between_versions: SchemaComparisonSettings,
pub comparison_between_current_and_latest: SchemaComparisonSettings,
pub named_versions: NamedSchemaVersions<S, C>,
}
pub type SingleTypeSchemaCompatibilityParameters<S> =
TypeCompatibilityParameters<S, SingleTypeSchema<S>>;
pub type TypeCollectionSchemaCompatibilityParameters<S> =
TypeCompatibilityParameters<S, TypeCollectionSchema<S>>;
impl<S: CustomSchema, C: ComparableSchema<S>> Default for TypeCompatibilityParameters<S, C> {
fn default() -> Self {
Self::new()
}
}
impl<S: CustomSchema, C: ComparableSchema<S>> TypeCompatibilityParameters<S, C> {
pub fn new() -> Self {
Self {
comparison_between_versions: SchemaComparisonSettings::allow_extension(),
comparison_between_current_and_latest: SchemaComparisonSettings::require_equality(),
named_versions: NamedSchemaVersions::new(),
}
}
pub fn with_comparison_between_versions(
mut self,
builder: impl FnOnce(SchemaComparisonSettings) -> SchemaComparisonSettings,
) -> Self {
self.comparison_between_versions = builder(self.comparison_between_versions);
self
}
pub fn with_comparison_between_current_and_latest(
mut self,
builder: impl FnOnce(SchemaComparisonSettings) -> SchemaComparisonSettings,
) -> Self {
self.comparison_between_current_and_latest =
builder(self.comparison_between_current_and_latest);
self
}
pub fn replace_versions_with(
mut self,
named_schema_versions: NamedSchemaVersions<S, C>,
) -> Self {
self.named_versions = named_schema_versions;
self
}
pub fn register_version(
mut self,
name: impl AsRef<str>,
version: impl IntoComparableSchema<C, S>,
) -> Self {
self.named_versions = self.named_versions.register_version(name, version);
self
}
}
pub fn assert_type_backwards_compatibility<
S: CustomSchema,
T: Describe<S::CustomAggregatorTypeKind>,
>(
parameters_builder: impl FnOnce(
TypeCompatibilityParameters<S, SingleTypeSchema<S>>,
) -> TypeCompatibilityParameters<S, SingleTypeSchema<S>>,
) {
let current = generate_single_type_schema::<T, S>();
assert_schema_compatibility(
¤t,
¶meters_builder(TypeCompatibilityParameters::new()),
)
}
pub fn assert_type_collection_backwards_compatibility<S: CustomSchema>(
current: &TypeCollectionSchema<S>,
parameters_builder: impl FnOnce(
TypeCompatibilityParameters<S, TypeCollectionSchema<S>>,
) -> TypeCompatibilityParameters<S, TypeCollectionSchema<S>>,
) {
assert_schema_compatibility(
current,
¶meters_builder(TypeCompatibilityParameters::new()),
)
}
fn assert_schema_compatibility<S: CustomSchema, C: ComparableSchema<S>>(
current: &C,
parameters: &TypeCompatibilityParameters<S, C>,
) {
let named_versions = parameters.named_versions.get_versions();
let Some((latest_version_name, latest_schema_version)) = named_versions.last() else {
let mut error = String::new();
writeln!(
&mut error,
"You must provide at least one named schema version."
)
.unwrap();
writeln!(&mut error, "Use a relevant name (for example, the current software version name), and save the current schema as follows:").unwrap();
writeln!(&mut error, "{}", current.encode_to_hex()).unwrap();
panic!("{error}");
};
let result = latest_schema_version
.compare_with(current, ¶meters.comparison_between_current_and_latest);
if let Some(error_message) = result.error_message(latest_version_name, "current") {
let mut error = String::new();
writeln!(&mut error, "The most recent named version ({latest_version_name}) DOES NOT PASS CHECKS, likely because it is not equal to the current version.").unwrap();
writeln!(&mut error).unwrap();
write!(&mut error, "{error_message}").unwrap();
writeln!(&mut error).unwrap();
writeln!(
&mut error,
"You will likely want to do one of the following:"
)
.unwrap();
writeln!(&mut error, "(A) Revert an unintended change to some model.").unwrap();
writeln!(
&mut error,
"(B) Add a new named version to the list, to be supported going forward. You must then generate its schema with `#[sbor_assert(backwards_compatible(..), generate)]`, running the test, and removing `generate`."
)
.unwrap();
writeln!(
&mut error,
"(C) If the latest version is under development, and has not been used / release, you can regenerate it with `#[sbor_assert(backwards_compatible(..), regenerate)]`, running the test, and removing `regenerate`."
)
.unwrap();
panic!("{error}");
}
for i in 0..named_versions.len() - 1 {
let (previous_version_name, previous_schema) = named_versions.get_index(i).unwrap();
let (next_version_name, next_schema) = named_versions.get_index(i + 1).unwrap();
previous_schema
.compare_with(next_schema, ¶meters.comparison_between_versions)
.assert_valid(previous_version_name, next_version_name);
}
}