1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use serde_wasm_bindgen::{Serializer, from_value};
4use wasm_bindgen::prelude::*;
5
6mod v0;
7pub mod v1;
8
9#[cfg(test)]
10mod test_utils;
11
12pub mod current {
13 pub use crate::v1::*;
16}
17
18#[wasm_bindgen(typescript_custom_section)]
27const TS_APPEND_CONTENT: &'static str = r#"
28type NonEmpty<T> = Array<T>;
29export type Uuid = string;
30type Ustr = string;
31type Value = unknown;
32"#;
33
34pub static CURRENT_VERSION: &str = "1";
35
36#[wasm_bindgen(js_name = "currentVersion")]
37pub fn current_version() -> String {
38 CURRENT_VERSION.to_string()
39}
40
41#[derive(Serialize, Debug)]
42pub enum VersionedDocument {
43 V0(v0::Document),
44 V1(v1::Document),
45}
46
47impl<'de> Deserialize<'de> for VersionedDocument {
48 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
49 where
50 D: serde::Deserializer<'de>,
51 {
52 let value = Value::deserialize(deserializer)?;
53
54 let version = value.get("version").and_then(Value::as_str).unwrap_or("0");
55
56 match version {
57 "0" => {
58 let doc: v0::Document =
59 serde_json::from_value(value).map_err(serde::de::Error::custom)?;
60 Ok(VersionedDocument::V0(doc))
61 }
62 "1" => {
63 let doc: v1::Document =
64 serde_json::from_value(value).map_err(serde::de::Error::custom)?;
65 Ok(VersionedDocument::V1(doc))
66 }
67 other => Err(serde::de::Error::custom(format!("unsupported version {other}"))),
68 }
69 }
70}
71
72impl VersionedDocument {
73 pub fn to_current(self) -> current::Document {
74 match self {
75 VersionedDocument::V0(v0) => {
76 VersionedDocument::V1(v1::Document::migrate_from_v0(v0)).to_current()
78 }
79
80 VersionedDocument::V1(old1) => old1,
81 }
82 }
83}
84
85#[wasm_bindgen(js_name = "migrateDocument")]
86pub fn migrate_document(input: JsValue) -> Result<JsValue, JsValue> {
87 let doc: VersionedDocument =
88 from_value(input).map_err(|e| JsValue::from_str(&format!("deserialize error: {e}")))?;
89
90 let current_doc = doc.to_current();
91
92 let serializer = Serializer::json_compatible();
95
96 let output = current_doc
97 .serialize(&serializer)
98 .map_err(|e| JsValue::from_str(&format!("serialize error: {e}")))?;
99
100 Ok(output)
101}
102
103#[cfg(test)]
104mod migration_tests {
105 use super::VersionedDocument;
106 use crate::test_utils::test_example_documents;
107
108 #[test]
109 fn test_v0_examples_migrate_to_current() {
110 test_example_documents::<VersionedDocument, _>("examples/v0", |doc, _| {
111 let _ = doc.to_current();
113 });
114 }
115}