1use std::path::Path;
7
8use crate::RrfError;
9
10pub trait FromRecord: Sized {
12 fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError>;
19}
20
21struct Fields<'a> {
23 record: &'a csv::ByteRecord,
24 path: &'a Path,
25 line: u64,
26}
27
28impl Fields<'_> {
29 fn new<'a>(
30 record: &'a csv::ByteRecord,
31 path: &'a Path,
32 line: u64,
33 expected: usize,
34 ) -> Result<Fields<'a>, RrfError> {
35 if record.len() < expected {
36 return Err(RrfError::Columns {
37 path: path.to_path_buf(),
38 line,
39 columns: record.len(),
40 expected,
41 });
42 }
43 Ok(Fields { record, path, line })
44 }
45
46 fn text(&self, index: usize) -> Result<String, RrfError> {
47 let bytes = self.record.get(index).unwrap_or_default();
48 std::str::from_utf8(bytes)
49 .map(str::to_owned)
50 .map_err(|source| RrfError::Utf8 {
51 path: self.path.to_path_buf(),
52 line: self.line,
53 source,
54 })
55 }
56
57 fn optional(&self, index: usize) -> Result<Option<String>, RrfError> {
58 let text = self.text(index)?;
59 Ok((!text.is_empty()).then_some(text))
60 }
61
62 fn id(&self, index: usize) -> Result<u64, RrfError> {
63 let text = self.text(index)?;
64 text.parse().map_err(|source| RrfError::Identifier {
65 path: self.path.to_path_buf(),
66 line: self.line,
67 value: text,
68 source,
69 })
70 }
71
72 fn optional_id(&self, index: usize) -> Result<Option<u64>, RrfError> {
73 match self.optional(index)? {
74 None => Ok(None),
75 Some(text) => text
76 .parse()
77 .map(Some)
78 .map_err(|source| RrfError::Identifier {
79 path: self.path.to_path_buf(),
80 line: self.line,
81 value: text,
82 source,
83 }),
84 }
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct Atom {
91 pub rxcui: u64,
93 pub language: String,
95 pub rxaui: u64,
97 pub sab: String,
99 pub tty: String,
101 pub code: String,
103 pub name: String,
105 pub suppress: String,
107}
108
109impl FromRecord for Atom {
110 fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError> {
111 let f = Fields::new(record, path, line, 18)?;
112 Ok(Self {
113 rxcui: f.id(0)?,
114 language: f.text(1)?,
115 rxaui: f.id(7)?,
116 sab: f.text(11)?,
117 tty: f.text(12)?,
118 code: f.text(13)?,
119 name: f.text(14)?,
120 suppress: f.text(16)?,
121 })
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct Relationship {
128 pub rxcui1: Option<u64>,
130 pub rxaui1: Option<u64>,
132 pub rel: String,
134 pub rxcui2: Option<u64>,
136 pub rxaui2: Option<u64>,
138 pub rela: Option<String>,
140 pub sab: String,
142}
143
144impl FromRecord for Relationship {
145 fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError> {
146 let f = Fields::new(record, path, line, 16)?;
147 Ok(Self {
148 rxcui1: f.optional_id(0)?,
149 rxaui1: f.optional_id(1)?,
150 rel: f.text(3)?,
151 rxcui2: f.optional_id(4)?,
152 rxaui2: f.optional_id(5)?,
153 rela: f.optional(7)?,
154 sab: f.text(10)?,
155 })
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct Attribute {
162 pub rxcui: u64,
164 pub rxaui: Option<u64>,
166 pub name: String,
168 pub sab: String,
170 pub value: String,
172 pub suppress: String,
174}
175
176impl FromRecord for Attribute {
177 fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError> {
178 let f = Fields::new(record, path, line, 13)?;
179 Ok(Self {
180 rxcui: f.id(0)?,
181 rxaui: f.optional_id(3)?,
182 name: f.text(8)?,
183 sab: f.text(9)?,
184 value: f.text(10)?,
185 suppress: f.text(11)?,
186 })
187 }
188}
189
190#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct SemanticType {
193 pub rxcui: u64,
195 pub tui: String,
197 pub name: String,
199}
200
201impl FromRecord for SemanticType {
202 fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError> {
203 let f = Fields::new(record, path, line, 6)?;
204 Ok(Self {
205 rxcui: f.id(0)?,
206 tui: f.text(1)?,
207 name: f.text(3)?,
208 })
209 }
210}