dbase/
lib.rs

1//! dbase is rust library meant to read and write dBase / FoxPro files.
2//!
3//! Theses files are nowadays generally found in association with shapefiles.
4//!
5//! # Reading
6//!
7//! The [Reader](struct.Reader.html) is the struct that you'll need to use in order
8//! to read the content of a dBase file.
9//!
10//! Once you have access to the records, you will have to `match` against the real
11//! [FieldValue](enum.FieldValue.html)
12//!
13//! ## Examples
14//!
15//! ```
16//! use dbase::FieldValue;
17//! # fn main() -> Result<(), dbase::Error> {
18//! let records = dbase::read("tests/data/line.dbf")?;
19//! for record in records {
20//!     for (name, value) in record {
21//!         println!("{} -> {:?}", name, value);
22//!         match value {
23//!             FieldValue::Character(Some(string)) => println!("Got string: {}", string),
24//!             FieldValue::Numeric(value) => println!("Got numeric value of  {:?}", value),
25//!             _ => {}
26//!         }
27//!     }
28//!}
29//! # Ok(())
30//! # }
31//! ```
32//!
33//! You can also create a [Reader](reading/struct.Reader.html) and iterate over the records.
34//!
35//! ```
36//! # fn main() -> Result<(), dbase::Error> {
37//! let mut reader = dbase::Reader::from_path("tests/data/line.dbf")?;
38//! for record_result in reader.iter_records() {
39//!     let record = record_result?;
40//!     for (name, value) in record {
41//!         println!("name: {}, value: {:?}", name, value);
42//!     }
43//! }
44//! # Ok(())
45//! # }
46//! ```
47//!
48//! ## Deserialisation
49//!
50//! If you know what kind of data to expect from a particular file you can use implement
51//! the [ReadbableRecord](trait.ReadableRecord.html) trait to "deserialize" the record into
52//! your custom struct:
53//!
54//!```
55//! use std::io::{Read, Seek};
56//! use dbase::Encoding;
57//!
58//! struct StationRecord {
59//!     name: String,
60//!     marker_col: String,
61//!     marker_sym: String,
62//!     line: String,
63//! }
64//!
65//! impl dbase::ReadableRecord for StationRecord {
66//!     fn read_using<R1, R2>(field_iterator: &mut dbase::FieldIterator<R1, R2>) -> Result<Self, dbase::FieldIOError>
67//!          where R1: Read + Seek,
68//!                R2: Read + Seek,
69//!    {
70//!        use dbase::Encoding;
71//!        Ok(Self {
72//!            name: field_iterator.read_next_field_as()?.value,
73//!            marker_col: field_iterator.read_next_field_as()?.value,
74//!            marker_sym: field_iterator.read_next_field_as()?.value,
75//!            line: field_iterator.read_next_field_as()?.value,
76//!        })
77//!     }
78//! }
79//! # fn main() -> Result<(), dbase::Error> {
80//! let mut reader = dbase::Reader::from_path("tests/data/stations.dbf")?;
81//! let stations = reader.read_as::<StationRecord>()?;
82//!
83//! assert_eq!(stations[0].name, "Van Dorn Street");
84//! assert_eq!(stations[0].marker_col, "#0000ff");
85//! assert_eq!(stations[0].marker_sym, "rail-metro");
86//! assert_eq!(stations[0].line, "blue");
87//! # Ok(())
88//! # }
89//! ```
90//!
91//! If you use the `serde` optional feature and serde_derive crate you can have the
92//! [ReadbableRecord](trait.ReadableRecord.html) impletemented for you
93//!
94//! ```
95//! # #[cfg(feature = "serde")]
96//! extern crate serde_derive;
97//!
98//! # #[cfg(feature = "serde")]
99//! # fn main() -> Result<(), dbase::Error>{
100//!
101//! use std::io::{Read, Seek};
102//! use serde_derive::Deserialize;
103//!
104//! #[derive(Deserialize)]
105//! struct StationRecord {
106//!     name: String,
107//!     marker_col: String,
108//!     marker_sym: String,
109//!     line: String,
110//! }
111//!
112//! let mut reader = dbase::Reader::from_path("tests/data/stations.dbf")?;
113//! let stations = reader.read_as::<StationRecord>()?;
114//!
115//! assert_eq!(stations[0].name, "Van Dorn Street");
116//! assert_eq!(stations[0].marker_col, "#0000ff");
117//! assert_eq!(stations[0].marker_sym, "rail-metro");
118//! assert_eq!(stations[0].line, "blue");
119//! # Ok(())
120//! # }
121//!
122//! # #[cfg(not(feature = "serde"))]
123//! # fn main() {
124//! # }
125//! ```
126//!
127//! ## Other Codepages / Encodings
128//!
129//! Support for encodings other than Unicode is provided via the crate [`yore`].
130//! This crate only supports some basic single-byte codepages, but many of those
131//! were used by older systems, which dBase databases you may want to open might use.
132//!
133//! ```
134//! # #[cfg(feature = "yore")]
135//! use yore::code_pages::CP850;
136//!
137//! # #[cfg(feature = "yore")]
138//! # fn main() -> Result<(), dbase::Error> {
139//! let mut reader = dbase::Reader::from_path_with_encoding("tests/data/cp850.dbf", CP850)?;
140//! let records = reader.read()?;
141//!
142//! assert_eq!(records[0].get("TEXT"), Some(&dbase::FieldValue::Character(Some("Äöü!§$%&/".to_string()))));
143//!
144//! # Ok(())
145//! # }
146//!
147//! # #[cfg(not(feature = "yore"))]
148//! # fn main() {
149//! # }
150//! ```
151//!
152//! The functions that do not take an encoding as parameter, use [`UnicodeLossy`] by default,
153//! they try to read all data as Unicode and replace unrepresentable characters with the unicode
154//! replacement character. Alternatively [`Unicode`] is available, to return an [`Err`] when data
155//! can't be represented as Unicode.
156//!
157//! # Writing
158//!
159//! In order to get a [TableWriter](struct.TableWriter.html) you will need to build it using
160//! its [TableWriterBuilder](struct.TableWriterBuilder.html) to specify the fields that constitute
161//! a record.
162//!
163//! As for reading, you can *serialize* structs into a dBase file, given that they match the
164//! declared fields in when building the TableWriterBuilder by implementing the
165//! [WritableRecord](trait.WritableRecord.html).
166//!
167//! ## Examples
168//!
169//! ```
170//! # fn main() -> Result<(), dbase::Error> {
171//! let mut reader = dbase::Reader::from_path("tests/data/stations.dbf")?;
172//! let mut stations = reader.read()?;
173//!
174//! let mut writer = dbase::TableWriterBuilder::from_reader(reader)
175//!     .build_with_file_dest("stations.dbf").unwrap();
176//!
177//! stations[0].get_mut("line").and_then(|_old| Some("Red".to_string()));
178//! writer.write_records(&stations)?;
179//! # Ok(())
180//! # }
181//! ```
182//!
183//!```
184//! use dbase::{TableWriterBuilder, FieldName, WritableRecord, FieldWriter, FieldIOError, Encoding};
185//! use std::convert::TryFrom;
186//! use std::io::{Cursor, Write};
187//!
188//! struct User {
189//!     nick_name: String,
190//!     age: f64
191//! }
192//!
193//! impl WritableRecord for User {
194//!     fn write_using<'a, W>(&self, field_writer: &mut FieldWriter<'a, W>) -> Result<(), FieldIOError>
195//!         where W: Write
196//!     {
197//!         field_writer.write_next_field_value(&self.nick_name)?;
198//!         field_writer.write_next_field_value(&self.age)?;
199//!         Ok(())
200//!     }
201//! }
202//!
203//! let mut writer = TableWriterBuilder::new()
204//!     .add_character_field(FieldName::try_from("Nick Name").unwrap(), 50)
205//!     .add_numeric_field(FieldName::try_from("Age").unwrap(), 20, 10)
206//!     .build_with_dest(Cursor::new(Vec::<u8>::new()));
207//!
208//!
209//! let records = User{
210//!     nick_name: "Yoshi".to_string(),
211//!     age: 32.0,
212//! };
213//!
214//! writer.write_record(&records).unwrap();
215//! ```
216//!
217//! If you use the serde optional feature and serde_derive crate you can have the
218//! [WritableRecord](trait.WritableRecord.html) implemented for you.
219//!
220//! ```
221//! # #[cfg(feature = "serde")]
222//! extern crate serde_derive;
223//!
224//! # #[cfg(feature = "serde")]
225//! use serde_derive::Serialize;
226//!
227//! use dbase::{TableWriterBuilder, FieldName, WritableRecord, FieldWriter};
228//! use std::convert::TryFrom;
229//! use std::io::{Cursor, Write};
230//!
231//! # #[cfg(feature = "serde")]
232//! # fn main () {
233//! #[derive(Serialize)]
234//! struct User {
235//!     nick_name: String,
236//!     age: f64
237//! }
238//!
239//! let writer = TableWriterBuilder::new()
240//!     .add_character_field(FieldName::try_from("Nick Name").unwrap(), 50)
241//!     .add_numeric_field(FieldName::try_from("Age").unwrap(), 20, 10)
242//!     .build_with_dest(Cursor::new(Vec::<u8>::new()));
243//!
244//!
245//! let records = vec![User{
246//!     nick_name: "Yoshi".to_string(),
247//!     age: 32.0,
248//! }];
249//!
250//!     writer.write_records(&records);
251//! # }
252//! # #[cfg(not(feature = "serde"))]
253//! # fn main() {}
254//! ```
255//!
256//! # File
257//!
258//! This crate also has a third option to handle dbase files, the [File]
259//! struct.
260//!
261//! This struct allows to read/write an existing or new file
262//! without having to fully read it first.
263#![deny(unstable_features)]
264
265extern crate byteorder;
266#[cfg(feature = "serde")]
267extern crate serde;
268extern crate time;
269
270#[cfg(feature = "serde")]
271mod de;
272#[cfg(feature = "serde")]
273mod ser;
274
275#[cfg(feature = "yore")]
276pub use yore;
277
278#[cfg(feature = "datafusion")]
279mod datafusion;
280
281pub mod encoding;
282mod error;
283mod field;
284mod file;
285mod header;
286mod memo;
287mod reading;
288mod record;
289mod writing;
290
291pub use file::{FieldIndex, FieldRef, File, RecordIndex, RecordRef};
292
293#[cfg(feature = "datafusion")]
294pub use crate::datafusion::{DbaseTable, DbaseTableFactory};
295pub use crate::encoding::{Encoding, Unicode, UnicodeLossy};
296pub use crate::error::{Error, ErrorKind, FieldIOError};
297pub use crate::field::types::{Date, DateTime, FieldType, FieldValue, Time, TrimOption};
298pub use crate::field::{FieldConversionError, FieldInfo, FieldName};
299pub use crate::header::CodePageMark;
300pub use crate::reading::{
301    read, FieldIterator, NamedValue, ReadableRecord, Reader, ReaderBuilder, ReadingOptions,
302    RecordIterator, TableInfo,
303};
304pub use crate::record::Record;
305pub use crate::writing::{FieldWriter, TableWriter, TableWriterBuilder, WritableRecord};
306
307/// macro to define a struct that implements the ReadableRecord and WritableRecord
308///
309/// # Examples
310///
311/// ```
312/// # #[macro_use] extern crate dbase;
313/// # fn main() {
314/// dbase_record!(
315///     #[derive(Debug)]
316///     struct UserRecord {
317///         first_name: String,
318///         last_name: String,
319///         age: f64
320///     }
321/// );
322/// # }
323/// ```
324#[macro_export]
325macro_rules! dbase_record {
326    (
327        $(#[derive($($derives:meta),*)])?
328        $vis:vis struct $name:ident {
329            $( $visf:vis $field_name:ident: $field_type:ty),+
330            $(,)?
331        }
332    ) => {
333
334        $(#[derive($($derives),*)])?
335        $vis struct $name {
336            $($visf $field_name: $field_type),+
337        }
338
339        impl dbase::ReadableRecord for $name {
340            fn read_using<Source, MemoSource>(field_iterator: &mut dbase::FieldIterator<Source, MemoSource>) -> Result<Self, dbase::FieldIOError>
341                where Source: std::io::Read + std::io::Seek,
342                      MemoSource: std::io::Read + std::io::Seek
343                {
344                    Ok(Self {
345                        $(
346                            $field_name: field_iterator
347                                .read_next_field_as::<$field_type>()?
348                                .value
349                        ),+
350                    })
351            }
352        }
353
354       impl dbase::WritableRecord for $name {
355           fn write_using<'a, W>(&self, field_writer: &mut dbase::FieldWriter<'a, W>) -> Result<(), dbase::FieldIOError>
356           where W: std::io::Write,
357           {
358                $(
359                    field_writer.write_next_field_value(&self.$field_name)?;
360                )+
361                Ok(())
362           }
363        }
364    };
365}