1use indexmap::IndexMap;
30use serde::{Deserialize, Serialize};
31
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
34pub struct LinkML {
35 pub id: String,
37 pub name: String,
39 pub title: String,
41 #[serde(
43 default,
44 skip_serializing_if = "Option::is_none",
45 deserialize_with = "remove_newlines"
46 )]
47 pub description: Option<String>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub license: Option<String>,
51 #[serde(default, skip_serializing_if = "Vec::is_empty")]
53 pub see_also: Vec<String>,
54 #[serde(skip_serializing_if = "IndexMap::is_empty")]
56 pub prefixes: IndexMap<String, String>,
57 pub default_prefix: String,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub default_range: Option<String>,
62 #[serde(default, skip_serializing_if = "Vec::is_empty")]
64 pub imports: Vec<String>,
65 #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
67 pub classes: IndexMap<String, ClassDefinition>,
68 #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
70 pub slots: IndexMap<String, AttributeDefinition>,
71 #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
73 pub enums: IndexMap<String, EnumDefinition>,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
78pub struct Contributor {
79 pub name: String,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
85pub struct Subset {
86 #[serde(
88 default,
89 skip_serializing_if = "Option::is_none",
90 deserialize_with = "remove_newlines"
91 )]
92 pub description: Option<String>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
97pub struct TypeDefinition {
98 pub base: String,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub pattern: Option<String>,
103 #[serde(
105 default,
106 skip_serializing_if = "Option::is_none",
107 deserialize_with = "remove_newlines"
108 )]
109 pub description: Option<String>,
110 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub minimum_value: Option<i64>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
117pub struct EnumDefinition {
118 #[serde(
120 default,
121 skip_serializing_if = "is_empty_string_option",
122 deserialize_with = "remove_newlines"
123 )]
124 pub description: Option<String>,
125 #[serde(skip_serializing_if = "IndexMap::is_empty")]
127 pub permissible_values: IndexMap<String, PermissibleValue>,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
132pub struct PermissibleValue {
133 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub text: Option<String>,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub meaning: Option<String>,
139 #[serde(
141 default,
142 skip_serializing_if = "Option::is_none",
143 deserialize_with = "remove_newlines"
144 )]
145 pub description: Option<String>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
150pub struct ClassDefinition {
151 #[serde(
153 default,
154 skip_serializing_if = "is_empty_string_option",
155 deserialize_with = "remove_newlines"
156 )]
157 pub description: Option<String>,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub class_uri: Option<String>,
161 #[serde(default, skip_serializing_if = "Vec::is_empty")]
163 pub slots: Vec<String>,
164 #[serde(default, skip_serializing_if = "Option::is_none")]
166 pub is_a: Option<String>,
167 #[serde(default, skip_serializing_if = "is_false_option")]
169 pub tree_root: Option<bool>,
170 #[serde(default, skip_serializing_if = "Option::is_none")]
172 pub slot_usage: Option<IndexMap<String, SlotUsage>>,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub attributes: Option<IndexMap<String, AttributeDefinition>>,
176 #[serde(default, skip_serializing_if = "Vec::is_empty")]
178 pub mixins: Vec<String>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
183pub struct Annotation {
184 #[serde(default, skip_serializing_if = "Option::is_none")]
186 pub value: Option<String>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
191pub struct SlotUsage {
192 #[serde(default, skip_serializing_if = "Option::is_none")]
194 pub pattern: Option<String>,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
199pub struct AttributeDefinition {
200 #[serde(
202 default,
203 skip_serializing_if = "is_empty_string_option",
204 deserialize_with = "remove_newlines"
205 )]
206 pub description: Option<String>,
207 #[serde(default, skip_serializing_if = "is_empty_string_option")]
209 pub slot_uri: Option<String>,
210 #[serde(default, skip_serializing_if = "is_false_option")]
212 pub identifier: Option<bool>,
213 #[serde(default, skip_serializing_if = "is_false_option")]
215 pub required: Option<bool>,
216 #[serde(default, skip_serializing_if = "Option::is_none")]
218 pub range: Option<String>,
219 #[serde(default, skip_serializing_if = "is_false_option")]
221 pub readonly: Option<bool>,
222 #[serde(default, skip_serializing_if = "is_false_option")]
224 pub multivalued: Option<bool>,
225 #[serde(default, skip_serializing_if = "Option::is_none")]
227 pub minimum_value: Option<i64>,
228 #[serde(default, skip_serializing_if = "Option::is_none")]
230 pub maximum_value: Option<i64>,
231 #[serde(default, skip_serializing_if = "is_false_option")]
233 pub recommended: Option<bool>,
234 #[serde(default, skip_serializing_if = "Vec::is_empty")]
236 pub examples: Vec<Example>,
237 #[serde(default, skip_serializing_if = "Option::is_none")]
239 pub annotations: Option<IndexMap<String, Annotation>>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
244pub struct Example {
245 #[serde(default, skip_serializing_if = "Option::is_none")]
247 pub value: Option<String>,
248 #[serde(
250 default,
251 skip_serializing_if = "is_empty_string_option",
252 deserialize_with = "remove_newlines"
253 )]
254 pub description: Option<String>,
255}
256
257fn remove_newlines<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
259where
260 D: serde::Deserializer<'de>,
261{
262 let opt = Option::<String>::deserialize(deserializer)?;
263 Ok(opt.map(|s| {
264 s.lines()
265 .map(|line| line.trim())
266 .collect::<Vec<&str>>()
267 .join(" ")
268 }))
269}
270
271fn is_empty_string_option(s: &Option<String>) -> bool {
272 s.is_none() || s.as_ref().unwrap().is_empty()
273}
274
275fn is_false_option(s: &Option<bool>) -> bool {
276 s.is_none() || !s.as_ref().unwrap()
277}