1use crate::parse;
2use crate::types::{Source, Submitter};
6
7use super::DateTime;
8use super::Gedc;
9use super::Line;
10
11#[derive(Debug, Default)]
38#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
39pub struct Header {
40 pub encoding: Option<String>,
41 pub copyright: Option<String>,
42 pub date: Option<DateTime>,
43 pub destination: Option<String>,
44 pub gedcom_version: Option<Gedc>,
45 pub language: Option<String>,
46 pub filename: Option<String>,
47 pub note: Option<String>,
48 pub source: Option<Source>,
49 pub submitter: Option<Submitter>,
50 pub submission: Option<String>,
51}
52
53impl Header {
54 pub fn parse(mut record: String) -> Header {
55 let mut header = Header {
56 encoding: None,
57 copyright: None,
58 date: None,
60 destination: None,
61 gedcom_version: None,
62 language: None,
63 filename: None,
64 note: None,
65 source: None,
66 submitter: None,
67 submission: None,
68 };
69
70 while !record.is_empty() {
72 let mut buffer: &str = record.as_str();
73 let line = Line::peek(&mut buffer).unwrap();
74
75 if line.level == 0 && line.tag == "HEAD" {
77 Line::parse(&mut buffer).unwrap();
81 } else if line.level == 1 {
82 match line.tag {
84 "CHAR" => {
85 header.encoding = Some(line.value.to_string());
86 Line::parse(&mut buffer).unwrap();
88 }
89 "COPR" => {
90 header.copyright = parse::get_tag_value(&mut buffer).unwrap();
92 }
99 "DATE" => {
104 (buffer, header.date) = DateTime::parse(&record);
107 }
108 "DEST" => {
109 header.destination = Some(line.value.to_string());
110 Line::parse(&mut buffer).unwrap();
112 }
113 "FILE" => {
114 header.filename = Some(line.value.to_string());
115 Line::parse(&mut buffer).unwrap();
117 }
118 "GEDC" => {
119 (buffer, header.gedcom_version) = Gedc::parse(&record);
120 }
121 "LANG" => {
122 header.language = Some(line.value.to_string());
123 Line::parse(&mut buffer).unwrap();
125 }
126 "NOTE" => {
127 header.note = parse::get_tag_value(&mut buffer).unwrap();
130 }
135 "SOUR" => {
136 (buffer, header.source) = Source::parse(&record);
137 }
138 "SUBM" => {
139 (buffer, header.submitter) = Submitter::parse(&record);
140 }
141 _ => {
142 Line::parse(&mut buffer).unwrap();
145 }
146 };
147 } else {
148 Line::parse(&mut buffer).unwrap();
150 }
151
152 record = buffer.to_string();
153 }
154 header
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use crate::types::{corporation::Corporation, Address, DateTime, Form};
161
162 use super::Header;
163
164 #[test]
165 fn parse_header() {
166 let data = vec![
167 "0 HEAD",
168 "1 CHAR UTF-8",
169 "1 SOUR Ancestry.com Family Trees",
170 "2 DATA Name of source data",
171 "3 DATE 1 JAN 1998",
172 "3 COPR Copyright of source data",
173 "2 VERS (2010.3)",
174 "2 NAME Ancestry.com Family Trees",
175 "2 CORP Ancestry.com",
176 "3 ADDR",
177 "4 ADR1 Example Software",
178 "4 ADR2 123 Main Street",
179 "4 ADR3 Ste 1",
180 "4 CITY Anytown",
181 "4 STAE IL",
182 "4 POST 55555",
183 "4 CTRY USA",
184 "3 PHON +1-800-555-1111",
185 "3 PHON +1-800-555-1212",
186 "3 PHON +1-800-555-1313",
187 "3 EMAIL a@example.com",
188 "3 EMAIL b@example.com",
189 "3 EMAIL c@example.com",
190 "3 FAX +1-800-555-1414",
191 "3 FAX +1-800-555-1515",
192 "3 FAX +1-800-555-1616",
193 "3 WWW https://www.example.com",
194 "3 WWW https://www.example.org",
195 "3 WWW https://www.example.net",
196 "1 SUBM @U1@",
197 "1 GEDC",
198 "2 VERS 5.5",
199 "2 FORM LINEAGE-LINKED",
200 "3 VERS 5.5",
201 "1 COPR A copyright statement",
202 "1 LANG English",
203 "1 DATE 1 JAN 2023",
204 "2 TIME 12:13:14.15",
205 "0 @U1@ SUBM",
207 "1 NAME Adam Israel",
208 "1 ADDR",
209 "2 ADR1 Example Software",
210 "2 ADR2 123 Main Street",
211 "2 ADR3 Ste 1",
212 "2 CITY Anytown",
213 "2 STAE IL ",
214 "2 POST 55555",
215 "2 CTRY USA",
216 "1 PHON +1-800-555-1111",
217 "1 PHON +1-800-555-1212",
218 "1 PHON +1-800-555-1313",
219 "1 EMAIL a@@example.com",
220 "1 EMAIL b@@example.com",
221 "1 EMAIL c@@example.com",
222 "1 FAX +1-800-555-1414",
223 "1 FAX +1-800-555-1515",
224 "1 FAX +1-800-555-1616",
225 "1 WWW https://www.example.com",
226 "1 WWW https://www.example.org",
227 "1 WWW https://www.example.net",
228 "1 OBJE @M1@",
229 "1 RIN 1",
230 "1 CHAN",
231 "2 DATE 7 SEP 2000",
232 "3 TIME 8:35:36",
233 ];
234
235 let header = Header::parse(data.join("\n"));
236
237 assert!(header.encoding.is_some());
239 assert!(header.encoding == Some("UTF-8".to_string()));
240
241 assert!(header.copyright.is_some());
243 assert!(header.copyright == Some("A copyright statement".to_string()));
244
245 assert!(header.source.is_some());
247 assert!(header.source.as_ref().unwrap().source == "Ancestry.com Family Trees".to_string());
248 assert!(header.source.as_ref().unwrap().version == Some("(2010.3)".to_string()));
249
250 assert!(
251 header
252 .source
253 .as_ref()
254 .unwrap()
255 .data
256 .as_ref()
257 .unwrap()
258 .copyright
259 == Some("Copyright of source data".to_string())
260 );
261 assert!(
262 header.source.as_ref().unwrap().data.as_ref().unwrap().date
263 == Some(DateTime {
264 date: Some("1 JAN 1998".to_string()),
265 time: None,
266 })
267 );
268 assert!(
269 header.source.as_ref().unwrap().data.as_ref().unwrap().name
270 == Some("Name of source data".to_string())
271 );
272
273 assert!(
274 header.source.as_ref().unwrap().name == Some("Ancestry.com Family Trees".to_string())
275 );
276 assert!(
277 header.source.as_ref().unwrap().corporation
278 == Some(Corporation {
279 name: Some("Ancestry.com".to_string()),
280 address: Some(Address {
281 addr1: Some("Example Software".to_string()),
282 addr2: Some("123 Main Street".to_string()),
283 addr3: Some("Ste 1".to_string()),
284 city: Some("Anytown".to_string()),
285 state: Some("IL".to_string()),
286 postal_code: Some("55555".to_string()),
287 country: Some("USA".to_string()),
288 phone: vec![
289 "+1-800-555-1111".to_string(),
290 "+1-800-555-1212".to_string(),
291 "+1-800-555-1313".to_string(),
292 ],
293 email: vec![
294 "a@example.com".to_string(),
295 "b@example.com".to_string(),
296 "c@example.com".to_string(),
297 ],
298 fax: vec![
299 "+1-800-555-1414".to_string(),
300 "+1-800-555-1515".to_string(),
301 "+1-800-555-1616".to_string(),
302 ],
303 www: vec![
304 "https://www.example.com".to_string(),
305 "https://www.example.org".to_string(),
306 "https://www.example.net".to_string(),
307 ],
308 })
309 })
310 );
311
312 assert!(
314 header.gedcom_version.as_ref().unwrap().form
315 == Some(Form {
316 name: Some("LINEAGE-LINKED".to_string()),
317 version: Some("5.5".to_string()),
318 })
319 );
320 assert!(header.gedcom_version.as_ref().unwrap().version == Some("5.5".to_string()));
321
322 assert!(header.language.is_some());
324 assert!(header.language == Some("English".to_string()));
325
326 assert!(header.date.is_some());
328 assert!(
329 header.date
330 == Some(DateTime {
331 date: Some("1 JAN 2023".to_string()),
332 time: Some("12:13:14.15".to_string())
333 })
334 );
335
336 assert!(header.submitter.is_some());
338 }
339}