1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use yaserde_derive::{YaDeserialize, YaSerialize};

use crate::{
    Agent, Attribution, Document, Event, GedcomxError, Group, Id, Lang, Person, PlaceDescription,
    Relationship, Result, SourceDescription, Uri,
};

/// A container for a set of GEDCOM X data.
#[skip_serializing_none]
#[derive(Deserialize, Serialize, YaSerialize, YaDeserialize, Debug, PartialEq, Clone, Default)]
#[non_exhaustive]
#[yaserde(
    rename = "gedcomx",
    prefix = "gx",
    default_namespace = "gx",
    namespace = "gx: http://gedcomx.org/v1/"
)]
#[serde(rename_all = "camelCase")]
pub struct Gedcomx {
    /// An identifier for the data set.
    #[yaserde(attribute)]
    pub id: Option<Id>,

    /// The locale identifier for the data set.
    #[yaserde(attribute, prefix = "xml")]
    pub lang: Option<Lang>,

    /// The attribution of this data set.
    pub attribution: Option<Attribution>,

    /// The list of persons contained in the data set.
    #[yaserde(rename = "person", prefix = "gx")]
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub persons: Vec<Person>,

    /// The list of relationships contained in the data set.
    #[yaserde(rename = "relationship")]
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub relationships: Vec<Relationship>,

    /// The list of source descriptions contained in the data set.
    #[yaserde(rename = "sourceDescription")]
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub source_descriptions: Vec<SourceDescription>,

    /// The list of agents contained in the data set.
    #[yaserde(rename = "agent")]
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub agents: Vec<Agent>,

    /// The list of events contained in the data set.
    #[yaserde(rename = "event")]
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub events: Vec<Event>,

    /// The list of documents contained in the data set.
    #[yaserde(rename = "document")]
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub documents: Vec<Document>,

    /// The list of places contained in the data set.
    #[yaserde(rename = "place")]
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub places: Vec<PlaceDescription>,

    /// The list of groups contained in the data set.
    #[yaserde(rename = "group")]
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub groups: Vec<Group>,

    /// Reference to the description of this data set.
    ///
    /// If provided, MUST resolve to an instance of SourceDescription.
    // TODO: Enforce
    #[yaserde(attribute)]
    pub description: Option<Uri>,
}

impl Gedcomx {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        id: Option<Id>,
        lang: Option<Lang>,
        attribution: Option<Attribution>,
        persons: Vec<Person>,
        relationships: Vec<Relationship>,
        source_descriptions: Vec<SourceDescription>,
        agents: Vec<Agent>,
        events: Vec<Event>,
        documents: Vec<Document>,
        places: Vec<PlaceDescription>,
        groups: Vec<Group>,
        description: Option<Uri>,
    ) -> Self {
        Self {
            id,
            lang,
            attribution,
            persons,
            relationships,
            source_descriptions,
            agents,
            events,
            documents,
            places,
            groups,
            description,
        }
    }

    pub fn builder() -> GedcomxBuilder {
        GedcomxBuilder::new()
    }
}

pub struct GedcomxBuilder(Gedcomx);

impl GedcomxBuilder {
    pub(crate) fn new() -> Self {
        Self(Gedcomx::default())
    }

    pub fn agent(&mut self, agent: Agent) -> &mut Self {
        self.0.agents.push(agent);
        self
    }

    pub fn person(&mut self, person: Person) -> &mut Self {
        self.0.persons.push(person);
        self
    }

    pub fn persons(&mut self, persons: Vec<Person>) -> &mut Self {
        self.0.persons = persons;
        self
    }

    pub fn relationship(&mut self, relationship: Relationship) -> &mut Self {
        self.0.relationships.push(relationship);
        self
    }

    pub fn relationships(&mut self, relationships: Vec<Relationship>) -> &mut Self {
        self.0.relationships = relationships;
        self
    }

    pub fn document(&mut self, document: Document) -> &mut Self {
        self.0.documents.push(document);
        self
    }

    pub fn attribution(&mut self, atribution: Attribution) -> &mut Self {
        self.0.attribution = Some(atribution);
        self
    }

    pub fn event(&mut self, event: Event) -> &mut Self {
        self.0.events.push(event);
        self
    }

    pub fn source_description(&mut self, source_description: SourceDescription) -> &mut Self {
        self.0.source_descriptions.push(source_description);
        self
    }

    pub fn source_descriptions(
        &mut self,
        source_descriptions: Vec<SourceDescription>,
    ) -> &mut Self {
        self.0.source_descriptions = source_descriptions;
        self
    }

    pub fn agents(&mut self, agents: Vec<Agent>) -> &mut Self {
        self.0.agents = agents;
        self
    }

    pub fn places(&mut self, places: Vec<PlaceDescription>) -> &mut Self {
        self.0.places = places;
        self
    }

    pub fn build(&self) -> Gedcomx {
        Gedcomx::new(
            self.0.id.clone(),
            self.0.lang.clone(),
            self.0.attribution.clone(),
            self.0.persons.clone(),
            self.0.relationships.clone(),
            self.0.source_descriptions.clone(),
            self.0.agents.clone(),
            self.0.events.clone(),
            self.0.documents.clone(),
            self.0.places.clone(),
            self.0.groups.clone(),
            self.0.description.clone(),
        )
    }
}

// Convenience methods for serializing / deserializing to JSON / XML.
impl Gedcomx {
    /// Serialize the instance as a string of JSON.
    /// # Errors
    ///
    /// Returns `GedcomxError::JSONError` if serialization fails.
    pub fn to_json_string(&self) -> Result<String> {
        serde_json::to_string(self).map_err(GedcomxError::JSONError)
    }

    /// Serialize the instance as a string of pretty-printed JSON.
    /// # Errors
    ///
    /// Returns `GedcomxError::JSONError` if serialization fails.
    pub fn to_json_string_pretty(&self) -> Result<String> {
        serde_json::to_string_pretty(self).map_err(GedcomxError::JSONError)
    }

    /// Serialize the instance as JSON into the IO stream.
    /// # Errors
    ///
    /// Returns `GedcomxError::JSONError` if serialization fails.
    pub fn to_writer_as_json<W: std::io::Write>(&self, writer: W) -> Result<()> {
        serde_json::to_writer(writer, self).map_err(GedcomxError::JSONError)
    }

    /// Serialize the instance as pretty-printed JSON into the IO stream.
    /// # Errors
    ///
    /// Returns `GedcomxError::JSONError` if serialization fails.
    pub fn to_writer_as_json_pretty<W: std::io::Write>(&self, writer: W) -> Result<()> {
        serde_json::to_writer_pretty(writer, self).map_err(GedcomxError::JSONError)
    }

    /// Deserialize an instance of the type from a string of JSON text.
    /// # Errors
    ///
    /// Returns `GedcomxError::JSONError` if deserialization fails.    
    pub fn from_json_str(s: &str) -> Result<Self> {
        serde_json::from_str(s).map_err(GedcomxError::JSONError)
    }

    /// Deserialize an instance of the type from an IO stream of JSON.
    /// # Errors
    ///
    /// Returns `GedcomxError::JSONError` if deserialization fails.
    pub fn from_json_reader<R: std::io::Read>(rdr: R) -> Result<Self> {
        serde_json::from_reader(rdr).map_err(GedcomxError::JSONError)
    }

    /// Serialize the instance as a string of XML.
    /// # Errors
    ///
    /// Returns `GedcomxError::XMLError` if serialization fails.
    pub fn to_xml_string(&self) -> Result<String> {
        yaserde::ser::to_string(self).map_err(GedcomxError::XMLError)
    }

    /// Serialize the instance as a string of pretty-printed XML.
    /// # Errors
    ///
    /// Returns `GedcomxError::XMLError` if serialization fails.
    pub fn to_xml_string_pretty(&self) -> Result<String> {
        let config = yaserde::ser::Config {
            perform_indent: true,
            ..yaserde::ser::Config::default()
        };
        yaserde::ser::to_string_with_config(self, &config).map_err(GedcomxError::XMLError)
    }

    /// Serialize the instance as XML into the IO stream.
    /// # Errors
    ///
    /// Returns `GedcomxError::XMLError` if serialization fails.
    pub fn to_writer_as_xml<W: std::io::Write>(&self, writer: W) -> Result<()> {
        yaserde::ser::serialize_with_writer(self, writer, &yaserde::ser::Config::default())
            .map(|_| ())
            .map_err(GedcomxError::XMLError)
    }

    /// Serialize the instance as pretty-printed XML into the IO stream.
    /// # Errors
    ///
    /// Returns `GedcomxError::XMLError` if serialization fails.
    pub fn to_writer_as_xml_pretty<W: std::io::Write>(&self, writer: W) -> Result<()> {
        let config = yaserde::ser::Config {
            perform_indent: true,
            ..yaserde::ser::Config::default()
        };
        yaserde::ser::serialize_with_writer(self, writer, &config)
            .map(|_| ())
            .map_err(GedcomxError::XMLError)
    }

    /// Deserialize an instance of the type from a string of XML text.
    /// # Errors
    ///
    /// Returns `GedcomxError::XMLError` if deserialization fails.
    pub fn from_xml_str(s: &str) -> Result<Self> {
        yaserde::de::from_str(s).map_err(GedcomxError::XMLError)
    }

    /// Deserialize an instance of the type from an IO stream of XML.
    /// # Errors
    ///
    /// Returns `GedcomxError::XMLError` if deserialization fails.
    pub fn from_xml_reader<R: std::io::Read>(rdr: R) -> Result<Self> {
        yaserde::de::from_reader(rdr).map_err(GedcomxError::XMLError)
    }
}