Skip to content

Commit 98fa12f

Browse files
committed
TTL syntax
1 parent b3fc078 commit 98fa12f

29 files changed

+3675
-801
lines changed

Cargo.lock

Lines changed: 100 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gedcomfy/Cargo.toml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,32 @@ license.workspace = true
66
authors.workspace = true
77

88
[features]
9-
default = ["miette-highlighting", "kdl"]
10-
miette-highlighting = ["miette/fancy-no-backtrace"]
11-
legacy-encodings = ["dep:oem_cp"]
9+
default = ["miette-highlighting", "kdl", "turtle"]
1210
kdl = ["dep:kdl"]
11+
legacy-encodings = ["dep:oem_cp"]
12+
miette-highlighting = ["miette/fancy-no-backtrace"]
13+
turtle = ["dep:sophia_api", "dep:sophia_turtle"]
1314

1415
[dependencies]
1516
ascii = "1.1.0"
1617
complex-indifference = { path = "../complex-indifference" }
1718
derive_more = { version = "2.0.1", features = ["from", "display"] }
18-
errful = { path = "../errful" }
19+
dunce = "1.0.5"
1920
encoding_rs = "0.8.34"
21+
errful = { path = "../errful" }
22+
itertools = "0.14.0"
2023
kdl = { version = "4.6.0", optional = true }
24+
memmap2 = "0.9.5"
2125
miette = { version = "7.4.0", features = ["fancy-no-backtrace"] }
2226
oem_cp = { version = "2.0.0", optional = true }
2327
owo-colors = { version = "4.0.0", features = ["supports-colors"] }
2428
paste = "1.0.15"
29+
sophia_api = { version = "0.9.0", optional = true }
30+
sophia_turtle = { version = "0.9.0", optional = true }
31+
thiserror = "2.0.12"
2532
tracing = { version = "0.1", features = ["attributes"] }
2633
vec1 = "1.12.1"
2734
yoke = { version = "0.7.4", features = ["derive"] }
28-
itertools = "0.14.0"
29-
memmap2 = "0.9.5"
30-
dunce = "1.0.5"
31-
thiserror = "2.0.12"
3235

3336

3437
[dev-dependencies]

gedcomfy/src/reader.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub trait GEDCOMSource: ascii::AsAsciiStr + PartialEq<AsciiStr> {
4747
fn span_of(&self, source: &Self) -> SourceSpan;
4848
fn starts_with(&self, char: AsciiChar) -> bool;
4949
fn ends_with(&self, char: AsciiChar) -> bool;
50+
fn except_start_and_end(&self) -> &Self;
5051
fn is_empty(&self) -> bool;
5152
fn slice_from(&self, offset: usize) -> &Self;
5253
}
@@ -90,6 +91,10 @@ impl GEDCOMSource for str {
9091
fn split_once(&self, char: AsciiChar) -> Option<(&Self, &Self)> {
9192
(*self).split_once(char.as_char())
9293
}
94+
95+
fn except_start_and_end(&self) -> &Self {
96+
&self[1..self.len() - 1]
97+
}
9398
}
9499

95100
impl GEDCOMSource for [u8] {
@@ -133,6 +138,10 @@ impl GEDCOMSource for [u8] {
133138
let (before, after) = self.split_at(ix);
134139
Some((before, &after[1..]))
135140
}
141+
142+
fn except_start_and_end(&self) -> &Self {
143+
&self[1..self.len() - 1]
144+
}
136145
}
137146

138147
/// A value that is sourced from a specific location in a GEDCOM file.
@@ -610,6 +619,15 @@ impl Reader {
610619
self.build_result::<modes::kdl::Mode>(input)
611620
}
612621

622+
#[cfg(feature = "turtle")]
623+
/// Parses a GEDCOM file into Turtle format.
624+
pub fn parse_ttl<'i, 's>(
625+
&self,
626+
input: &'i (impl Input<'s> + ?Sized),
627+
) -> Result<Vec<u8>, WithSourceCode<'s, ReaderError>> {
628+
self.build_result::<modes::ttl::Mode>(input)
629+
}
630+
613631
#[instrument(skip_all)]
614632
fn build_result<'i, 's, M: ReadMode<'i>>(
615633
&self,

gedcomfy/src/reader/decoding.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
use std::{borrow::Cow, ops::Deref};
1+
use std::{borrow::Cow, io::Read, ops::Deref};
22

33
use ascii::AsAsciiStr;
4+
use itertools::Itertools;
45
use miette::SourceSpan;
56
use owo_colors::{OwoColorize, Stream};
67
use vec1::Vec1;
78

89
use super::{
910
encodings::{ansel, EncodingError, EncodingReason, SupportedEncoding},
10-
lines::LineSyntaxError,
11+
lines::{self, LineSyntaxError},
1112
records::RecordStructureError,
1213
versions::VersionError,
1314
};
@@ -19,17 +20,26 @@ pub enum DecodingError {
1920
#[diagnostic(transparent)]
2021
VersionError(#[from] VersionError),
2122

22-
#[error("Unable to determine encoding of GEDCOM file")]
23-
#[diagnostic(transparent)]
24-
EncodingError(#[from] EncodingError),
23+
#[error("A problem was found while trying to determine the encoding of the GEDCOM file")]
24+
EncodingError(
25+
#[from]
26+
#[diagnostic_source]
27+
EncodingError,
28+
),
2529

2630
#[error("GEDCOM file contained data which was invalid in the detected encoding")]
27-
#[diagnostic(transparent)]
28-
InvalidDataForEncoding(#[from] InvalidDataForEncodingError),
31+
InvalidDataForEncoding(
32+
#[from]
33+
#[diagnostic_source]
34+
InvalidDataForEncodingError,
35+
),
2936

3037
#[error("GEDCOM file structure is invalid")]
31-
#[diagnostic(transparent)]
32-
FileStructureError(#[from] FileStructureError),
38+
FileStructureError(
39+
#[from]
40+
#[diagnostic_source]
41+
FileStructureError,
42+
),
3343

3444
#[error(transparent)]
3545
#[diagnostic(transparent)]
@@ -131,7 +141,24 @@ pub fn detect_external_encoding(input: &[u8]) -> Result<Option<DetectedEncoding>
131141
// otherwise it’s probably not a GEDCOM file (at least in supported versions)
132142
// TODO: it could be the non-first GEDCOM file in a volume?
133143
// - check for '0 ' and then produce an error about that?
134-
_ => return Err(EncodingError::NotGedcomFile {}),
144+
_ => {
145+
let line = input
146+
.split(|c| matches!(c, b'\r' | b'\n'))
147+
.next()
148+
.unwrap_or(input);
149+
150+
let span_until = if line.len() < 100 { line.len() } else { 0 };
151+
152+
if lines::parse_line(input, line).is_ok() {
153+
return Err(EncodingError::MultiVolume {
154+
start: SourceSpan::from((0, span_until)),
155+
});
156+
}
157+
158+
return Err(EncodingError::NotGedcomFile {
159+
start: SourceSpan::from((0, span_until)),
160+
});
161+
}
135162
};
136163

137164
Ok(Some(result))

0 commit comments

Comments
 (0)