use serde::{Deserialize, Serialize};
use thisversion::traits::IntoSchema;
use thisversion::{VersionBoundary, VersionFamily};
const V1_JSON: &str = r#"{
"version": "V1",
"data": {
"title": "First document",
"body": "Written before archiving existed."
}
}"#;
const V2_JSON: &str = r#"{
"version": "V2",
"data": {
"title": "Second document",
"body": "This one has an archived field.",
"archived": true
}
}"#;
const V3_JSON: &str = r#"{
"version": "V3",
"data": {
"title": "Second document",
"content": "This one has an archived field.",
"archived": true
}
}"#;
#[derive(Deserialize, Serialize, VersionFamily)]
#[serde(tag = "version", content = "data")]
pub enum DocumentVersions {
V1(DocumentV1),
V2(DocumentV2),
V3(DocumentV3),
}
#[derive(Deserialize, Serialize)]
pub struct DocumentV1 {
pub title: String,
pub body: String,
}
#[derive(Deserialize, Serialize)]
pub struct DocumentV2 {
pub title: String,
pub body: String,
pub archived: bool,
}
impl From<DocumentV1> for DocumentV2 {
fn from(prev: DocumentV1) -> Self {
Self {
title: prev.title,
body: prev.body,
archived: false,
}
}
}
#[derive(Deserialize, Serialize, VersionBoundary)]
#[thisversion(model = Document)]
pub struct DocumentV3 {
pub title: String,
pub content: String,
pub archived: bool,
}
impl From<DocumentV2> for DocumentV3 {
fn from(prev: DocumentV2) -> Self {
Self {
title: prev.title,
content: prev.body,
archived: prev.archived,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Document {
pub title: String,
pub content: String,
pub archived: bool,
}
fn deserialize_document(json: &str) -> Document {
use thisversion::traits::IntoModel;
serde_json::from_str::<DocumentVersions>(json)
.unwrap()
.into_model()
}
fn serialize_document(document: &Document) -> String {
use thisversion::serde::serialize_into_schema;
#[derive(Serialize)]
struct Record<'a> {
#[serde(serialize_with = "serialize_into_schema::<DocumentVersions, _>")]
document: &'a Document,
}
serde_json::to_string(&Record { document }).unwrap()
}
fn main() {
let document = deserialize_document(V1_JSON);
assert_eq!(
serde_json::from_str::<serde_json::Value>(&serialize_document(&document)).unwrap(),
serde_json::json!({
"document": {
"version": "V3",
"data": {
"title": "First document",
"content": "Written before archiving existed.",
"archived": false,
},
},
})
);
let versions: DocumentVersions = document.into_schema();
assert!(matches!(versions, DocumentVersions::V3(_)));
assert_eq!(
serde_json::to_value(&versions).unwrap(),
serde_json::json!({
"version": "V3",
"data": {
"title": "First document",
"content": "Written before archiving existed.",
"archived": false,
},
})
);
assert_eq!(
deserialize_document(V2_JSON),
Document {
title: "Second document".to_owned(),
content: "This one has an archived field.".to_owned(),
archived: true,
}
);
assert_eq!(
deserialize_document(V3_JSON),
Document {
title: "Second document".to_owned(),
content: "This one has an archived field.".to_owned(),
archived: true,
}
);
}