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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions src/change.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright 2025 Oxide Computer Company
// Copyright 2026 Oxide Computer Company

use std::fmt;

use crate::JsonPathStack;

// Describes any change detected between two OpenAPI documents.
#[derive(Debug)]
pub struct Change {
/// Human-readable message describing the nature of the change.
pub message: String,
Expand All @@ -18,10 +19,71 @@ pub struct Change {
/// Classification of the change compatibility.
pub class: ChangeClass,

/// Details on the kind of change
/// Details on the kind of change.
pub details: ChangeDetails,
}

// Format `Change` in the nested `paths`/`changes` layout that will be
// introduced when the type is restructured into `Change`, `ChangePath`, and
// `ChangeInfo`. Doing this ahead of time keeps the restructuring commit free
// of test-output noise.
impl fmt::Debug for Change {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// Wrapper that formats as `ChangePath { old, new, comparison }`.
struct PathFmt<'a> {
old: &'a JsonPathStack,
new: &'a JsonPathStack,
comparison: &'a ChangeComparison,
}

impl fmt::Debug for PathFmt<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChangePath")
.field("old", self.old)
.field("new", self.new)
.field("comparison", self.comparison)
.finish()
}
}

/// Wrapper that formats as `ChangeInfo { old_subpath, new_subpath,
/// message, class, details }`.
struct InfoFmt<'a> {
message: &'a str,
class: &'a ChangeClass,
details: &'a ChangeDetails,
}

impl fmt::Debug for InfoFmt<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChangeInfo")
.field("old_subpath", &"")
.field("new_subpath", &"")
Comment thread
sunshowers marked this conversation as resolved.
.field("message", &self.message)
.field("class", self.class)
.field("details", self.details)
.finish()
}
}

let path = PathFmt {
old: &self.old_path,
new: &self.new_path,
comparison: &self.comparison,
};
let info = InfoFmt {
message: &self.message,
class: &self.class,
details: &self.details,
};

f.debug_struct("Change")
.field("paths", &[path])
.field("changes", &[info])
.finish()
}
}

impl Change {
pub fn new(
message: impl ToString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,29 @@
Result for patch:
[
Change {
message: "object properties changed",
old_path: [
"#/components/schemas/Person",
"#/components/schemas/Company/properties/ceo/$ref",
"#/paths/~1companies/get/responses/200/content/application~1json/schema/$ref",
paths: [
ChangePath {
old: [
"#/components/schemas/Person",
"#/components/schemas/Company/properties/ceo/$ref",
"#/paths/~1companies/get/responses/200/content/application~1json/schema/$ref",
],
new: [
"#/components/schemas/Person",
"#/components/schemas/Company/properties/ceo/$ref",
"#/paths/~1companies/get/responses/200/content/application~1json/schema/$ref",
],
comparison: Output,
},
],
new_path: [
"#/components/schemas/Person",
"#/components/schemas/Company/properties/ceo/$ref",
"#/paths/~1companies/get/responses/200/content/application~1json/schema/$ref",
changes: [
ChangeInfo {
old_subpath: "",
new_subpath: "",
message: "object properties changed",
class: Unhandled,
details: UnknownDifference,
},
],
comparison: Output,
class: Unhandled,
details: UnknownDifference,
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -34,49 +34,79 @@
Result for patch:
[
Change {
message: "schema metadata changed",
old_path: [
"#/components/schemas/DirectLoop",
"#/components/schemas/DirectLoop/properties/next/$ref",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
paths: [
ChangePath {
old: [
"#/components/schemas/DirectLoop",
"#/components/schemas/DirectLoop/properties/next/$ref",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
],
new: [
"#/components/schemas/LoopIndirect",
"#/components/schemas/DirectLoop/properties/next/$ref",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
],
comparison: Output,
},
],
new_path: [
"#/components/schemas/LoopIndirect",
"#/components/schemas/DirectLoop/properties/next/$ref",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
changes: [
ChangeInfo {
old_subpath: "",
new_subpath: "",
message: "schema metadata changed",
class: Trivial,
details: Metadata,
},
],
comparison: Output,
class: Trivial,
details: Metadata,
},
Change {
message: "object properties changed",
old_path: [
"#/components/schemas/DirectLoop",
"#/components/schemas/DirectLoop/properties/next/$ref",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
paths: [
ChangePath {
old: [
"#/components/schemas/DirectLoop",
"#/components/schemas/DirectLoop/properties/next/$ref",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
],
new: [
"#/components/schemas/LoopIndirect",
"#/components/schemas/DirectLoop/properties/next/$ref",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
],
comparison: Output,
},
],
new_path: [
"#/components/schemas/LoopIndirect",
"#/components/schemas/DirectLoop/properties/next/$ref",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
changes: [
ChangeInfo {
old_subpath: "",
new_subpath: "",
message: "object properties changed",
class: Unhandled,
details: UnknownDifference,
},
],
comparison: Output,
class: Unhandled,
details: UnknownDifference,
},
Change {
message: "schema types changed",
old_path: [
"#/components/schemas/DirectLoop/properties/value",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
paths: [
ChangePath {
old: [
"#/components/schemas/DirectLoop/properties/value",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
],
new: [
"#/components/schemas/DirectLoop/properties/value",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
],
comparison: Output,
},
],
new_path: [
"#/components/schemas/DirectLoop/properties/value",
"#/paths/~1direct-loop/get/responses/200/content/application~1json/schema/$ref",
changes: [
ChangeInfo {
old_subpath: "",
new_subpath: "",
message: "schema types changed",
class: Incompatible,
details: UnknownDifference,
},
],
comparison: Output,
class: Incompatible,
details: UnknownDifference,
},
]
34 changes: 22 additions & 12 deletions tests/cases/cycle-detection/output/change-item-value-type.out
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,29 @@
Result for patch:
[
Change {
message: "schema types changed",
old_path: [
"#/components/schemas/Item/properties/value",
"#/components/schemas/ItemPage/properties/items/items/$ref",
"#/paths/~1items/get/responses/200/content/application~1json/schema/$ref",
paths: [
ChangePath {
old: [
"#/components/schemas/Item/properties/value",
"#/components/schemas/ItemPage/properties/items/items/$ref",
"#/paths/~1items/get/responses/200/content/application~1json/schema/$ref",
],
new: [
"#/components/schemas/Item/properties/value",
"#/components/schemas/ItemPage/properties/items/items/$ref",
"#/paths/~1items/get/responses/200/content/application~1json/schema/$ref",
],
comparison: Output,
},
],
new_path: [
"#/components/schemas/Item/properties/value",
"#/components/schemas/ItemPage/properties/items/items/$ref",
"#/paths/~1items/get/responses/200/content/application~1json/schema/$ref",
changes: [
ChangeInfo {
old_subpath: "",
new_subpath: "",
message: "schema types changed",
class: Incompatible,
details: UnknownDifference,
},
],
comparison: Output,
class: Incompatible,
details: UnknownDifference,
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,29 @@
Result for patch:
[
Change {
message: "schema types changed",
old_path: [
"#/components/schemas/Person/properties/name",
"#/components/schemas/Company/properties/ceo/$ref",
"#/paths/~1companies/get/responses/200/content/application~1json/schema/$ref",
paths: [
ChangePath {
old: [
"#/components/schemas/Person/properties/name",
"#/components/schemas/Company/properties/ceo/$ref",
"#/paths/~1companies/get/responses/200/content/application~1json/schema/$ref",
],
new: [
"#/components/schemas/Person/properties/name",
"#/components/schemas/Company/properties/ceo/$ref",
"#/paths/~1companies/get/responses/200/content/application~1json/schema/$ref",
],
comparison: Output,
},
],
new_path: [
"#/components/schemas/Person/properties/name",
"#/components/schemas/Company/properties/ceo/$ref",
"#/paths/~1companies/get/responses/200/content/application~1json/schema/$ref",
changes: [
ChangeInfo {
old_subpath: "",
new_subpath: "",
message: "schema types changed",
class: Incompatible,
details: UnknownDifference,
},
],
comparison: Output,
class: Incompatible,
details: UnknownDifference,
},
]
34 changes: 22 additions & 12 deletions tests/cases/cycle-detection/output/change-three-cycle-property.out
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,29 @@
Result for patch:
[
Change {
message: "schema types changed",
old_path: [
"#/components/schemas/NodeB/properties/label",
"#/components/schemas/NodeA/properties/next/$ref",
"#/paths/~1three-cycle/get/responses/200/content/application~1json/schema/$ref",
paths: [
ChangePath {
old: [
"#/components/schemas/NodeB/properties/label",
"#/components/schemas/NodeA/properties/next/$ref",
"#/paths/~1three-cycle/get/responses/200/content/application~1json/schema/$ref",
],
new: [
"#/components/schemas/NodeB/properties/label",
"#/components/schemas/NodeA/properties/next/$ref",
"#/paths/~1three-cycle/get/responses/200/content/application~1json/schema/$ref",
],
comparison: Output,
},
],
new_path: [
"#/components/schemas/NodeB/properties/label",
"#/components/schemas/NodeA/properties/next/$ref",
"#/paths/~1three-cycle/get/responses/200/content/application~1json/schema/$ref",
changes: [
ChangeInfo {
old_subpath: "",
new_subpath: "",
message: "schema types changed",
class: Incompatible,
details: UnknownDifference,
},
],
comparison: Output,
class: Incompatible,
details: UnknownDifference,
},
]
Loading
Loading