1use crate::error::{Result, ShapefileError};
7use std::io::{Read, Write};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum FieldType {
12 Character,
14 Number,
16 Logical,
18 Date,
20 Float,
22 Memo,
24}
25
26impl FieldType {
27 pub fn from_code(code: u8) -> Result<Self> {
29 match code {
30 b'C' => Ok(Self::Character),
31 b'N' => Ok(Self::Number),
32 b'L' => Ok(Self::Logical),
33 b'D' => Ok(Self::Date),
34 b'F' => Ok(Self::Float),
35 b'M' => Ok(Self::Memo),
36 _ => Err(ShapefileError::DbfError {
37 message: format!("invalid field type code: {}", code as char),
38 field: None,
39 record: None,
40 }),
41 }
42 }
43
44 pub fn to_code(self) -> u8 {
46 match self {
47 Self::Character => b'C',
48 Self::Number => b'N',
49 Self::Logical => b'L',
50 Self::Date => b'D',
51 Self::Float => b'F',
52 Self::Memo => b'M',
53 }
54 }
55
56 pub fn name(self) -> &'static str {
58 match self {
59 Self::Character => "Character",
60 Self::Number => "Number",
61 Self::Logical => "Logical",
62 Self::Date => "Date",
63 Self::Float => "Float",
64 Self::Memo => "Memo",
65 }
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct FieldDescriptor {
72 pub name: String,
74 pub field_type: FieldType,
76 pub length: u8,
78 pub decimal_count: u8,
80}
81
82impl FieldDescriptor {
83 pub fn new(name: String, field_type: FieldType, length: u8, decimal_count: u8) -> Result<Self> {
85 if name.len() > 10 {
86 return Err(ShapefileError::InvalidFieldDescriptor {
87 message: format!("field name too long: {} (max 10)", name.len()),
88 field: Some(name),
89 });
90 }
91
92 if name.is_empty() {
93 return Err(ShapefileError::InvalidFieldDescriptor {
94 message: "field name cannot be empty".to_string(),
95 field: None,
96 });
97 }
98
99 Ok(Self {
100 name,
101 field_type,
102 length,
103 decimal_count,
104 })
105 }
106
107 pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
109 let mut name_bytes = [0u8; 11];
111 reader
112 .read_exact(&mut name_bytes)
113 .map_err(|_| ShapefileError::unexpected_eof("reading field name"))?;
114
115 let name_end = name_bytes.iter().position(|&b| b == 0).unwrap_or(11);
117 let name = String::from_utf8_lossy(&name_bytes[..name_end]).to_string();
118
119 let mut type_byte = [0u8; 1];
121 reader
122 .read_exact(&mut type_byte)
123 .map_err(|_| ShapefileError::unexpected_eof("reading field type"))?;
124 let field_type = FieldType::from_code(type_byte[0])?;
125
126 let mut reserved = [0u8; 4];
128 reader
129 .read_exact(&mut reserved)
130 .map_err(|_| ShapefileError::unexpected_eof("reading field reserved bytes"))?;
131
132 let mut length = [0u8; 1];
134 reader
135 .read_exact(&mut length)
136 .map_err(|_| ShapefileError::unexpected_eof("reading field length"))?;
137
138 let mut decimal_count = [0u8; 1];
140 reader
141 .read_exact(&mut decimal_count)
142 .map_err(|_| ShapefileError::unexpected_eof("reading decimal count"))?;
143
144 let mut reserved2 = [0u8; 14];
146 reader
147 .read_exact(&mut reserved2)
148 .map_err(|_| ShapefileError::unexpected_eof("reading field reserved bytes 2"))?;
149
150 Ok(Self {
151 name,
152 field_type,
153 length: length[0],
154 decimal_count: decimal_count[0],
155 })
156 }
157
158 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
160 let mut name_bytes = [0u8; 11];
162 let name_slice = self.name.as_bytes();
163 let copy_len = name_slice.len().min(10);
164 name_bytes[..copy_len].copy_from_slice(&name_slice[..copy_len]);
165 writer.write_all(&name_bytes).map_err(ShapefileError::Io)?;
166
167 writer
169 .write_all(&[self.field_type.to_code()])
170 .map_err(ShapefileError::Io)?;
171
172 writer.write_all(&[0u8; 4]).map_err(ShapefileError::Io)?;
174
175 writer
177 .write_all(&[self.length])
178 .map_err(ShapefileError::Io)?;
179
180 writer
182 .write_all(&[self.decimal_count])
183 .map_err(ShapefileError::Io)?;
184
185 writer.write_all(&[0u8; 14]).map_err(ShapefileError::Io)?;
187
188 Ok(())
189 }
190}
191
192#[derive(Debug, Clone, PartialEq)]
194pub enum FieldValue {
195 String(String),
197 Integer(i64),
199 Float(f64),
201 Boolean(bool),
203 Date(String),
205 Null,
207}
208
209impl FieldValue {
210 pub fn parse(bytes: &[u8], field_type: FieldType, decimal_count: u8) -> Result<Self> {
212 let trimmed = String::from_utf8_lossy(bytes).trim().to_string();
214
215 if trimmed.is_empty() {
216 return Ok(Self::Null);
217 }
218
219 match field_type {
220 FieldType::Character => Ok(Self::String(trimmed)),
221 FieldType::Number | FieldType::Float => {
222 if decimal_count > 0 {
223 trimmed
225 .parse::<f64>()
226 .map(Self::Float)
227 .map_err(|_| ShapefileError::DbfError {
228 message: format!("failed to parse float: {}", trimmed),
229 field: None,
230 record: None,
231 })
232 } else {
233 trimmed.parse::<i64>().map(Self::Integer).map_err(|_| {
235 ShapefileError::DbfError {
236 message: format!("failed to parse integer: {}", trimmed),
237 field: None,
238 record: None,
239 }
240 })
241 }
242 }
243 FieldType::Logical => {
244 let value = match trimmed.chars().next() {
245 Some('T') | Some('t') | Some('Y') | Some('y') => true,
246 Some('F') | Some('f') | Some('N') | Some('n') => false,
247 _ => {
248 return Err(ShapefileError::DbfError {
249 message: format!("invalid logical value: {}", trimmed),
250 field: None,
251 record: None,
252 });
253 }
254 };
255 Ok(Self::Boolean(value))
256 }
257 FieldType::Date => {
258 if trimmed.len() != 8 {
260 return Err(ShapefileError::DbfError {
261 message: format!("invalid date format: {} (expected YYYYMMDD)", trimmed),
262 field: None,
263 record: None,
264 });
265 }
266 Ok(Self::Date(trimmed))
267 }
268 FieldType::Memo => Ok(Self::String(trimmed)),
269 }
270 }
271
272 pub fn format(&self, length: usize) -> Vec<u8> {
274 let mut buffer = vec![b' '; length];
275
276 let content = match self {
277 Self::String(s) => s.clone(),
278 Self::Integer(i) => i.to_string(),
279 Self::Float(f) => f.to_string(),
280 Self::Boolean(b) => {
281 if *b {
282 "T".to_string()
283 } else {
284 "F".to_string()
285 }
286 }
287 Self::Date(d) => d.clone(),
288 Self::Null => String::new(),
289 };
290
291 let bytes = content.as_bytes();
292 let copy_len = bytes.len().min(length);
293 buffer[..copy_len].copy_from_slice(&bytes[..copy_len]);
294
295 buffer
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302 use std::io::Cursor;
303
304 #[test]
305 fn test_field_type_conversion() {
306 assert_eq!(
307 FieldType::from_code(b'C').expect("valid field type code"),
308 FieldType::Character
309 );
310 assert_eq!(FieldType::Character.to_code(), b'C');
311 assert_eq!(FieldType::Number.name(), "Number");
312 }
313
314 #[test]
315 fn test_field_descriptor_round_trip() {
316 let desc = FieldDescriptor::new("NAME".to_string(), FieldType::Character, 50, 0)
317 .expect("valid field descriptor");
318
319 let mut buffer = Vec::new();
320 desc.write(&mut buffer).expect("write field descriptor");
321
322 assert_eq!(buffer.len(), 32); let mut cursor = Cursor::new(buffer);
325 let read_desc = FieldDescriptor::read(&mut cursor).expect("read field descriptor");
326
327 assert_eq!(read_desc.name, "NAME");
328 assert_eq!(read_desc.field_type, FieldType::Character);
329 assert_eq!(read_desc.length, 50);
330 }
331
332 #[test]
333 fn test_field_value_parsing() {
334 let value =
336 FieldValue::parse(b" test ", FieldType::Character, 0).expect("parse character field");
337 assert_eq!(value, FieldValue::String("test".to_string()));
338
339 let value =
341 FieldValue::parse(b" 123 ", FieldType::Number, 0).expect("parse integer field");
342 assert_eq!(value, FieldValue::Integer(123));
343
344 let value = FieldValue::parse(b" 12.34 ", FieldType::Number, 2).expect("parse float field");
346 assert_eq!(value, FieldValue::Float(12.34));
347
348 let value =
350 FieldValue::parse(b"T", FieldType::Logical, 0).expect("parse logical field true");
351 assert_eq!(value, FieldValue::Boolean(true));
352
353 let value =
354 FieldValue::parse(b"F", FieldType::Logical, 0).expect("parse logical field false");
355 assert_eq!(value, FieldValue::Boolean(false));
356
357 let value = FieldValue::parse(b"20240125", FieldType::Date, 0).expect("parse date field");
359 assert_eq!(value, FieldValue::Date("20240125".to_string()));
360
361 let value = FieldValue::parse(b" ", FieldType::Character, 0).expect("parse empty field");
363 assert_eq!(value, FieldValue::Null);
364 }
365
366 #[test]
367 fn test_field_value_formatting() {
368 let value = FieldValue::String("test".to_string());
369 let formatted = value.format(10);
370 assert_eq!(formatted.len(), 10);
371 assert_eq!(&formatted[..4], b"test");
372
373 let value = FieldValue::Integer(123);
374 let formatted = value.format(10);
375 assert_eq!(&formatted[..3], b"123");
376
377 let value = FieldValue::Boolean(true);
378 let formatted = value.format(1);
379 assert_eq!(formatted[0], b'T');
380 }
381}