1use crate::encoding::DynEncoding;
2use crate::field::{DeletionFlag, FieldsInfo, DELETION_FLAG_SIZE};
3use crate::header::Header;
4use crate::memo::MemoReader;
5use crate::reading::{ReadingOptions, BACKLINK_SIZE, TERMINATOR_VALUE};
6use crate::writing::{write_header_parts, WritableAsDbaseField};
7use crate::ErrorKind::UnsupportedCodePage;
8use crate::{
9 Error, ErrorKind, FieldConversionError, FieldIOError, FieldInfo, FieldIterator, FieldValue,
10 FieldWriter, ReadableRecord, TableInfo, WritableRecord,
11};
12use byteorder::ReadBytesExt;
13use std::fmt::{Debug, Formatter};
14use std::io::{BufReader, BufWriter, Cursor, Read, Seek, SeekFrom, Write};
15use std::path::Path;
16
17#[cfg(target_family = "wasm")]
19type SharedFile = std::sync::Arc<std::fs::File>;
20#[cfg(not(target_family = "wasm"))]
21type SharedFile = std::fs::File;
22
23#[derive(Debug)]
24pub struct BufReadWriteFile {
25 input: BufReader<SharedFile>,
26 output: BufWriter<SharedFile>,
27}
28
29impl BufReadWriteFile {
30 fn new(file: impl Into<SharedFile>) -> std::io::Result<Self> {
31 let file = file.into();
32
33 #[cfg(target_family = "wasm")]
34 let file_ = file.clone();
35 #[cfg(not(target_family = "wasm"))]
36 let file_ = file.try_clone()?;
37
38 let input = BufReader::new(file_);
39 let output = BufWriter::new(file);
40 Ok(Self { input, output })
41 }
42}
43
44impl Read for BufReadWriteFile {
45 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
46 self.input.read(buf)
47 }
48}
49
50impl Write for BufReadWriteFile {
51 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
52 self.output.write(buf)
53 }
54
55 fn flush(&mut self) -> std::io::Result<()> {
56 self.output.flush()
57 }
58}
59
60impl Seek for BufReadWriteFile {
61 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
62 self.output.seek(pos)?;
63 self.input.seek(pos)
64 }
65}
66
67#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
69pub struct FieldIndex(pub usize);
70
71#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
73pub struct RecordIndex(pub usize);
74
75pub struct FieldRef<'a, T> {
80 file: &'a mut File<T>,
81 record_index: RecordIndex,
82 field_index: FieldIndex,
83}
84
85impl<T> Debug for FieldRef<'_, T> {
86 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
87 f.debug_struct("FieldRef")
88 .field("record_index", &self.record_index)
89 .field("field_index", &self.field_index)
90 .finish()
91 }
92}
93
94impl<T> FieldRef<'_, T> {
95 fn position_in_source(&self) -> u64 {
96 let record_position = self
97 .file
98 .header
99 .record_position(self.record_index.0)
100 .unwrap() as u64;
101
102 record_position + self.position_in_record() as u64
103 }
104
105 fn position_in_record(&self) -> usize {
106 self.file
107 .fields_info
108 .field_position_in_record(self.field_index.0)
109 .expect("internal error: invalid field index")
110 }
111}
112
113impl<T> FieldRef<'_, T>
114where
115 T: Seek,
116{
117 fn seek_to_beginning(&mut self) -> Result<u64, FieldIOError> {
118 let field_info = &self.file.fields_info[self.field_index.0];
119
120 self.file
121 .inner
122 .seek(SeekFrom::Start(self.position_in_source()))
123 .map_err(|e| FieldIOError::new(ErrorKind::IoError(e), Some(field_info.clone())))
124 }
125}
126
127impl<T> FieldRef<'_, T>
128where
129 T: Seek + Read,
130{
131 pub fn read(&mut self) -> Result<FieldValue, Error> {
133 self.file
134 .ensure_record_has_been_read_into_buffer(self.record_index)?;
135
136 let field_info = &self.file.fields_info[self.field_index.0];
137
138 let start_pos = self.position_in_record();
139 let field_bytes = &mut self.file.record_data_buffer.get_mut()
140 [start_pos..start_pos + field_info.field_length as usize];
141
142 FieldValue::read_from(
143 field_bytes,
144 &mut self.file.memo_reader,
145 field_info,
146 &self.file.encoding,
147 self.file.options.character_trim,
148 )
149 .map_err(|e| {
150 Error::new(
151 FieldIOError::new(e, Some(field_info.clone())),
152 self.record_index.0,
153 )
154 })
155 }
156
157 pub fn read_as<ValueType>(&mut self) -> Result<ValueType, Error>
159 where
160 ValueType: TryFrom<FieldValue, Error = FieldConversionError>,
161 {
162 let value = self.read()?;
163
164 let converted_value = ValueType::try_from(value).map_err(|e| {
165 let field_info = &self.file.fields_info[self.field_index.0];
166 Error::new(
167 FieldIOError::new(ErrorKind::BadConversion(e), Some(field_info.clone())),
168 self.record_index.0,
169 )
170 })?;
171
172 Ok(converted_value)
173 }
174}
175
176impl<T> FieldRef<'_, T>
177where
178 T: Seek + Write,
179{
180 pub fn write<ValueType>(&mut self, value: &ValueType) -> Result<(), Error>
182 where
183 ValueType: WritableAsDbaseField,
184 {
185 self.file.file_position = self
186 .seek_to_beginning()
187 .map_err(|e| Error::new(e, self.record_index.0))?;
188
189 let field_info = &self.file.fields_info[self.field_index.0];
190
191 let start_pos = self.position_in_record();
192 let field_bytes = &mut self.file.record_data_buffer.get_mut()
193 [start_pos..start_pos + field_info.field_length as usize];
194 field_bytes.fill(0);
195
196 let mut cursor = Cursor::new(field_bytes);
200 value
201 .write_as(field_info, &self.file.encoding, &mut cursor)
202 .map_err(|e| {
203 Error::new(
204 FieldIOError::new(e, Some(field_info.clone())),
205 self.record_index.0,
206 )
207 })?;
208
209 let buffer = cursor.into_inner();
210
211 self.file.inner.write_all(buffer).map_err(|e| {
212 Error::new(
213 FieldIOError::new(ErrorKind::IoError(e), Some(field_info.clone())),
214 self.record_index.0,
215 )
216 })?;
217
218 self.file.file_position += buffer.len() as u64;
219
220 Ok(())
221 }
222}
223
224pub struct RecordRef<'a, T> {
232 file: &'a mut File<T>,
233 index: RecordIndex,
234}
235
236impl<T> Debug for RecordRef<'_, T> {
237 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
238 f.debug_struct("RecordRef")
239 .field("index", &self.index)
240 .finish()
241 }
242}
243
244impl<T> RecordRef<'_, T> {
245 pub fn field(&mut self, index: FieldIndex) -> Option<FieldRef<'_, T>> {
246 if index.0 >= self.file.fields_info.len() {
247 return None;
248 }
249 Some(FieldRef {
250 file: self.file,
251 record_index: self.index,
252 field_index: index,
253 })
254 }
255
256 fn position_in_source(&self) -> u64 {
257 self.file.header.record_position(self.index.0).unwrap() as u64
258 }
259}
260
261impl<T> RecordRef<'_, T>
262where
263 T: Seek,
264{
265 pub fn seek_before_deletion_flag(&mut self) -> Result<u64, FieldIOError> {
266 self.file
267 .inner
268 .seek(SeekFrom::Start(self.position_in_source()))
269 .map_err(|e| FieldIOError::new(ErrorKind::IoError(e), None))
270 }
271}
272
273impl<T> RecordRef<'_, T>
274where
275 T: Read + Seek,
276{
277 pub fn is_deleted(&mut self) -> Result<bool, Error> {
282 self.file
283 .ensure_record_has_been_read_into_buffer(self.index)?;
284 let deletion_flag = DeletionFlag::from_byte(self.file.record_data_buffer.get_ref()[0]);
285
286 Ok(deletion_flag == DeletionFlag::Deleted)
287 }
288
289 pub fn read_field(&mut self, field_index: FieldIndex) -> Result<FieldValue, Error> {
293 let record_index = self.index.0;
294 let mut field = self
295 .field(field_index)
296 .ok_or_else(|| Error::new(FieldIOError::end_of_record(), record_index))?;
297 field.read()
298 }
299
300 pub fn read_field_as<ValueType>(&mut self, field_index: FieldIndex) -> Result<ValueType, Error>
304 where
305 ValueType: TryFrom<FieldValue, Error = FieldConversionError>,
306 {
307 let record_index = self.index.0;
308 let mut field = self
309 .field(field_index)
310 .ok_or_else(|| Error::new(FieldIOError::end_of_record(), record_index))?;
311 field.read_as()
312 }
313
314 pub fn read(&mut self) -> Result<crate::Record, Error> {
316 self.read_as()
317 }
318
319 pub fn read_as<R>(&mut self) -> Result<R, Error>
321 where
322 R: ReadableRecord,
323 {
324 self.file
325 .ensure_record_has_been_read_into_buffer(self.index)?;
326 self.file
327 .record_data_buffer
328 .set_position(DELETION_FLAG_SIZE as u64);
329 let mut field_iterator = FieldIterator {
330 source: &mut self.file.record_data_buffer,
331 fields_info: self.file.fields_info.iter().peekable(),
332 memo_reader: &mut self.file.memo_reader,
333 field_data_buffer: &mut self.file.field_data_buffer,
334 encoding: &self.file.encoding,
335 options: self.file.options,
336 };
337
338 R::read_using(&mut field_iterator).map_err(|error| Error::new(error, self.index.0))
339 }
340}
341
342impl<T> RecordRef<'_, T>
343where
344 T: Write + Seek,
345{
346 pub fn write_field<ValueType>(
350 &mut self,
351 field_index: FieldIndex,
352 value: &ValueType,
353 ) -> Result<(), Error>
354 where
355 ValueType: WritableAsDbaseField,
356 {
357 let record_index = self.index.0;
358 let mut field = self
359 .field(field_index)
360 .ok_or_else(|| Error::new(FieldIOError::end_of_record(), record_index))?;
361 field.write(value)
362 }
363
364 pub fn write<R>(&mut self, record: &R) -> Result<(), Error>
367 where
368 R: WritableRecord,
369 {
370 self.file.record_data_buffer.get_mut().fill(0);
371 self.file.record_data_buffer.get_mut()[0] = DeletionFlag::NotDeleted.to_byte();
372 self.file.record_data_buffer.set_position(1);
373
374 let mut field_writer = FieldWriter {
375 dst: &mut self.file.record_data_buffer,
376 fields_info: self.file.fields_info.iter().peekable(),
377 field_buffer: &mut Cursor::new(&mut self.file.field_data_buffer),
378 encoding: &self.file.encoding,
379 };
380
381 record
382 .write_using(&mut field_writer)
383 .map_err(|error| Error::new(error, self.index.0))?;
384
385 self.seek_before_deletion_flag()
386 .map_err(|error| Error::new(error, self.index.0))?;
387
388 self.file
389 .inner
390 .write_all(self.file.record_data_buffer.get_ref())
391 .map_err(|error| Error::io_error(error, self.index.0))?;
392
393 debug_assert_eq!(
395 self.file.file_position,
396 self.file.inner.stream_position().unwrap()
397 );
398
399 Ok(())
400 }
401}
402
403pub struct FileRecordIterator<'a, T> {
405 file: &'a mut File<T>,
406 current_record: RecordIndex,
407}
408
409impl<T> FileRecordIterator<'_, T>
410where
411 T: Seek + Read,
412{
413 pub fn next(&mut self) -> Option<RecordRef<'_, T>> {
416 let record_ref = self.file.record(self.current_record.0);
417 self.current_record.0 += usize::from(record_ref.is_some());
418 record_ref
419 }
420}
421
422#[derive(Debug)]
462pub struct File<T> {
463 pub(crate) inner: T,
464 memo_reader: Option<MemoReader<T>>,
465 pub(crate) header: Header,
466 pub(crate) fields_info: FieldsInfo,
467 pub(crate) encoding: DynEncoding,
468 record_data_buffer: Cursor<Vec<u8>>,
471 field_data_buffer: [u8; 255],
474 pub(crate) options: ReadingOptions,
475 file_position: u64,
479}
480
481impl<T> File<T> {
482 pub fn fields(&self) -> &[FieldInfo] {
484 self.fields_info.as_ref()
485 }
486
487 pub fn field_index(&self, name: &str) -> Option<FieldIndex> {
489 self.fields_info
490 .iter()
491 .position(|info| info.name.eq_ignore_ascii_case(name))
492 .map(FieldIndex)
493 }
494
495 pub fn num_records(&self) -> usize {
497 self.header.num_records as usize
498 }
499
500 pub fn set_options(&mut self, options: ReadingOptions) {
501 self.options = options;
502 }
503}
504
505impl<T: Read + Seek> File<T> {
506 pub fn open(mut source: T) -> Result<Self, Error> {
508 let mut header =
509 Header::read_from(&mut source).map_err(|error| Error::io_error(error, 0))?;
510
511 let offset = if header.file_type.is_visual_fox_pro() {
512 if BACKLINK_SIZE > header.offset_to_first_record {
513 panic!("Invalid file");
514 }
515 header.offset_to_first_record - BACKLINK_SIZE
516 } else {
517 header.offset_to_first_record
518 };
519 let num_fields = (offset as usize - Header::SIZE - size_of::<u8>()) / FieldInfo::SIZE;
520
521 let encoding = header.code_page_mark.to_encoding().ok_or_else(|| {
522 let field_error = FieldIOError::new(UnsupportedCodePage(header.code_page_mark), None);
523 Error::new(field_error, 0)
524 })?;
525
526 let fields_info =
527 FieldsInfo::read_from(&mut source, num_fields, &encoding).map_err(|error| Error {
528 record_num: 0,
529 field: None,
530 kind: error,
531 })?;
532
533 let terminator = source
534 .read_u8()
535 .map_err(|error| Error::io_error(error, 0))?;
536
537 debug_assert_eq!(terminator, TERMINATOR_VALUE);
538
539 source
540 .seek(SeekFrom::Start(u64::from(header.offset_to_first_record)))
541 .map_err(|error| Error::io_error(error, 0))?;
542
543 let record_size: usize = DELETION_FLAG_SIZE + fields_info.size_of_all_fields();
544 let record_data_buffer = Cursor::new(vec![0u8; record_size]);
545 header.size_of_record = record_size as u16;
548 Ok(Self {
551 inner: source,
552 memo_reader: None,
553 header,
554 fields_info,
555 encoding,
556 record_data_buffer,
557 field_data_buffer: [0u8; 255],
558 options: ReadingOptions::default(),
559 file_position: header.offset_to_first_record as u64,
560 })
561 }
562
563 pub fn record(&mut self, index: usize) -> Option<RecordRef<'_, T>> {
567 if index >= self.header.num_records as usize {
568 None
569 } else {
570 let record_ref = RecordRef {
571 file: self,
572 index: RecordIndex(index),
573 };
574 Some(record_ref)
575 }
576 }
577
578 pub fn records(&mut self) -> FileRecordIterator<'_, T> {
582 FileRecordIterator {
583 file: self,
584 current_record: RecordIndex(0),
585 }
586 }
587
588 fn ensure_record_has_been_read_into_buffer(
590 &mut self,
591 record_index: RecordIndex,
592 ) -> Result<bool, Error> {
593 let record_ref = RecordRef {
594 file: self,
595 index: record_index,
596 };
597 let start_of_record_pos = record_ref.position_in_source();
598 let end_of_record_pos = start_of_record_pos + u64::from(self.header.size_of_record);
599
600 if self.file_position > start_of_record_pos && self.file_position <= end_of_record_pos {
601 return Ok(false);
603 }
604
605 if start_of_record_pos != self.file_position {
606 self.file_position = self
609 .inner
610 .seek(SeekFrom::Start(start_of_record_pos))
611 .map_err(|e| Error::io_error(e, record_index.0))?;
612 }
613
614 self.inner
615 .read_exact(self.record_data_buffer.get_mut())
616 .map_err(|e| Error::io_error(e, record_index.0))?;
617 self.file_position += self.record_data_buffer.get_mut().len() as u64;
618 Ok(true)
619 }
620}
621
622impl<T: Write + Seek> File<T> {
623 pub fn create_new(mut dst: T, table_info: TableInfo) -> Result<Self, Error> {
624 write_header_parts(&mut dst, &table_info.header, &table_info.fields_info)?;
625 let record_size: usize = DELETION_FLAG_SIZE
626 + table_info
627 .fields_info
628 .iter()
629 .map(|i| i.field_length as usize)
630 .sum::<usize>();
631 let record_data_buffer = Cursor::new(vec![0u8; record_size]);
632 let file_position = table_info.header.offset_to_first_record as u64;
633 debug_assert_eq!(file_position, dst.stream_position().unwrap());
634 Ok(Self {
635 inner: dst,
636 memo_reader: None,
637 header: table_info.header,
638 fields_info: FieldsInfo {
639 inner: table_info.fields_info,
640 },
641 encoding: table_info.encoding,
642 record_data_buffer,
643 field_data_buffer: [0u8; 255],
644 options: ReadingOptions::default(),
645 file_position,
646 })
647 }
648
649 pub fn append_record<R>(&mut self, record: &R) -> Result<(), Error>
650 where
651 R: WritableRecord,
652 {
653 self.append_records(std::slice::from_ref(record))
654 }
655
656 pub fn append_records<R>(&mut self, records: &[R]) -> Result<(), Error>
657 where
658 R: WritableRecord,
659 {
660 assert!(
661 !self
662 .header
663 .num_records
664 .overflowing_add(records.len() as u32)
665 .1,
666 "Too many records (u32 overflow)"
667 );
668
669 let end_of_last_record = self.header.offset_to_first_record as u64
670 + (self.num_records() as u64 * self.header.size_of_record as u64);
671
672 self.inner
673 .seek(SeekFrom::Start(end_of_last_record))
674 .map_err(|error| Error::io_error(error, self.num_records()))?;
675
676 for record in records {
677 let current_record_index = self.header.num_records + 1;
678
679 let mut field_writer = FieldWriter {
680 dst: &mut self.inner,
681 fields_info: self.fields_info.iter().peekable(),
682 field_buffer: &mut Cursor::new(&mut self.field_data_buffer),
683 encoding: &self.encoding,
684 };
685
686 field_writer
687 .write_deletion_flag()
688 .map_err(|error| Error::io_error(error, current_record_index as usize))?;
689
690 record
691 .write_using(&mut field_writer)
692 .map_err(|error| Error::new(error, current_record_index as usize))?;
693
694 self.header.num_records = current_record_index;
695 }
696
697 self.sync_all()
698 .map_err(|error| Error::io_error(error, self.num_records()))?;
699
700 Ok(())
701 }
702
703 pub fn sync_all(&mut self) -> std::io::Result<()> {
704 let current_pos = self.inner.stream_position()?;
705 self.inner.seek(SeekFrom::Start(0))?;
706 self.header.write_to(&mut self.inner)?;
707 self.inner.seek(SeekFrom::Start(current_pos))?;
708 Ok(())
709 }
710}
711
712impl File<BufReadWriteFile> {
713 pub fn open_with_options<P: AsRef<Path>>(
714 path: P,
715 options: std::fs::OpenOptions,
716 ) -> Result<Self, Error> {
717 let file = options
718 .open(path)
719 .map_err(|error| Error::io_error(error, 0))?;
720 let source = BufReadWriteFile::new(file).unwrap();
721 File::open(source)
722 }
723
724 pub fn open_read_only<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
726 let file = std::fs::File::open(path.as_ref()).map_err(|error| Error::io_error(error, 0))?;
727
728 let mut file = File::open(BufReadWriteFile::new(file).unwrap())?;
729 if file.fields_info.at_least_one_field_is_memo() {
730 let p = path.as_ref();
731 let memo_type = file.header.file_type.supported_memo_type();
732 if let Some(mt) = memo_type {
733 let memo_path = p.with_extension(mt.extension());
734
735 let memo_file = std::fs::File::open(memo_path).map_err(|error| Error {
736 record_num: 0,
737 field: None,
738 kind: ErrorKind::ErrorOpeningMemoFile(error),
739 })?;
740
741 let memo_reader = BufReadWriteFile::new(memo_file)
742 .and_then(|memo_file| MemoReader::new(mt, memo_file))
743 .map_err(|error| Error::io_error(error, 0))?;
744
745 file.memo_reader = Some(memo_reader);
746 }
747 }
748 Ok(file)
749 }
750
751 pub fn open_write_only<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
753 let mut options = std::fs::OpenOptions::new();
754 options
755 .read(false)
756 .write(true)
757 .create(false)
758 .truncate(false);
759
760 File::open_with_options(path, options)
761 }
762
763 pub fn open_read_write<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
765 let mut options = std::fs::OpenOptions::new();
766 options.read(true).write(true).create(false).truncate(false);
767
768 File::open_with_options(path, options)
769 }
770
771 pub fn create<P: AsRef<Path>>(path: P, table_info: TableInfo) -> Result<Self, Error> {
773 let file = std::fs::File::create(path).map_err(|error| Error::io_error(error, 0))?;
774
775 File::create_new(BufReadWriteFile::new(file).unwrap(), table_info)
776 }
777}
778
779#[cfg(test)]
780mod tests {
781 #[test]
782 fn ensure_record_has_been_read_into_buffer() {
783 let mut file = crate::File::open_read_only("tests/data/stations.dbf").unwrap();
784
785 {
786 let mut record = file.record(0).unwrap();
787 let _ = record.read_field(crate::FieldIndex(0)).unwrap();
788 assert!(!file
790 .ensure_record_has_been_read_into_buffer(crate::RecordIndex(0))
791 .unwrap());
792 assert!(file
793 .ensure_record_has_been_read_into_buffer(crate::RecordIndex(1))
794 .unwrap());
795 }
796
797 {
798 let mut record = file.record(4).unwrap();
799 let _ = record.read_field(crate::FieldIndex(3)).unwrap();
800 assert!(!file
802 .ensure_record_has_been_read_into_buffer(crate::RecordIndex(4))
803 .unwrap());
804 assert!(file
805 .ensure_record_has_been_read_into_buffer(crate::RecordIndex(1))
806 .unwrap());
807 }
808
809 {
811 let mut record = file.record(10).unwrap();
812 let value = record.read_field(crate::FieldIndex(2)).unwrap();
813 assert!(!record
815 .file
816 .ensure_record_has_been_read_into_buffer(crate::RecordIndex(10))
817 .unwrap());
818 record.write_field(crate::FieldIndex(2), &value).unwrap();
819 assert!(!file
820 .ensure_record_has_been_read_into_buffer(crate::RecordIndex(10))
821 .unwrap());
822 }
823 }
824}