use thisversion::traits::{IntoSchema, TryIntoModel};
use thisversion::{VersionBoundary, VersionFamily};
#[derive(Debug, PartialEq, Eq)]
pub struct ContentError;
#[derive(VersionFamily)]
#[thisversion(fallible)]
pub enum ContentVersions {
V1(ContentV1),
V2(ContentV2),
}
pub struct ContentV1 {
pub text: String,
}
#[derive(VersionBoundary)]
#[thisversion(model = Content, error = ContentError)]
pub struct ContentV2 {
pub paragraphs: Vec<String>,
}
impl TryFrom<ContentV1> for ContentV2 {
type Error = ContentError;
fn try_from(previous: ContentV1) -> Result<Self, Self::Error> {
if previous.text.contains('\0') {
return Err(ContentError);
}
Ok(Self {
paragraphs: vec![previous.text],
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct TitleError;
#[derive(Debug, PartialEq, Eq)]
pub enum DocumentError {
Title(TitleError),
Content(ContentError),
}
impl From<TitleError> for DocumentError {
fn from(error: TitleError) -> Self {
Self::Title(error)
}
}
impl From<ContentError> for DocumentError {
fn from(error: ContentError) -> Self {
Self::Content(error)
}
}
#[derive(VersionFamily)]
#[thisversion(fallible)]
pub enum DocumentVersions {
V1(DocumentV1),
V2(DocumentV2),
}
pub struct DocumentV1 {
pub title: String,
pub content: ContentV1,
}
#[derive(VersionBoundary)]
#[thisversion(model = Document, error = DocumentError)]
pub struct DocumentV2 {
pub title: String,
#[thisversion(nested)]
pub content: ContentVersions,
pub archived: bool,
}
impl TryFrom<DocumentV1> for DocumentV2 {
type Error = TitleError;
fn try_from(previous: DocumentV1) -> Result<Self, Self::Error> {
if previous.title.trim().is_empty() {
return Err(TitleError);
}
Ok(Self {
title: previous.title,
content: previous.content.into(),
archived: false,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Document {
pub title: String,
pub content: Content,
pub archived: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub struct Content {
pub paragraphs: Vec<String>,
}
fn main() -> Result<(), DocumentError> {
let stored = DocumentVersions::V1(DocumentV1 {
title: "First document".to_owned(),
content: ContentV1 {
text: "Original text.".to_owned(),
},
});
let model = stored.try_into_model()?;
assert_eq!(model.content.paragraphs, ["Original text."]);
let stored = DocumentVersions::V2(DocumentV2 {
title: "Already current document".to_owned(),
content: ContentVersions::V1(ContentV1 {
text: "Still stored as an old content snapshot.".to_owned(),
}),
archived: true,
});
let model = stored.try_into_model()?;
assert_eq!(
model.content.paragraphs,
["Still stored as an old content snapshot."]
);
let persisted: DocumentVersions = model.into_schema();
assert!(matches!(
persisted,
DocumentVersions::V2(DocumentV2 {
content: ContentVersions::V2(_),
..
})
));
Ok(())
}