1#![allow(rustdoc::private_intra_doc_links)]
2#![deny(
3 deprecated_in_future,
12 exported_private_dependencies,
13 future_incompatible,
14 missing_copy_implementations,
15 missing_debug_implementations,
16 private_in_public,
17 rust_2018_compatibility,
18 rust_2018_idioms,
19 trivial_casts,
20 trivial_numeric_casts,
21 unsafe_code,
22 unstable_features,
23 unused_import_braces,
24 unused_qualifications,
25
26 clippy::missing_const_for_fn,
28 clippy::redundant_pub_crate,
29 clippy::use_self
30)]
31#![cfg_attr(docsrs, feature(doc_cfg), feature(doc_alias))]
32
33use serde::{Deserialize, Serialize};
34#[cfg(feature = "validate")]
35use serde_valid::Validate;
36use std::fmt;
37
38#[derive(Default, Debug, Clone, Serialize, Deserialize)]
40#[cfg_attr(feature = "validate", derive(Validate))]
41#[serde(default)]
42pub struct Resume {
43 pub basics: Option<Basics>,
44 pub work: Vec<Work>,
45 pub volunteer: Vec<Volunteer>,
46 pub education: Vec<Education>,
47 pub awards: Vec<Award>,
49 pub certificates: Vec<Certificate>,
51 pub publications: Vec<Publication>,
53 pub skills: Vec<Skill>,
55 pub languages: Vec<Language>,
57 pub interests: Vec<Interest>,
58 pub references: Vec<Reference>,
60 pub projects: Vec<Project>,
62 #[cfg(feature = "side-projects")]
64 #[serde(rename = "sideProjects")]
65 pub side_projects: Vec<Project>,
66 pub meta: Option<Meta>,
68}
69
70#[derive(Default, Debug, Clone, Serialize, Deserialize)]
71#[cfg_attr(feature = "validate", derive(Validate))]
72#[serde(default)]
73pub struct Basics {
74 pub name: Option<String>,
75 pub label: Option<String>,
77 pub image: Option<String>,
79 pub email: Option<String>,
81 pub phone: Option<String>,
83 pub url: Option<String>,
85 pub summary: Option<String>,
87 pub location: Location,
88 pub profiles: Vec<Profile>,
90}
91
92#[derive(Default, Debug, Clone, Serialize, Deserialize)]
93#[cfg_attr(feature = "validate", derive(Validate))]
94#[serde(default)]
95pub struct Location {
96 pub address: Option<String>,
98 #[serde(rename = "postalCode")]
99 pub postal_code: Option<String>,
100 pub city: Option<String>,
101 #[serde(rename = "countryCode")]
103 pub country_code: Option<String>,
104 pub region: Option<String>,
106}
107
108#[derive(Default, Debug, Clone, Serialize, Deserialize)]
110#[cfg_attr(feature = "validate", derive(Validate))]
111#[serde(default)]
112pub struct Profile {
113 pub network: Option<String>,
115 pub username: Option<String>,
117 pub url: Option<String>,
119}
120
121#[derive(Default, Debug, Clone, Serialize, Deserialize)]
122#[cfg_attr(feature = "validate", derive(Validate))]
123#[serde(default)]
124pub struct Work {
125 pub name: Option<String>,
127 pub location: Option<String>,
129 pub description: Option<String>,
131 pub position: Option<String>,
133 pub url: Option<String>,
135 #[serde(rename = "startDate")]
136 #[cfg_attr(
137 feature = "validate",
138 validate(
139 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
140 )
141 )]
142 pub start_date: Option<String>,
143 #[serde(rename = "endDate")]
144 #[cfg_attr(
145 feature = "validate",
146 validate(
147 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
148 )
149 )]
150 pub end_date: Option<String>,
151 pub highlights: Vec<Highlight>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157#[cfg_attr(feature = "validate", derive(Validate))]
158pub struct Highlight(pub String);
159
160impl fmt::Display for Highlight {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 write!(f, "{}", self.0)
163 }
164}
165
166#[derive(Default, Debug, Clone, Serialize, Deserialize)]
167#[cfg_attr(feature = "validate", derive(Validate))]
168#[serde(default)]
169pub struct Volunteer {
170 pub organization: Option<String>,
172 pub position: Option<String>,
174 pub url: Option<String>,
176 #[serde(rename = "startDate")]
177 #[cfg_attr(
178 feature = "validate",
179 validate(
180 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
181 )
182 )]
183 pub start_date: Option<String>,
184 #[serde(rename = "endDate")]
185 #[cfg_attr(
186 feature = "validate",
187 validate(
188 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
189 )
190 )]
191 pub end_date: Option<String>,
192 pub summary: Option<String>,
194 pub highlights: Vec<Highlight>,
196}
197
198#[derive(Default, Debug, Clone, Serialize, Deserialize)]
199#[cfg_attr(feature = "validate", derive(Validate))]
200#[serde(default)]
201pub struct Education {
202 pub institution: Option<String>,
204 pub url: Option<String>,
206 pub area: Option<String>,
208 #[serde(rename = "studyType")]
210 pub study_type: Option<String>,
211 #[serde(rename = "startDate")]
212 #[cfg_attr(
213 feature = "validate",
214 validate(
215 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
216 )
217 )]
218 pub start_date: Option<String>,
219 #[serde(rename = "endDate")]
220 #[cfg_attr(
221 feature = "validate",
222 validate(
223 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
224 )
225 )]
226 pub end_date: Option<String>,
227 pub score: Option<String>,
229 #[serde(default)]
231 pub courses: Vec<Course>,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236#[cfg_attr(feature = "validate", derive(Validate))]
237pub struct Course(pub String);
238
239impl fmt::Display for Course {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "{}", self.0)
242 }
243}
244
245#[derive(Default, Debug, Clone, Serialize, Deserialize)]
247#[cfg_attr(feature = "validate", derive(Validate))]
248#[serde(default)]
249pub struct Award {
250 pub title: Option<String>,
252 #[cfg_attr(
253 feature = "validate",
254 validate(
255 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
256 )
257 )]
258 pub date: Option<String>,
259 pub awarder: Option<String>,
261 pub summary: Option<String>,
263}
264
265#[derive(Default, Debug, Clone, Serialize, Deserialize)]
267#[cfg_attr(feature = "validate", derive(Validate))]
268#[serde(default)]
269pub struct Certificate {
270 pub name: Option<String>,
272 pub date: Option<String>,
274 pub url: Option<String>,
276 pub issuer: Option<String>,
278}
279
280#[derive(Default, Debug, Clone, Serialize, Deserialize)]
282#[cfg_attr(feature = "validate", derive(Validate))]
283#[serde(default)]
284pub struct Publication {
285 pub name: Option<String>,
287 pub publisher: Option<String>,
289 #[serde(rename = "releaseDate")]
290 #[cfg_attr(
291 feature = "validate",
292 validate(
293 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
294 )
295 )]
296 pub release_date: Option<String>,
297 pub url: Option<String>,
299 pub summary: Option<String>,
301}
302
303#[derive(Default, Debug, Clone, Serialize, Deserialize)]
305#[cfg_attr(feature = "validate", derive(Validate))]
306#[serde(default)]
307pub struct Skill {
308 pub name: Option<String>,
310 pub level: Option<String>,
312 pub keywords: Vec<Keyword>,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
318#[cfg_attr(feature = "validate", derive(Validate))]
319pub struct Keyword(pub String);
320
321impl fmt::Display for Keyword {
322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323 write!(f, "{}", self.0)
324 }
325}
326
327#[derive(Default, Debug, Clone, Serialize, Deserialize)]
329#[cfg_attr(feature = "validate", derive(Validate))]
330#[serde(default)]
331pub struct Language {
332 pub language: Option<String>,
334 pub fluency: Option<String>,
336}
337
338#[derive(Default, Debug, Clone, Serialize, Deserialize)]
339#[cfg_attr(feature = "validate", derive(Validate))]
340#[serde(default)]
341pub struct Interest {
342 pub name: Option<String>,
344 pub keywords: Vec<Keyword>,
345}
346
347#[derive(Default, Debug, Clone, Serialize, Deserialize)]
349#[cfg_attr(feature = "validate", derive(Validate))]
350#[serde(default)]
351pub struct Reference {
352 pub name: Option<String>,
354 pub reference: Option<String>,
356}
357
358#[derive(Default, Debug, Clone, Serialize, Deserialize)]
360#[cfg_attr(feature = "validate", derive(Validate))]
361#[serde(default)]
362pub struct Project {
363 pub name: Option<String>,
365 pub description: Option<String>,
367 pub highlights: Vec<Highlight>,
369 pub keywords: Vec<Keyword>,
371 #[serde(rename = "startDate")]
372 #[cfg_attr(
373 feature = "validate",
374 validate(
375 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
376 )
377 )]
378 pub start_date: Option<String>,
379 #[serde(rename = "endDate")]
380 #[cfg_attr(
381 feature = "validate",
382 validate(
383 pattern = r"^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$"
384 )
385 )]
386 pub end_date: Option<String>,
387 pub url: Option<String>,
389 pub roles: Vec<Role>,
391 pub entity: Option<String>,
393 pub r#type: Option<String>,
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize)]
399#[cfg_attr(feature = "validate", derive(Validate))]
400pub struct Role(pub String);
401
402impl fmt::Display for Role {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 write!(f, "{}", self.0)
405 }
406}
407
408#[derive(Default, Debug, Clone, Serialize, Deserialize)]
410#[cfg_attr(feature = "validate", derive(Validate))]
411#[serde(default)]
412pub struct Meta {
413 pub canonical: Option<String>,
415 pub version: Option<String>,
417 #[serde(rename = "lastModified")]
419 pub last_modified: Option<String>,
420}
421
422#[cfg(feature = "validate")]
423#[cfg(test)]
424mod validate {
425 use super::*;
426
427 #[test]
428 fn sample() -> Result<(), Box<dyn std::error::Error>> {
429 const SAMPLE: &str = include_str!("../sample.resume.json");
430
431 let resume: Resume = serde_json::from_str(SAMPLE)?;
432 resume.validate()?;
433
434 Ok(())
435 }
436
437 #[test]
438 #[ignore = "Run explicitly"]
439 fn env() -> Result<(), Box<dyn std::error::Error>> {
440 let resume_file = std::env::var_os("RESUME_FILE").unwrap();
441 let resume = std::fs::read_to_string(resume_file)?;
442
443 let resume: Resume = serde_json::from_str(&resume)?;
444 resume.validate()?;
445
446 println!("{resume:#?}");
447
448 Ok(())
449 }
450}