co_primitives/types/metadata.rs
1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 1io BRANDGUARDIAN GmbH
3
4use serde::{Deserialize, Serialize};
5
6/// Special CO metadata.
7pub trait CoMetadata: Serialize {
8 fn metadata() -> Vec<Metadata>;
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub enum Metadata {
13 /// External field namess.
14 #[serde(rename = "ext")]
15 External(Vec<String>),
16}
17
18#[derive(Clone, Serialize)]
19pub struct WithCoMetadata<T: CoMetadata + Serialize> {
20 #[serde(rename = "$co", skip_serializing_if = "Vec::is_empty")]
21 co: Vec<Metadata>,
22 #[serde(flatten)]
23 value: T,
24}
25impl<T> WithCoMetadata<T>
26where
27 T: CoMetadata + Serialize,
28{
29 pub fn new(value: T) -> Self {
30 Self { co: T::metadata(), value }
31 }
32}
33impl<T: CoMetadata + Serialize> From<T> for WithCoMetadata<T> {
34 fn from(value: T) -> Self {
35 WithCoMetadata::new(value)
36 }
37}
38
39// pub fn serialize_with_metadata<T: CoMetadata + Serialize, S: serde::Serializer>(
40// serializer: S,
41// value: T,
42// ) -> Result<S::Ok, S::Error> {
43// WithCoMetadata::new(value).serialize(serializer)
44// }
45
46// Workaround for https://github.com/rust-lang/rust/issues/50133
47// pub struct WithCoMetadataWrapper<T>(T);
48// impl<T: CoMetadata> Into<WithCoMetadataWrapper<T>> for WithCoMetadata<T> {
49// fn into(self) -> WithCoMetadataWrapper<T> {
50// WithCoMetadataWrapper(self.value)
51// }
52// }
53// impl<T: CoMetadata> Into<WithCoMetadata<T>> for T {
54// fn into(self) -> WithCoMetadata<T> {
55// WithCoMetadata::new(self)
56// }
57// }
58
59#[cfg(test)]
60mod tests {
61 use super::WithCoMetadata;
62 use crate::{CoMetadata, Metadata};
63 use cid::Cid;
64 use co_macros::TaggedFields;
65 use serde::{Deserialize, Serialize};
66
67 #[test]
68 fn metadata() {
69 #[derive(Debug, Clone, Serialize, Deserialize)]
70 // error[E0275]: overflow evaluating the requirement `&mut Vec<u8>: Sized`
71 // #[serde(into = "WithCoMetadata<Test>")]
72 struct Test {
73 hello: i32,
74 world: Cid,
75 }
76 impl CoMetadata for Test {
77 fn metadata() -> Vec<crate::Metadata> {
78 vec![Metadata::External(vec!["world".to_owned()])]
79 }
80 }
81 // impl Serialize for Test {
82 // fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83 // where
84 // S: serde::Serializer,
85 // {
86 // self.serialize_with_metadata(serializer)
87 // }
88 // }
89
90 let json = serde_json::to_string_pretty(&WithCoMetadata::new(Test {
91 hello: 1,
92 world: Cid::try_from("bafyr4igf663hpuvdpvque42uxmkbacg5ubd4cgageulmwmqo33g2tpod7e").unwrap(),
93 }))
94 .unwrap();
95 println!("{json}");
96 }
97
98 #[test]
99 fn metadata_derive() {
100 #[derive(Debug, Clone, Serialize, Deserialize, TaggedFields)]
101 struct Test {
102 hello: i32,
103 #[tagged(external)]
104 world: Cid,
105 }
106 let json = serde_json::to_string_pretty(&WithCoMetadata::new(Test {
107 hello: 1,
108 world: Cid::try_from("bafyr4igf663hpuvdpvque42uxmkbacg5ubd4cgageulmwmqo33g2tpod7e").unwrap(),
109 }))
110 .unwrap();
111 println!("{json}");
112 }
113}
114
115// impl<T> From<WithCoMetadata<T>> for T
116// where
117// T: CoMetadata,
118// {
119// fn from(value: WithCoMetadata<T>) -> Self {
120// value.value
121// }
122// }
123
124// impl<T: CoMetadata> Into<T> for WithCoMetadata<T> {
125// fn into(self) -> T {
126// self.value
127// }
128// }
129
130// #[proc_macro_attribute]
131// pub fn co_metadata(attr: TokenStream, input: TokenStream) -> TokenStream {
132// }
133
134// macro_rules! metadata_serialize {
135// ($t:ident) => {
136// impl Serialize for X {
137// fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
138// #[derive(Serialize)]
139// struct XSerialize {
140// a: u32,
141// b: u32,
142// c: u32,
143// $co: &'static str,
144// }
145
146// XSerialize { a: self.a, b: self.b, c: self.c, d: "only at serialization" }.serialize(serializer)
147// }
148// }
149// };
150// }
151
152// impl Serialize for X {
153// fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
154// #[derive(Serialize)]
155// struct XSerialize {
156// a: u32,
157// b: u32,
158// c: u32,
159// d: &'static str,
160// }
161
162// XSerialize { a: self.a, b: self.b, c: self.c, d: "only at serialization" }.serialize(serializer)
163// }
164// }