Skip to content

Commit 2dc8f9e

Browse files
authored
Merge pull request #52 from weso/issue51
Add DCTap support
2 parents f8b388b + 3bddde5 commit 2dc8f9e

File tree

10 files changed

+474
-28
lines changed

10 files changed

+474
-28
lines changed

dctap/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ repository.workspace = true
1111

1212
[dependencies]
1313
csv = "1.3.0"
14+
indexmap = { version = "2"}
1415
serde = "1.0"
1516
serde_derive = "1.0"
16-
1717
thiserror = "1"
1818
tracing = "0.1"

dctap/src/dctap.rs

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
use crate::{tap_config::TapConfig, tap_error::TapError};
1+
use crate::{tap_config::TapConfig, tap_error::TapError, ShapeId, TapReaderBuilder, TapShape};
2+
use indexmap::IndexMap;
23
use serde_derive::{Deserialize, Serialize};
3-
use std::path::Path;
4+
use std::{io, path::Path};
45
use tracing::debug;
56

67
#[derive(Debug, Serialize, Deserialize)]
78
struct TapShapeId(String);
89

9-
#[derive(Debug, Serialize)]
10+
#[derive(Debug, Serialize, PartialEq)]
1011
pub struct DCTap {
1112
version: String,
13+
shapes: IndexMap<Option<ShapeId>, TapShape>,
1214
}
1315

1416
impl Default for DCTap {
@@ -21,12 +23,58 @@ impl DCTap {
2123
pub fn new() -> DCTap {
2224
DCTap {
2325
version: "0.1".to_string(),
26+
shapes: IndexMap::new(),
2427
}
2528
}
2629

27-
pub fn read_buf(_path: &Path, _config: TapConfig) -> Result<DCTap, TapError> {
28-
let dctap = DCTap::new();
30+
pub fn add_shape(&mut self, shape: &TapShape) {
31+
self.shapes.insert(shape.shape_id(), shape.clone());
32+
}
33+
34+
pub fn from_path(path: &Path, _config: TapConfig) -> Result<DCTap, TapError> {
35+
let mut dctap = DCTap::new();
36+
debug!("DCTap parsed: {:?}", dctap);
37+
let mut tap_reader = TapReaderBuilder::new().flexible(true).from_path(path)?;
38+
for maybe_shape in tap_reader.shapes() {
39+
let shape = maybe_shape?;
40+
println!("Shape read: {shape:?}");
41+
dctap.add_shape(&shape)
42+
}
43+
Ok(dctap)
44+
}
45+
46+
pub fn from_reader<R: io::Read>(reader: R) -> Result<DCTap, TapError> {
47+
let mut dctap = DCTap::new();
2948
debug!("DCTap parsed: {:?}", dctap);
49+
let mut tap_reader = TapReaderBuilder::new().flexible(true).from_reader(reader)?;
50+
for maybe_shape in tap_reader.shapes() {
51+
let shape = maybe_shape?;
52+
println!("Shape read: {shape:?}");
53+
dctap.add_shape(&shape)
54+
}
3055
Ok(dctap)
3156
}
3257
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
use crate::{PropertyId, TapShape, TapStatement};
62+
63+
use super::*;
64+
65+
#[test]
66+
fn test_simple() {
67+
let data = "\
68+
shapeId,shapeLabel,propertyId,propertyLabel
69+
Person,PersonLabel,knows,KnowsLabel
70+
";
71+
let dctap = DCTap::from_reader(data.as_bytes()).unwrap();
72+
let mut expected_shape = TapShape::new();
73+
expected_shape.set_shape_id(&ShapeId::new("Person"));
74+
expected_shape.add_statement(TapStatement::new(PropertyId::new("knows")));
75+
76+
let mut expected_dctap = DCTap::new();
77+
expected_dctap.add_shape(&expected_shape);
78+
assert_eq!(dctap, expected_dctap);
79+
}
80+
}

dctap/src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,41 @@
88
pub mod dctap;
99
pub mod tap_config;
1010
pub mod tap_error;
11+
pub mod tap_headers;
12+
pub mod tap_reader;
1113
pub mod tap_shape;
1214
pub mod tap_statement;
1315

1416
pub use crate::tap_config::*;
1517
pub use crate::tap_error::*;
18+
pub use crate::tap_reader::*;
1619
pub use crate::tap_shape::*;
1720
pub use crate::tap_statement::*;
1821
pub use dctap::*;
22+
use serde_derive::{Deserialize, Serialize};
23+
24+
#[derive(Deserialize, Serialize, Debug, PartialEq, Default, Clone)]
25+
pub struct PropertyId {
26+
str: String,
27+
}
28+
29+
impl PropertyId {
30+
pub fn new(str: &str) -> PropertyId {
31+
PropertyId {
32+
str: str.to_string(),
33+
}
34+
}
35+
}
36+
37+
#[derive(Deserialize, Serialize, Debug, Hash, PartialEq, Eq, Clone)]
38+
pub struct ShapeId {
39+
str: String,
40+
}
41+
42+
impl ShapeId {
43+
pub fn new(str: &str) -> ShapeId {
44+
ShapeId {
45+
str: str.to_string(),
46+
}
47+
}
48+
}

dctap/src/tap_error.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,21 @@
1+
use std::result;
2+
3+
use csv::StringRecord;
14
use thiserror::Error;
25

6+
pub type Result<T> = result::Result<T, TapError>;
7+
38
#[derive(Error, Debug)]
4-
pub enum TapError {}
9+
pub enum TapError {
10+
#[error("CSV Error: {err}")]
11+
RDFParseError {
12+
#[from]
13+
err: csv::Error,
14+
},
15+
16+
#[error("Cannot obtain shape id with index {shape_id} from record {record:?}")]
17+
NoShapeId {
18+
shape_id: usize,
19+
record: StringRecord,
20+
},
21+
}

dctap/src/tap_headers.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use crate::tap_error::Result;
2+
use csv::StringRecord;
3+
use tracing::debug;
4+
5+
#[derive(Debug, Default)]
6+
pub(crate) struct TapHeaders {
7+
shape_id: Option<usize>,
8+
property_id: Option<usize>,
9+
}
10+
11+
impl TapHeaders {
12+
pub(crate) fn new() -> TapHeaders {
13+
TapHeaders::default()
14+
}
15+
16+
pub(crate) fn from_record(record: &StringRecord) -> Result<TapHeaders> {
17+
let mut shape_id = None;
18+
let mut property_id = None;
19+
20+
for (idx, field) in record.iter().enumerate() {
21+
match clean(field).as_str() {
22+
"SHAPEID" => shape_id = Some(idx),
23+
"PROPERTYID" => property_id = Some(idx),
24+
_ => {
25+
debug!("Unknown field reading headers: {field}")
26+
}
27+
}
28+
}
29+
Ok(TapHeaders {
30+
shape_id,
31+
property_id,
32+
})
33+
}
34+
35+
pub fn shape_id(&self) -> Option<usize> {
36+
self.shape_id
37+
}
38+
39+
pub fn property_id(&self) -> Option<usize> {
40+
self.property_id
41+
}
42+
}
43+
44+
fn clean(str: &str) -> String {
45+
str.to_uppercase()
46+
}

0 commit comments

Comments
 (0)