From 2906d758ebfc18ac7b59d7c8bf548aeb86f096a2 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Wed, 28 Aug 2024 22:42:22 -0400 Subject: [PATCH 01/12] changelog and license --- CHANGELOG.md | 3 +++ LICENSE.txt | 18 ++++++++++++++++++ README.md | 9 +++++---- 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 LICENSE.txt diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..790eba7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## Development releases + +- **0.1.0** Initial release (Aug, 2024) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..b84a9e6 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,18 @@ +Copyright (c) 2024 Brian London + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 64f095a..8046710 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,12 @@ What strict should enable ## Wishlist of new features - Fixed column offsets - - Error messages for writing operations - - Strict mode - - Add an option for padding enum variants to all be the same length - - Also support shorter than expected lines gracefully + - Better error messages for writing operations - Make param list data rather than code to support dynamic lists of valid parameters. - Allow a function based custom deserialization on individual columns - Clear error messages of location of error on read errors + +## License + +Licensed under the MIT license. See: [LICENSE.txt]. \ No newline at end of file From 8df1479981d0d34bb6f9d70fd29b2cd77bf19384 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Thu, 29 Aug 2024 08:46:59 -0400 Subject: [PATCH 02/12] minor format --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 790eba7..043d113 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ ## Development releases -- **0.1.0** Initial release (Aug, 2024) +### 0.1.0 (Aug, 2024) +- Initial release From 3be0b1a523b7ba28a0dcb4b3dcf63fa074d32d26 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Thu, 29 Aug 2024 20:58:02 -0400 Subject: [PATCH 03/12] crate level docs --- fixcol-derive/src/enums.rs | 2 +- src/lib.rs | 171 +++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 1 deletion(-) diff --git a/fixcol-derive/src/enums.rs b/fixcol-derive/src/enums.rs index aab2dc5..fe09fdf 100644 --- a/fixcol-derive/src/enums.rs +++ b/fixcol-derive/src/enums.rs @@ -93,7 +93,7 @@ fn read_embedded_variant(name: &Ident, fields: &FieldsUnnamed) -> MacroResult { if let Some(fa) = fixcol_attrs(&field.attrs).first() { return Err(MacroError::new( "Did not expect fixcol attribute on embedded enum variant", - fa.meta.span(), + fa.meta.path().span(), )); } diff --git a/src/lib.rs b/src/lib.rs index d2d7e8f..5bd5fb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,177 @@ //! delimeters such as CSV or JSON are more common today, fixed column file formats //! are more naturally human readable and many older data sets, especially public //! domain data sets continue to use them. +//! +//! The library is built around the [`ReadFixed`] trait which is ordinarily +//! derived on a data type (`struct` or `enum`) that represents a row of the data +//! file. The `fixcol` attribute is used to define how fields map to the schema. +//! +//! For writing data files rudimentary serialization is provided by [`WriteFixed`] +//! and [`WriteFixedAll`] behind the `experimental-write` feature flag. +//! +//! ## Examples +//! ### Basic Example +//! +//! Consider the following data file: +//! +//! ```text +//! Tokyo 13515271 35.689 139.692 +//! Delhi 16753235 28.610 77.230 +//! Shanghai 24870895 31.229 121.475 +//! São Paulo 12252023 -23.550 -46.333 +//! Mexico City 9209944 19.433 -99.133 +//! ``` +//! +//! We can create a basic data structure coresponding to the records in the file +//! and then read the data file as shown. +//! +//! ``` +//! use fixcol::ReadFixed; +//! # use std::fs::File; +//! +//! #[derive(ReadFixed)] +//! # #[derive(Debug, PartialEq)] +//! struct City { +//! #[fixcol(width = 12)] +//! name: String, +//! #[fixcol(width = 8, align = "right")] +//! population: u64, +//! #[fixcol(skip = 1, width = 8, align = "right")] +//! lat: f32, +//! #[fixcol(skip = 1, width = 8, align = "right")] +//! lon: f32, +//! } +//! +//! # // TODO: Unicode support :/ make São Paulo work +//! # fn f() { +//! let mut file = File::open("cities.txt"); +//! # } +//! # let mut file = "Tokyo 13515271 35.689 139.692 +//! # Delhi 16753235 28.610 77.230 +//! # Shanghai 24870895 31.229 121.475 +//! # Sao Paulo 12252023 -23.550 -46.333 +//! # Mexico City 9209944 19.433 -99.133".as_bytes(); +//! let cities: Vec = City::read_fixed_all(file).map(|res| match res { +//! Ok(city) => city, +//! Err(err) => { +//! eprintln!("{}", err); +//! std::process::exit(1); +//! } +//! }).collect(); +//! +//! assert_eq!(cities, vec![ +//! City { name: "Tokyo".into(), population: 13515271, lat: 35.689, lon: 139.692 }, +//! City { name: "Delhi".into(), population: 16753235, lat: 28.610, lon: 77.230 }, +//! City { name: "Shanghai".into(), population: 24870895, lat: 31.229, lon: 121.475 }, +//! City { name: "Sao Paulo".into(), population: 12252023, lat: -23.550, lon: -46.333 }, +//! City { name: "Mexico City".into(), population: 9209944, lat: 19.433, lon: -99.133 }, +//! ]); +//! ``` +//! +//! ### Multiple Record Types +//! +//! Many data files contain lines corresponding to multiple types of records. +//! Typically the record type is indicated by the first few columns of the line. +//! In Fixcol we call this the *key* of the record. Multiple record types can be +//! decoded using an `enum` with a key annotation. +//! +//! Consider a directed graph with named nodes defined in a data file like the +//! follinwg. +//! +//! ```text +//! NODE 001 Item A +//! NODE 002 Item B +//! EDGE 001 002 +//! ``` +//! +//! This file can be parsed with an enum like the follwing. +//! +//! ``` +//! use fixcol::ReadFixed; +//! +//! # #[derive(PartialEq, Debug)] +//! #[derive(ReadFixed)] +//! #[fixcol(key_width = 4)] +//! enum GraphItem { +//! #[fixcol(key = "NODE")] +//! Node { +//! #[fixcol(skip = 1, width = 3)] +//! id: u8, +//! #[fixcol(skip = 1, width = 6)] +//! name: String +//! }, +//! #[fixcol(key = "EDGE")] +//! Edge { +//! #[fixcol(skip = 1, width = 3)] +//! from_id: u8, +//! #[fixcol(skip = 1, width = 3)] +//! to_id: u8, +//! } +//! } +//! # let mut buf = "NODE 001 Item A +//! # NODE 002 Item B +//! # EDGE 001 002".as_bytes(); +//! # let graph: Vec = GraphItem::read_fixed_all(buf) +//! # .map(|r| r.unwrap()) +//! # .collect(); +//! # assert_eq!(graph, vec![ +//! # GraphItem::Node { id: 1, name: "Item A".to_string() }, +//! # GraphItem::Node { id: 2, name: "Item B".to_string() }, +//! # GraphItem::Edge { from_id: 1, to_id: 2 }, +//! # ]); +//! ``` +//! +//! ### Embedded Variants +//! +//! Often instead of having fields defined directly on an `enum` variant it is +//! convenient to *embed* a struct within a single parameter named tuple +//! variant. Fixcol supports this pattern. To use it, derive [`ReadFixed`] on +//! the inner type and use the `embed` parameter on the variant. +//! +//! ``` +//! # use fixcol::ReadFixed; +//! +//! # #[derive(PartialEq, Debug)] +//! #[derive(ReadFixed)] +//! struct Node { +//! #[fixcol(skip = 1, width = 3)] +//! id: u8, +//! #[fixcol(skip = 1, width = 6)] +//! name: String +//! } +//! +//! # #[derive(PartialEq, Debug)] +//! #[derive(ReadFixed)] +//! struct Edge { +//! #[fixcol(skip = 1, width = 3)] +//! from_id: u8, +//! #[fixcol(skip = 1, width = 3)] +//! to_id: u8, +//! } +//! +//! # #[derive(PartialEq, Debug)] +//! #[derive(ReadFixed)] +//! #[fixcol(key_width = 4)] +//! enum GraphItem { +//! #[fixcol(key = "NODE", embed = true)] +//! Node(Node), +//! #[fixcol(key = "EDGE", embed = true)] +//! Edge(Edge), +//! } +//! # let mut buf = "NODE 001 Item A +//! # NODE 002 Item B +//! # EDGE 001 002".as_bytes(); +//! # let graph: Vec = GraphItem::read_fixed_all(buf) +//! # .map(|r| r.unwrap()) +//! # .collect(); +//! # assert_eq!(graph, vec![ +//! # GraphItem::Node(Node { id: 1, name: "Item A".to_string() }), +//! # GraphItem::Node(Node { id: 2, name: "Item B".to_string() }), +//! # GraphItem::Edge(Edge { from_id: 1, to_id: 2 }), +//! # ]); +//! ``` +//! +//! ## Schema Definition Parameters #![feature(doc_auto_cfg)] pub mod error; From 0855e2ba8f891686d17bc682f0ec4f54341df207 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Thu, 29 Aug 2024 21:07:48 -0400 Subject: [PATCH 04/12] fix a write test --- fixcol-derive/src/enums.rs | 2 +- tests/ui/read-write/embed_extra_attr.rs | 3 ++- tests/ui/read-write/embed_extra_attr.stderr | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fixcol-derive/src/enums.rs b/fixcol-derive/src/enums.rs index fe09fdf..2e1e07b 100644 --- a/fixcol-derive/src/enums.rs +++ b/fixcol-derive/src/enums.rs @@ -247,7 +247,7 @@ fn write_embedded_variant(ident: &Ident, config: &VariantConfig, fields: &Fields if let Some(fa) = fixcol_attrs(&field.attrs).first() { return Err(MacroError::new( "Did not expect fixcol attribute on embedded enum variant", - fa.meta.span(), + fa.meta.path().span(), )); } diff --git a/tests/ui/read-write/embed_extra_attr.rs b/tests/ui/read-write/embed_extra_attr.rs index cc432a9..00090ed 100644 --- a/tests/ui/read-write/embed_extra_attr.rs +++ b/tests/ui/read-write/embed_extra_attr.rs @@ -1,5 +1,6 @@ use fixcol::{ReadFixed, WriteFixed}; -#[derive(ReadFixed)] + +#[derive(ReadFixed, WriteFixed)] struct Point { #[fixcol(width = 5)] point_x: u16, diff --git a/tests/ui/read-write/embed_extra_attr.stderr b/tests/ui/read-write/embed_extra_attr.stderr index ececf5e..00b7101 100644 --- a/tests/ui/read-write/embed_extra_attr.stderr +++ b/tests/ui/read-write/embed_extra_attr.stderr @@ -1,5 +1,5 @@ error: fixcol-derive error: Did not expect fixcol attribute on embedded enum variant - --> tests/ui/read-write/embed_extra_attr.rs:16:13 + --> tests/ui/read-write/embed_extra_attr.rs:17:13 | -16 | Point(#[fixcol(width = 5)] Point), +17 | Point(#[fixcol(width = 5)] Point), | ^^^^^^ From f7004ca7b317952fe7f0c0a11904a1e143d4d779 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Fri, 30 Aug 2024 12:39:23 -0400 Subject: [PATCH 05/12] more crate level docs --- src/lib.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5bd5fb4..1091031 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,7 +134,6 @@ //! //! ``` //! # use fixcol::ReadFixed; -//! //! # #[derive(PartialEq, Debug)] //! #[derive(ReadFixed)] //! struct Node { @@ -175,7 +174,57 @@ //! # ]); //! ``` //! +//! ## Strict Mode +//! +//! Strict mode may be toggled on or off setting the appropriate `fixcol` attribute +//! like `#[fixcol(strict = true)]`. When strict mode is disabled, Fixcol will +//! try it's best to recover encoding errors. When enabled, many more unexpected +//! conditions will be reported as errors. +//! +//! Strict mode is currently enabled by default, but **this may change** in a +//! future version. +//! +//! The `strict` parameter can be applied to a `struct` or `enum`, `enum` variant, +//! or field. The setting will cascade to other levels with the innermost explicit +//! application of the `strict` parameter controlling. +//! +//! #### Example +//! +//! ``` +//! # use fixcol::ReadFixed; +//! #[derive(ReadFixed)] +//! #[fixcol(strict = false)] // Inner elements will not be parsed in strict mode +//! struct Point { +//! #[fixcol(width = 3)] +//! x: u8, // Strict mode not will be applied +//! #[fixcol(width = 3, strict = true)] +//! y: u8, // Strict mode will be applied +//! #[fixcol(width = 3)] +//! z: u8, // Strict mode not will be applied +//! } +//! ``` +//! +//! #### Strict mode effects +//! +//! When a given field is parsed in strict mode the following conditions become +//! errors. +//! - The last field on a line is not whitespace padded to the defined length. +//! - Columns between defined data columns contain non-whitespace characters. +//! - Numeric column defined with `Full` alignment are not zero-padded to the +//! full length. +//! - A `Left` aligned field beginning with whitespace. +//! - A `Right` aligned field ending with whitespace. +//! +//! Additional rules are applied while attempting to write a record. The following +//! are errors in strict mode. +//! - A `Full` aligned `String` field that is not the expected full length. That +//! is, the supplied string must either be naturally the correct length or +//! explicitly whitespace padded to be. +//! - Value supplied for any column that would overflow the allowed space. +//! //! ## Schema Definition Parameters +//! +//! #![feature(doc_auto_cfg)] pub mod error; From efcc38bca3eefbe91d9da853ee0b6088cb80151f Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Fri, 30 Aug 2024 13:44:57 -0400 Subject: [PATCH 06/12] readme and docs --- README.md | 69 +++++++++++++++++++++++++-------- src/lib.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 163 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8046710..b6ffeb7 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,57 @@ A library for reading fixed width / column delimited data files. -## Strict Mode - -We'll start by enabling it only on the field level and then allowing cascades -as a future enhancement. There's currently no parsing of attributes on the -`struct` level, so that also provides an impediment to the cascade behavior. - -What strict should enable - - require last field of line to be full length when reading - - require written `Full` aligned text columns to be the correct length - - require `Left` and `Right` aligned text columns to not overflow - - require unread columns to contain only whitespace - - require no whitespace in numeric `Full` columns - - left aligned fields cannot start with white space - - right aligned fields cannot end with white space - - error on integer width overflow on write +## Basic Usage + +Consider the following data file: +```text +Tokyo 13515271 35.689 139.692 +Delhi 16753235 28.610 77.230 +Shanghai 24870895 31.229 121.475 +São Paulo 12252023 -23.550 -46.333 +Mexico City 9209944 19.433 -99.133 +``` + +We can create a basic data structure corresponding to the records in the file +and then read the data file as shown. + +```rust +use fixcol::ReadFixed; +use std::fs::File; + +#[derive(ReadFixed)] +struct City { + #[fixcol(width = 12)] + name: String, + #[fixcol(width = 8, align = "right")] + population: u64, + #[fixcol(skip = 1, width = 8, align = "right")] + lat: f32, + #[fixcol(skip = 1, width = 8, align = "right")] + lon: f32, +} + +let mut file = File::open("cities.txt"); +let cities: Vec = City::read_fixed_all(file).map(|res| match res { + Ok(city) => city, + Err(err) => { + eprintln!("{}", err); + std::process::exit(1); + } +}).collect(); +``` + +Please see the official documentation for complete usage guidance. + + + ## Wishlist of new features @@ -26,7 +62,8 @@ What strict should enable valid parameters. - Allow a function based custom deserialization on individual columns - Clear error messages of location of error on read errors + - Enable the `ignore_others` parameter ## License -Licensed under the MIT license. See: [LICENSE.txt]. \ No newline at end of file +Licensed under the MIT license. See: [LICENSE.txt]. diff --git a/src/lib.rs b/src/lib.rs index 1091031..cf7a4d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(doc_auto_cfg)] + //! A crate used for *fixed* width *column* serialization and deserialization //! //! Fixcol provides a derive based deserialization framework for parsing text files @@ -26,7 +28,7 @@ //! Mexico City 9209944 19.433 -99.133 //! ``` //! -//! We can create a basic data structure coresponding to the records in the file +//! We can create a basic data structure corresponding to the records in the file //! and then read the data file as shown. //! //! ``` @@ -224,8 +226,114 @@ //! //! ## Schema Definition Parameters //! +//! Fixcol defines serialization and deserialization schemas using `fixcol` +//! annotations that contain a list of one or more parameters in the form +//! `#[fixcol(param1 = value1, param2 = value2, ...)]`. +//! +//! #### Align +//! +//! Indicates the text alignment of the specified field. +//! +//! **Can be applied to**: Field +//! +//! **Allowed Values**: `"left"`, `"right"`, `"full"` +//! +//! | Value | Meaning | +//! |-------|---------| +//! | Left | The value is left aligned and trailing whitespace can be ignored | +//! | Right | The caule is right aligned and leading whitespace can be ignored | +//! | Full | The value is expected to occupy the full defined width. Leading and trailing whitespace are considered significant. | +//! +//! The values of the `align` parameter are mapped to an instance of [`Alignment`] +//! internally. +//! +//! **Default**: Left +//! +//! **Example**: `#[fixcol(width = 6, align = "right")]` +//! +//! #### Embed +//! +//! When decoding a single valued tuple-style enum variant, use the [`ReadFixed`] +//! implementation on the inner type. +//! +//! **Can be applied to**: Enum Variant +//! +//! **Allowed Values**: `true`, `false` +//! +//! **Default**: `false` +//! +//! **Example**: `#[fixcol(embed = true)]` +//! +//! #### Key +//! +//! When decoding multiple record types into an enum, indicates the key that +//! signifies this particular enum variant should be used to decode the line. +//! +//! To encode keys of different lengths, space pad the shorter keys so that all +//! declared keys are explicitly `key_width` characters. +//! +//! **Can be applied to**: Enum Variant +//! +//! **Allowed Values**: Strings of length `key_width` +//! +//! **Default**: Must be set **explicitly**. +//! +//! **Example**: `#[fixcol(key = "EDGE")]` +//! +//! #### Key Width +//! +//! When decoding multiple record types into an enum, indicates how many characters +//! at the begining of each line should be considered the *key* used to identify +//! which record type the line contains and therefore which enum variant should +//! be used to decode the line. +//! +//! **Can be applied to**: Enum +//! +//! **Allowed Values**: Positive integers +//! +//! **Default**: Must be set **explicitly**. +//! +//! **Example**: `#[fixcol(key_width = 4)]` +//! +//! #### Skip +//! +//! Indicates the number of columns (measured in bytes) that are expected to be +//! blank between the prior data field and the current data field. +//! +//! **Can be applied to**: Field +//! +//! **Allowed Values**: Non-negative integers +//! +//! **Default**: Zero +//! +//! **Example**: `#[fixcol(skip = 1, width = 12)]` +//! +//! #### Strict +//! +//! Indicates whether [strict mode](crate#strict-mode) should be enabled (See above). +//! +//! **Can be applied to**: Struct, Enum, Enum Varriant, Field +//! +//! **Allowed Values**: `true`, `false` +//! +//! **Default**: Cascades from outer context, outermost default `true`. +//! +//! **Example**: `#[fixcol(strict = true)]` +//! +//! +//! #### Width +//! +//! Indicates the number of columns (measured in bytes) used to encode the +//! target field. +//! +//! **Can be applied to**: Field +//! +//! **Allowed Values**: Positive integers +//! +//! **Default**: Must be set **explicitly**. +//! +//! **Example**: `#[fixcol(width = 12)]` //! -#![feature(doc_auto_cfg)] pub mod error; mod fixcol; From d8eaa92bdef58f4178e76c91a3f7857db8137f14 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Fri, 30 Aug 2024 14:09:53 -0400 Subject: [PATCH 07/12] cargo metadata --- Cargo.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 4db59ed..8f4b417 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,13 @@ name = "fixcol" version = "0.1.0" edition = "2021" +license = "MIT" +keywords = ["fixed", "column", "serialization", "parse", "file"] +categories = ["encoding", "parsing"] +homepage = "https://github.com/BrianLondon/fixcol" +repository = "https://github.com/BrianLondon/fixcol" +readme = "README.md" +description = "A library for reading and writing fixed width / column delimited data files." [features] experimental-write = [] From e60f9c5eec1cd448121d09e4d4913d48ff7e7ce8 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Fri, 30 Aug 2024 14:13:35 -0400 Subject: [PATCH 08/12] fmt --- examples/habsburgs/alg.rs | 53 ++++++++---- examples/habsburgs/main.rs | 8 +- fixcol-derive/src/attrs.rs | 20 +++-- fixcol-derive/src/enums.rs | 38 ++++++--- fixcol-derive/src/fields.rs | 20 ++--- fixcol-derive/src/lib.rs | 10 ++- fixcol-derive/src/structs.rs | 4 +- src/error.rs | 29 ++++--- src/fixcol.rs | 6 +- src/lib.rs | 160 +++++++++++++++++------------------ src/parse.rs | 36 ++++++-- src/write.rs | 10 +-- tests/examples.rs | 6 +- tests/test_derive_nested.rs | 1 - tests/ui.rs | 1 - 15 files changed, 233 insertions(+), 169 deletions(-) diff --git a/examples/habsburgs/alg.rs b/examples/habsburgs/alg.rs index e38965d..8a448e8 100644 --- a/examples/habsburgs/alg.rs +++ b/examples/habsburgs/alg.rs @@ -1,5 +1,5 @@ //! Genealogy Algorithms -//! +//! //! Does some calculations on genealogy records. The functionality //! here is not important for the serialization example, but is used //! to transform the data from the input format to an output format @@ -34,7 +34,10 @@ pub(crate) fn coi_for_data_set(records: Vec) -> Vec { .iter() .map(|id| { parents.get(id).map(|(a, b)| { - (*idx_from_id.get(a).unwrap() as usize, *idx_from_id.get(b).unwrap() as usize) + ( + *idx_from_id.get(a).unwrap() as usize, + *idx_from_id.get(b).unwrap() as usize, + ) }) }) .collect(); @@ -46,7 +49,7 @@ pub(crate) fn coi_for_data_set(records: Vec) -> Vec { let mut matrix = zeros(ids.len()); - for row in 0 .. matrix.len() { + for row in 0..matrix.len() { // Diag element: a_jj = 1 + 0.5 * a_pq if let Some((p1, p2)) = parents[row] { matrix[row][row] = 1.0 + 0.5 * matrix[p1][p2] @@ -54,28 +57,29 @@ pub(crate) fn coi_for_data_set(records: Vec) -> Vec { matrix[row][row] = 1.0; } - for col in row + 1 .. matrix.len() { + for col in row + 1..matrix.len() { // Other elements: a_ij = 0.5(a_ip + a_iq) if let Some((p1, p2)) = parents[col] { let f = 0.5 * (matrix[row][p1] + matrix[row][p2]); matrix[row][col] = f; matrix[col][row] = f; - } + } } } - + let coi_values = diag_minus_one(matrix); let mut out: Vec = Vec::new(); - for i in 0 .. coi_values.len() { + for i in 0..coi_values.len() { let coi = coi_values[i]; let name = ordered_people[i].name.clone(); - + out.push(OutputRecord { name, coi }); } out.sort_by(|a, b| { - b.coi.partial_cmp(&a.coi) + b.coi + .partial_cmp(&a.coi) .unwrap_or(a.name.cmp(&b.name)) .then(a.name.cmp(&b.name)) }); @@ -88,19 +92,25 @@ fn records_to_genealogy(records: Vec) -> HashMap { // Note we assume relations always come after the referenced person records for record in records { match record { - Record::Person { id, name, regnal_number, birth: _, death: _ } => { + Record::Person { + id, + name, + regnal_number, + birth: _, + death: _, + } => { let person = Person { name: cat_name(&name, ®nal_number), id: id, children: Vec::new(), }; people.insert(id, person); - }, + } Record::Relation { rel_type, from, to } => { if rel_type == RelationType::ParentChild { people.get_mut(&from).unwrap().children.push(to); - } - }, + } + } } } @@ -112,18 +122,22 @@ fn get_parents(people: &HashMap) -> HashMap { for (_, parent) in people { for child in &parent.children { - map.entry(*child).and_modify(|r| r.1 = Some(parent.id)).or_insert((parent.id, None)); + map.entry(*child) + .and_modify(|r| r.1 = Some(parent.id)) + .or_insert((parent.id, None)); } } - map.into_iter().map(|(k, v)| (k, (v.0, v.1.unwrap()))).collect() + map.into_iter() + .map(|(k, v)| (k, (v.0, v.1.unwrap()))) + .collect() } fn zeros(size: usize) -> Vec> { let mut matrix = Vec::with_capacity(size); - for _ in 0 .. size { + for _ in 0..size { let mut row = Vec::new(); - for _ in 0 .. size { + for _ in 0..size { row.push(0.0); } matrix.push(row); @@ -132,7 +146,10 @@ fn zeros(size: usize) -> Vec> { } fn diag_minus_one(data: Vec>) -> Vec { - data.iter().enumerate().map(|(r, row)| row[r] - 1.0).collect() + data.iter() + .enumerate() + .map(|(r, row)| row[r] - 1.0) + .collect() } // concatenates two strings inserting a space if the second is not empty diff --git a/examples/habsburgs/main.rs b/examples/habsburgs/main.rs index 9f07eeb..9d5dd63 100644 --- a/examples/habsburgs/main.rs +++ b/examples/habsburgs/main.rs @@ -1,5 +1,5 @@ -use std::{fs::File, io}; use std::path::Path; +use std::{fs::File, io}; use alg::coi_for_data_set; use fixcol::{ReadFixed, WriteFixed, WriteFixedAll}; @@ -20,7 +20,7 @@ enum RelationType { #[fixcol(key_width = 1)] enum Record { #[fixcol(key = "P")] - Person{ + Person { #[fixcol(width = 3)] id: u8, #[fixcol(width = 11, align = "right")] @@ -74,7 +74,9 @@ pub fn main() { .collect(); // Run the coi calculation - let results = coi_for_data_set(records).into_iter().filter(|r| r.coi > 0.0); + let results = coi_for_data_set(records) + .into_iter() + .filter(|r| r.coi > 0.0); // Write the serialized output to STDOUT let mut stdout = io::stdout(); diff --git a/fixcol-derive/src/attrs.rs b/fixcol-derive/src/attrs.rs index bc6964d..1598629 100644 --- a/fixcol-derive/src/attrs.rs +++ b/fixcol-derive/src/attrs.rs @@ -322,7 +322,12 @@ struct FieldConfigBuilder { impl FieldConfigBuilder { fn new() -> Self { - Self { width: None, skip: None, align: None, strict: None } + Self { + width: None, + skip: None, + align: None, + strict: None, + } } } @@ -385,7 +390,6 @@ pub(crate) fn parse_field_attributes( .map_err(|_| MacroError::new(err, param.value_span()))?; let old = conf.strict.replace(val); check_none("strict", param.key_span(), old)?; - } key => { return Err(MacroError::new( @@ -415,7 +419,7 @@ pub(crate) fn parse_field_attributes( } // TODO: confirm these need to be public -struct StructConfigBuilder { +struct StructConfigBuilder { strict: Option, } @@ -429,9 +433,7 @@ pub(crate) struct StructConfig { strict: bool, } -pub(crate) fn parse_struct_attributes( - attrs: &Vec, -) -> Result { +pub(crate) fn parse_struct_attributes(attrs: &Vec) -> Result { let params = parse_attributes(attrs)?; let mut conf = StructConfigBuilder::new(); @@ -471,7 +473,11 @@ struct EnumConfigBuilder { impl EnumConfigBuilder { pub fn new() -> Self { - Self { ignore_others: None, key_width: None, strict: None } + Self { + ignore_others: None, + key_width: None, + strict: None, + } } } diff --git a/fixcol-derive/src/enums.rs b/fixcol-derive/src/enums.rs index 2e1e07b..487ae2d 100644 --- a/fixcol-derive/src/enums.rs +++ b/fixcol-derive/src/enums.rs @@ -3,13 +3,12 @@ use quote::{format_ident, quote}; use syn::spanned::Spanned; use syn::{Attribute, FieldsNamed, FieldsUnnamed, Ident, Variant}; -use crate::attrs::{fixcol_attrs, parse_enum_attributes, parse_variant_attributes, OuterConfig, VariantConfig}; +use crate::attrs::{ + fixcol_attrs, parse_enum_attributes, parse_variant_attributes, OuterConfig, VariantConfig, +}; use crate::error::{MacroError, MacroResult}; use crate::fields::{ - read_named_fields, - read_unnamed_fields, - write_named_fields, - write_unnamed_fields, + read_named_fields, read_unnamed_fields, write_named_fields, write_unnamed_fields, }; // @@ -28,7 +27,8 @@ pub(crate) fn enum_read( .map(|variant| -> Result<(String, TokenStream), MacroError> { let var_name = &variant.ident; - let config: VariantConfig = parse_variant_attributes(&var_name, &variant.attrs, &enum_config)?; + let config: VariantConfig = + parse_variant_attributes(&var_name, &variant.attrs, &enum_config)?; let key = config.key.clone(); let read = match &variant.fields { @@ -151,11 +151,15 @@ pub(crate) fn enum_write( parse_variant_attributes(&variant.ident, &variant.attrs, &enum_config).unwrap(); // TODO: need to do this for write macros also let out = match &variant.fields { - syn::Fields::Named(fields) => write_struct_variant(&variant.ident, &config, fields)?, + syn::Fields::Named(fields) => { + write_struct_variant(&variant.ident, &config, fields)? + } syn::Fields::Unnamed(fields) if config.embed => { write_embedded_variant(&variant.ident, &config, fields)? } - syn::Fields::Unnamed(fields) => write_tuple_variant(&variant.ident, &config, fields)?, + syn::Fields::Unnamed(fields) => { + write_tuple_variant(&variant.ident, &config, fields)? + } syn::Fields::Unit => write_unit_variant(&variant.ident, &config), }; @@ -180,7 +184,11 @@ pub(crate) fn enum_write( Ok(code) } -fn write_struct_variant(ident: &Ident, config: &VariantConfig, fields: &FieldsNamed) -> MacroResult { +fn write_struct_variant( + ident: &Ident, + config: &VariantConfig, + fields: &FieldsNamed, +) -> MacroResult { let key = config.key.to_owned(); let key_len = key.len(); let (names, configs) = write_named_fields(&fields, &(*config).clone().into())?; @@ -204,7 +212,11 @@ fn write_struct_variant(ident: &Ident, config: &VariantConfig, fields: &FieldsNa Ok(code) } -fn write_tuple_variant(ident: &Ident, config: &VariantConfig, fields: &FieldsUnnamed) -> MacroResult { +fn write_tuple_variant( + ident: &Ident, + config: &VariantConfig, + fields: &FieldsUnnamed, +) -> MacroResult { let (_, configs) = write_unnamed_fields(&fields, &config.clone().into())?; let VariantConfig { key, strict, .. } = config; @@ -235,7 +247,11 @@ fn write_tuple_variant(ident: &Ident, config: &VariantConfig, fields: &FieldsUnn Ok(code) } -fn write_embedded_variant(ident: &Ident, config: &VariantConfig, fields: &FieldsUnnamed) -> MacroResult { +fn write_embedded_variant( + ident: &Ident, + config: &VariantConfig, + fields: &FieldsUnnamed, +) -> MacroResult { if fields.unnamed.len() != 1 { return Err(MacroError::new( "Embed param is only valid on variants with exactly one field", diff --git a/fixcol-derive/src/fields.rs b/fixcol-derive/src/fields.rs index c192956..519f58b 100644 --- a/fixcol-derive/src/fields.rs +++ b/fixcol-derive/src/fields.rs @@ -28,19 +28,19 @@ pub(crate) fn read_unnamed_fields( let buf_size = skip + width; let read_field = if field_num == last_field && !strict { - quote! { + quote! { println!("last/lax"); let n = buf.read(&mut s) .map_err(|e| fixcol::error::Error::from(e))?; println!("{}, [{}]", n, s); let raw = String::from_utf8(s[..n].to_vec()) - .map_err(|e| fixcol::error::Error::from(e))?; + .map_err(|e| fixcol::error::Error::from(e))?; } } else { - quote! { + quote! { println!("last/strict"); buf.read_exact(&mut s) - .map_err(|e| fixcol::error::Error::from(e))?; + .map_err(|e| fixcol::error::Error::from(e))?; let raw = String::from_utf8(s.to_vec()) .map_err(|e| fixcol::error::Error::from(e))?; } @@ -84,22 +84,21 @@ pub(crate) fn read_named_fields( let buf_size = skip + width; let read_field = if field_num == last_field && !strict { - quote! { + quote! { let n = buf.read(&mut s) .map_err(|e| fixcol::error::Error::from(e))?; let raw = String::from_utf8(s[..n].to_vec()) - .map_err(|e| fixcol::error::Error::from(e))?; + .map_err(|e| fixcol::error::Error::from(e))?; } } else { - quote! { + quote! { buf.read_exact(&mut s) - .map_err(|e| fixcol::error::Error::from(e))?; + .map_err(|e| fixcol::error::Error::from(e))?; let raw = String::from_utf8(s.to_vec()) .map_err(|e| fixcol::error::Error::from(e))?; } }; - // TODO: we shouldn't need a String here at all let read = quote! { let mut s: [u8; #buf_size] = [0; #buf_size]; @@ -143,7 +142,8 @@ pub(crate) fn write_unnamed_fields( .enumerate() .map(|field| -> Result<(Index, FieldConfig), MacroError> { let name = syn::Index::from(field.0); - let config = attrs::parse_field_attributes(&field.1.span(), &field.1.attrs, outer_config)?; + let config = + attrs::parse_field_attributes(&field.1.span(), &field.1.attrs, outer_config)?; Ok((name, config)) }) diff --git a/fixcol-derive/src/lib.rs b/fixcol-derive/src/lib.rs index 9994410..b26dc80 100644 --- a/fixcol-derive/src/lib.rs +++ b/fixcol-derive/src/lib.rs @@ -23,8 +23,8 @@ use crate::structs::{struct_read, struct_write}; /// Derive proc-macro for ReadFixed // // See documentation on [`ReadFixed`] for a full description. -// -// [`ReadFixed`]: fixcol::ReadFixed +// +// [`ReadFixed`]: fixcol::ReadFixed #[proc_macro_derive(ReadFixed, attributes(fixcol))] pub fn read_fixed_impl(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); @@ -59,7 +59,7 @@ pub fn read_fixed_impl(input: TokenStream) -> TokenStream { } /// Derive proc-macro for WriteFixed -// +// // See [[`WriteFixed`]] for a complete discuassion. #[proc_macro_derive(WriteFixed, attributes(fixcol))] pub fn write_fixed_impl(input: TokenStream) -> TokenStream { @@ -71,7 +71,9 @@ pub fn write_fixed_impl(input: TokenStream) -> TokenStream { let function_impl_result = match ast.data { Data::Struct(DataStruct { fields, .. }) => struct_write(name, attrs, fields), - Data::Enum(DataEnum { variants, .. }) => enum_write(name, attrs, &variants.iter().collect()), + Data::Enum(DataEnum { variants, .. }) => { + enum_write(name, attrs, &variants.iter().collect()) + } Data::Union(u) => Err(MacroError::new( "Deriving WriteFixed on unions is not supported", u.union_token.span(), diff --git a/fixcol-derive/src/structs.rs b/fixcol-derive/src/structs.rs index 6930c91..e690ffe 100644 --- a/fixcol-derive/src/structs.rs +++ b/fixcol-derive/src/structs.rs @@ -4,7 +4,9 @@ use syn::{Attribute, Fields, FieldsNamed, FieldsUnnamed}; use crate::attrs::{parse_struct_attributes, OuterConfig, StructConfig}; use crate::error::{MacroError, MacroResult}; -use crate::fields::{read_named_fields, read_unnamed_fields, write_named_fields, write_unnamed_fields}; +use crate::fields::{ + read_named_fields, read_unnamed_fields, write_named_fields, write_unnamed_fields, +}; // // Reads diff --git a/src/error.rs b/src/error.rs index 989cccc..1205b8f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -62,7 +62,7 @@ use std::string::FromUtf8Error; /// /// ``` /// use fixcol::ReadFixed; -/// +/// /// #[derive(ReadFixed)] /// struct MyType { /// // Fields here @@ -71,7 +71,7 @@ use std::string::FromUtf8Error; /// use std::fs::File; /// # fn f() { /// let mut file = File::open("my_file.txt").unwrap(); -/// +/// /// for row in MyType::read_fixed_all(file) { /// match row { /// Ok(my_type) => { @@ -198,17 +198,11 @@ impl DataError { } pub(crate) fn new_data_width_error(text: String, expected: usize, actual: usize) -> Self { - Self::new_err( - text, - InnerError::InvalidWidth(expected, actual) - ) + Self::new_err(text, InnerError::InvalidWidth(expected, actual)) } pub(crate) fn whitespace_error(text: String) -> Self { - Self::new_err( - text, - InnerError::WhitespaceError, - ) + Self::new_err(text, InnerError::WhitespaceError) } /// Creates a new custom `DataError` @@ -304,11 +298,18 @@ impl Display for DataError { } InnerError::InvalidWidth(exp, act) => { fmt_err(&self.text, f)?; - write!(f, "Expected field to have width {} but supplied value has width {}.", exp, act)?; + write!( + f, + "Expected field to have width {} but supplied value has width {}.", + exp, act + )?; } InnerError::WhitespaceError => { fmt_err(&self.text, f)?; - write!(f, "Found non-whitespace character between data fields (strict)")?; + write!( + f, + "Found non-whitespace character between data fields (strict)" + )?; } } @@ -334,11 +335,11 @@ pub enum InnerError { /// While decoding an enum found a key that does not match any known variant UnknownKey, /// Aparent width of parsed field do not match declared field size - /// + /// /// Params are expected len, actual len InvalidWidth(usize, usize), /// Whitespace error in `strict` mode. - /// + /// /// While parsing serialized data in `strict` mode, found missing whitespace /// at end of line or a non-whitespace character where whitespace was expected. WhitespaceError, diff --git a/src/fixcol.rs b/src/fixcol.rs index 702ceb6..e064ea1 100644 --- a/src/fixcol.rs +++ b/src/fixcol.rs @@ -1,6 +1,6 @@ -use std::io::{BufRead, BufReader, Lines, Read}; #[cfg(any(feature = "experimental-write", doc))] use std::io::Write; +use std::io::{BufRead, BufReader, Lines, Read}; use std::marker::PhantomData; use crate::error::Error; @@ -198,7 +198,7 @@ pub trait ReadFixed { /// use fixcol::ReadFixed; /// use std::fs::File; /// use std::io; - /// + /// /// #[derive(ReadFixed)] /// struct Foo { /// #[fixcol(width=3)] @@ -409,7 +409,7 @@ mod tests { // Derive tests (struct) //////////////////////////////// - + // Helper function only used in write tests #[cfg(feature = "experimental-write")] fn to_str(inp: Vec) -> String { diff --git a/src/lib.rs b/src/lib.rs index cf7a4d2..83f60bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,25 @@ #![feature(doc_auto_cfg)] //! A crate used for *fixed* width *column* serialization and deserialization -//! +//! //! Fixcol provides a derive based deserialization framework for parsing text files //! with fixed column width serialization formats. While file formats using character //! delimeters such as CSV or JSON are more common today, fixed column file formats //! are more naturally human readable and many older data sets, especially public //! domain data sets continue to use them. -//! -//! The library is built around the [`ReadFixed`] trait which is ordinarily +//! +//! The library is built around the [`ReadFixed`] trait which is ordinarily //! derived on a data type (`struct` or `enum`) that represents a row of the data //! file. The `fixcol` attribute is used to define how fields map to the schema. -//! +//! //! For writing data files rudimentary serialization is provided by [`WriteFixed`] //! and [`WriteFixedAll`] behind the `experimental-write` feature flag. -//! +//! //! ## Examples //! ### Basic Example -//! +//! //! Consider the following data file: -//! +//! //! ```text //! Tokyo 13515271 35.689 139.692 //! Delhi 16753235 28.610 77.230 @@ -27,14 +27,14 @@ //! São Paulo 12252023 -23.550 -46.333 //! Mexico City 9209944 19.433 -99.133 //! ``` -//! +//! //! We can create a basic data structure corresponding to the records in the file //! and then read the data file as shown. -//! +//! //! ``` //! use fixcol::ReadFixed; //! # use std::fs::File; -//! +//! //! #[derive(ReadFixed)] //! # #[derive(Debug, PartialEq)] //! struct City { @@ -47,7 +47,7 @@ //! #[fixcol(skip = 1, width = 8, align = "right")] //! lon: f32, //! } -//! +//! //! # // TODO: Unicode support :/ make São Paulo work //! # fn f() { //! let mut file = File::open("cities.txt"); @@ -64,7 +64,7 @@ //! std::process::exit(1); //! } //! }).collect(); -//! +//! //! assert_eq!(cities, vec![ //! City { name: "Tokyo".into(), population: 13515271, lat: 35.689, lon: 139.692 }, //! City { name: "Delhi".into(), population: 16753235, lat: 28.610, lon: 77.230 }, @@ -73,28 +73,28 @@ //! City { name: "Mexico City".into(), population: 9209944, lat: 19.433, lon: -99.133 }, //! ]); //! ``` -//! +//! //! ### Multiple Record Types -//! -//! Many data files contain lines corresponding to multiple types of records. +//! +//! Many data files contain lines corresponding to multiple types of records. //! Typically the record type is indicated by the first few columns of the line. //! In Fixcol we call this the *key* of the record. Multiple record types can be //! decoded using an `enum` with a key annotation. -//! +//! //! Consider a directed graph with named nodes defined in a data file like the //! follinwg. -//! +//! //! ```text //! NODE 001 Item A //! NODE 002 Item B //! EDGE 001 002 //! ``` -//! +//! //! This file can be parsed with an enum like the follwing. -//! +//! //! ``` //! use fixcol::ReadFixed; -//! +//! //! # #[derive(PartialEq, Debug)] //! #[derive(ReadFixed)] //! #[fixcol(key_width = 4)] @@ -128,12 +128,12 @@ //! ``` //! //! ### Embedded Variants -//! +//! //! Often instead of having fields defined directly on an `enum` variant it is -//! convenient to *embed* a struct within a single parameter named tuple -//! variant. Fixcol supports this pattern. To use it, derive [`ReadFixed`] on +//! convenient to *embed* a struct within a single parameter named tuple +//! variant. Fixcol supports this pattern. To use it, derive [`ReadFixed`] on //! the inner type and use the `embed` parameter on the variant. -//! +//! //! ``` //! # use fixcol::ReadFixed; //! # #[derive(PartialEq, Debug)] @@ -144,7 +144,7 @@ //! #[fixcol(skip = 1, width = 6)] //! name: String //! } -//! +//! //! # #[derive(PartialEq, Debug)] //! #[derive(ReadFixed)] //! struct Edge { @@ -153,7 +153,7 @@ //! #[fixcol(skip = 1, width = 3)] //! to_id: u8, //! } -//! +//! //! # #[derive(PartialEq, Debug)] //! #[derive(ReadFixed)] //! #[fixcol(key_width = 4)] @@ -175,23 +175,23 @@ //! # GraphItem::Edge(Edge { from_id: 1, to_id: 2 }), //! # ]); //! ``` -//! +//! //! ## Strict Mode -//! +//! //! Strict mode may be toggled on or off setting the appropriate `fixcol` attribute //! like `#[fixcol(strict = true)]`. When strict mode is disabled, Fixcol will //! try it's best to recover encoding errors. When enabled, many more unexpected //! conditions will be reported as errors. -//! -//! Strict mode is currently enabled by default, but **this may change** in a +//! +//! Strict mode is currently enabled by default, but **this may change** in a //! future version. -//! +//! //! The `strict` parameter can be applied to a `struct` or `enum`, `enum` variant, //! or field. The setting will cascade to other levels with the innermost explicit //! application of the `strict` parameter controlling. -//! +//! //! #### Example -//! +//! //! ``` //! # use fixcol::ReadFixed; //! #[derive(ReadFixed)] @@ -205,9 +205,9 @@ //! z: u8, // Strict mode not will be applied //! } //! ``` -//! +//! //! #### Strict mode effects -//! +//! //! When a given field is parsed in strict mode the following conditions become //! errors. //! - The last field on a line is not whitespace padded to the defined length. @@ -216,124 +216,124 @@ //! full length. //! - A `Left` aligned field beginning with whitespace. //! - A `Right` aligned field ending with whitespace. -//! +//! //! Additional rules are applied while attempting to write a record. The following //! are errors in strict mode. //! - A `Full` aligned `String` field that is not the expected full length. That -//! is, the supplied string must either be naturally the correct length or +//! is, the supplied string must either be naturally the correct length or //! explicitly whitespace padded to be. //! - Value supplied for any column that would overflow the allowed space. -//! +//! //! ## Schema Definition Parameters -//! -//! Fixcol defines serialization and deserialization schemas using `fixcol` +//! +//! Fixcol defines serialization and deserialization schemas using `fixcol` //! annotations that contain a list of one or more parameters in the form //! `#[fixcol(param1 = value1, param2 = value2, ...)]`. -//! +//! //! #### Align -//! +//! //! Indicates the text alignment of the specified field. -//! +//! //! **Can be applied to**: Field -//! +//! //! **Allowed Values**: `"left"`, `"right"`, `"full"` -//! +//! //! | Value | Meaning | //! |-------|---------| //! | Left | The value is left aligned and trailing whitespace can be ignored | //! | Right | The caule is right aligned and leading whitespace can be ignored | //! | Full | The value is expected to occupy the full defined width. Leading and trailing whitespace are considered significant. | -//! +//! //! The values of the `align` parameter are mapped to an instance of [`Alignment`] //! internally. -//! +//! //! **Default**: Left //! //! **Example**: `#[fixcol(width = 6, align = "right")]` -//! +//! //! #### Embed -//! +//! //! When decoding a single valued tuple-style enum variant, use the [`ReadFixed`] //! implementation on the inner type. -//! +//! //! **Can be applied to**: Enum Variant -//! +//! //! **Allowed Values**: `true`, `false` -//! +//! //! **Default**: `false` //! //! **Example**: `#[fixcol(embed = true)]` -//! +//! //! #### Key -//! +//! //! When decoding multiple record types into an enum, indicates the key that //! signifies this particular enum variant should be used to decode the line. -//! +//! //! To encode keys of different lengths, space pad the shorter keys so that all //! declared keys are explicitly `key_width` characters. -//! +//! //! **Can be applied to**: Enum Variant -//! +//! //! **Allowed Values**: Strings of length `key_width` -//! +//! //! **Default**: Must be set **explicitly**. //! //! **Example**: `#[fixcol(key = "EDGE")]` -//! +//! //! #### Key Width -//! +//! //! When decoding multiple record types into an enum, indicates how many characters //! at the begining of each line should be considered the *key* used to identify //! which record type the line contains and therefore which enum variant should //! be used to decode the line. -//! +//! //! **Can be applied to**: Enum -//! +//! //! **Allowed Values**: Positive integers -//! +//! //! **Default**: Must be set **explicitly**. //! //! **Example**: `#[fixcol(key_width = 4)]` -//! +//! //! #### Skip -//! +//! //! Indicates the number of columns (measured in bytes) that are expected to be //! blank between the prior data field and the current data field. -//! +//! //! **Can be applied to**: Field -//! +//! //! **Allowed Values**: Non-negative integers -//! +//! //! **Default**: Zero //! //! **Example**: `#[fixcol(skip = 1, width = 12)]` -//! +//! //! #### Strict -//! +//! //! Indicates whether [strict mode](crate#strict-mode) should be enabled (See above). -//! +//! //! **Can be applied to**: Struct, Enum, Enum Varriant, Field -//! +//! //! **Allowed Values**: `true`, `false` -//! +//! //! **Default**: Cascades from outer context, outermost default `true`. //! //! **Example**: `#[fixcol(strict = true)]` -//! -//! +//! +//! //! #### Width -//! +//! //! Indicates the number of columns (measured in bytes) used to encode the //! target field. -//! +//! //! **Can be applied to**: Field -//! +//! //! **Allowed Values**: Positive integers -//! +//! //! **Default**: Must be set **explicitly**. //! //! **Example**: `#[fixcol(width = 12)]` -//! +//! pub mod error; mod fixcol; diff --git a/src/parse.rs b/src/parse.rs index f1956ac..409621c 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -236,11 +236,15 @@ macro_rules! fixed_deserializer_int_impl { if desc.strict && desc.alignment == Alignment::Full && trimmed.len() != s.len() { let trimmed_len = trimmed.len(); - Err(DataError::new_data_width_error(String::from(trimmed), trimmed_len, s.len())) + Err(DataError::new_data_width_error( + String::from(trimmed), + trimmed_len, + s.len(), + )) } else { trimmed.parse::<$t>().map_err(|e| { DataError::new_err(trimmed.to_string(), InnerError::ParseIntError(e)) - }) + }) } } } @@ -586,9 +590,24 @@ mod tests { #[test] fn extract_f32_padding() { let descs = vec![ - FieldDescription{ skip: 0, len: 6, alignment: Alignment::Full, strict: false }, - FieldDescription{ skip: 0, len: 6, alignment: Alignment::Left, strict: false }, - FieldDescription{ skip: 0, len: 6, alignment: Alignment::Right, strict: false }, + FieldDescription { + skip: 0, + len: 6, + alignment: Alignment::Full, + strict: false, + }, + FieldDescription { + skip: 0, + len: 6, + alignment: Alignment::Left, + strict: false, + }, + FieldDescription { + skip: 0, + len: 6, + alignment: Alignment::Right, + strict: false, + }, ]; let expected: f32 = 3.14; @@ -612,7 +631,6 @@ mod tests { assert_eq!(tests_run, 3); } - #[test] fn extract_f32_full() { @@ -731,7 +749,7 @@ mod tests { }; let actual = u8::parse_fixed("042", &desc).unwrap(); assert_eq!(actual, 42); - + let desc = FieldDescription { skip: 0, len: 3, @@ -749,7 +767,7 @@ mod tests { }; let actual = u8::parse_fixed(" 42", &desc).unwrap(); assert_eq!(actual, 42); - + let desc = FieldDescription { skip: 0, len: 3, @@ -822,7 +840,7 @@ mod tests { let actual = u8::parse_fixed(" 42 ", &desc); assert!(actual.is_err()); assert_eq!( - actual.unwrap_err().to_string(), + actual.unwrap_err().to_string(), "Error decoding data from \"42 \": invalid digit found in string\n" ); diff --git a/src/write.rs b/src/write.rs index 1d1bc4e..e059c7c 100644 --- a/src/write.rs +++ b/src/write.rs @@ -41,12 +41,12 @@ impl FixedSerializer for String { ) -> Result<(), Error> { // If strict fail on overflow if desc.strict && self.len() > desc.len { - return Err(DataError::new_data_width_error(self.clone(), desc.len, self.len()).into()) + return Err(DataError::new_data_width_error(self.clone(), desc.len, self.len()).into()); } // if strict and full-align fail on too short also if desc.strict && desc.alignment == Alignment::Full && self.len() != desc.len { - return Err(DataError::new_data_width_error(self.clone(), desc.len, self.len()).into()) + return Err(DataError::new_data_width_error(self.clone(), desc.len, self.len()).into()); } // If so we'll need to truncate @@ -92,9 +92,7 @@ macro_rules! fixed_serializer_int_impl { if s.len() > desc.len { if desc.strict { let len = s.len(); - return Err( - DataError::new_data_width_error(s, desc.len, len).into() - ); + return Err(DataError::new_data_width_error(s, desc.len, len).into()); } // truncate if not strict s = s.as_str()[..desc.len].to_string(); @@ -295,7 +293,7 @@ mod tests { assert!(res.is_err()); let e = res.unwrap_err(); assert_eq!( - e.to_string(), + e.to_string(), "Error decoding data from \"foo\": Expected field to \ have width 6 but supplied value has width 3.\n" ); diff --git a/tests/examples.rs b/tests/examples.rs index e2526d3..8cd98df 100644 --- a/tests/examples.rs +++ b/tests/examples.rs @@ -8,7 +8,11 @@ fn read_expected_output_file(name: &str, variant: &str) -> String { let file_name = format!("{}.txt", variant); let cargo = Path::new(env!("CARGO_MANIFEST_DIR")); - let path = cargo.join("examples").join(name).join("expected_output").join(file_name); + let path = cargo + .join("examples") + .join(name) + .join("expected_output") + .join(file_name); if path.exists() { fs::read_to_string(path).unwrap() diff --git a/tests/test_derive_nested.rs b/tests/test_derive_nested.rs index d8ec923..9e86ea2 100644 --- a/tests/test_derive_nested.rs +++ b/tests/test_derive_nested.rs @@ -9,7 +9,6 @@ use fixcol::{WriteFixed, WriteFixedAll}; // we did not correctly handle when those two had different // names. i.e., Atom(Atom) worked but AtomV(AtomS) did not. - #[cfg_attr(feature = "experimental-write", derive(WriteFixed))] #[derive(Debug, PartialEq, Eq, ReadFixed)] struct AtomS { diff --git a/tests/ui.rs b/tests/ui.rs index 8773470..b1c9689 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -11,7 +11,6 @@ fn ui_write() { t.compile_fail("tests/ui/read-write/*.rs") } - #[cfg(not(feature = "experimental-write"))] #[test] fn ui_read_only() { From 5e60c60ef577e7cc99d850c7031b4e6da1511ff8 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Fri, 30 Aug 2024 14:16:50 -0400 Subject: [PATCH 09/12] more formating --- examples/habsburgs/main.rs | 3 +- fixcol-derive/src/attrs.rs | 3 +- fixcol-derive/src/fields.rs | 3 +- fixcol-derive/src/lib.rs | 1 - src/error.rs | 12 +++--- src/fixcol.rs | 28 ++++++++------ src/lib.rs | 77 +++++++++++++++++++++++++------------ src/parse.rs | 19 +++++---- 8 files changed, 91 insertions(+), 55 deletions(-) diff --git a/examples/habsburgs/main.rs b/examples/habsburgs/main.rs index 9d5dd63..9eb3992 100644 --- a/examples/habsburgs/main.rs +++ b/examples/habsburgs/main.rs @@ -1,5 +1,6 @@ +use std::fs::File; +use std::io; use std::path::Path; -use std::{fs::File, io}; use alg::coi_for_data_set; use fixcol::{ReadFixed, WriteFixed, WriteFixedAll}; diff --git a/fixcol-derive/src/attrs.rs b/fixcol-derive/src/attrs.rs index 1598629..8f8d428 100644 --- a/fixcol-derive/src/attrs.rs +++ b/fixcol-derive/src/attrs.rs @@ -4,7 +4,8 @@ use std::str::FromStr; use proc_macro2::{Literal, Span, TokenStream, TokenTree}; use quote::quote; -use syn::{spanned::Spanned, Attribute, Ident, Meta, Path}; +use syn::spanned::Spanned; +use syn::{Attribute, Ident, Meta, Path}; use crate::error::MacroError; diff --git a/fixcol-derive/src/fields.rs b/fixcol-derive/src/fields.rs index 519f58b..8f6c8ff 100644 --- a/fixcol-derive/src/fields.rs +++ b/fixcol-derive/src/fields.rs @@ -1,6 +1,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; -use syn::{spanned::Spanned, FieldsNamed, FieldsUnnamed, Index}; +use syn::spanned::Spanned; +use syn::{FieldsNamed, FieldsUnnamed, Index}; use crate::attrs::{self, parse_field_attributes, FieldConfig, OuterConfig}; use crate::error::MacroError; diff --git a/fixcol-derive/src/lib.rs b/fixcol-derive/src/lib.rs index b26dc80..6106942 100644 --- a/fixcol-derive/src/lib.rs +++ b/fixcol-derive/src/lib.rs @@ -12,7 +12,6 @@ extern crate syn; use enums::enum_write; use error::MacroError; use proc_macro::TokenStream; - use quote::quote; use syn::spanned::Spanned; use syn::{Data, DataEnum, DataStruct, DeriveInput}; diff --git a/src/error.rs b/src/error.rs index 1205b8f..5d4b790 100644 --- a/src/error.rs +++ b/src/error.rs @@ -96,7 +96,6 @@ use std::string::FromUtf8Error; /// [`ReadFixed`]: crate::ReadFixed /// [`WriteFixed`]: crate::WriteFixed /// [`to_string`]: std::string::ToString::to_string() -/// #[derive(Debug)] pub enum Error { /// An error that occured while parsing the formatted data @@ -226,8 +225,8 @@ impl DataError { /// use `DataError::custom` to provide error context. /// /// ``` - /// use fixcol::{FixedDeserializer, FieldDescription}; /// use fixcol::error::DataError; + /// use fixcol::{FieldDescription, FixedDeserializer}; /// /// struct TriState(Option); /// @@ -236,14 +235,15 @@ impl DataError { /// // We've defined this type as always having one column so confirm that /// assert_eq!(desc.len, 1); /// // burn columns we have to skip - /// let column = &s[desc.skip..desc.skip+1]; + /// let column = &s[desc.skip..desc.skip + 1]; /// match column { /// "Y" => Ok(TriState(Some(true))), /// "N" => Ok(TriState(Some(false))), /// " " => Ok(TriState(None)), - /// other => { - /// Err(DataError::custom(other, "Expected \"Y\", \"N\", or an empty column")) - /// } + /// other => Err(DataError::custom( + /// other, + /// "Expected \"Y\", \"N\", or an empty column", + /// )), /// } /// } /// } diff --git a/src/fixcol.rs b/src/fixcol.rs index e064ea1..76b4cc1 100644 --- a/src/fixcol.rs +++ b/src/fixcol.rs @@ -27,9 +27,9 @@ pub trait WriteFixed { /// # use std::io; /// #[derive(WriteFixed)] /// struct Point { - /// #[fixcol(width=3)] + /// #[fixcol(width = 3)] /// x: u8, - /// #[fixcol(width=3)] + /// #[fixcol(width = 3)] /// y: u8, /// } /// @@ -58,8 +58,10 @@ pub trait WriteFixed { /// use fixcol::WriteFixed; /// #[derive(WriteFixed)] /// struct Point { -/// #[fixcol(width=3)] x: u8, -/// #[fixcol(width=3)] y: u8, +/// #[fixcol(width = 3)] +/// x: u8, +/// #[fixcol(width = 3)] +/// y: u8, /// } /// // Point implements WriteFixed /// @@ -195,15 +197,16 @@ pub trait ReadFixed { /// /// # Example /// ``` - /// use fixcol::ReadFixed; /// use std::fs::File; /// use std::io; /// + /// use fixcol::ReadFixed; + /// /// #[derive(ReadFixed)] /// struct Foo { - /// #[fixcol(width=3)] + /// #[fixcol(width = 3)] /// foo: String, - /// #[fixcol(width=3)] + /// #[fixcol(width = 3)] /// bar: String, /// } /// @@ -266,9 +269,9 @@ pub trait ReadFixed { /// # use fixcol::FieldDescription; /// #[derive(ReadFixed)] /// struct Point { - /// #[fixcol(width=3, align="right")] + /// #[fixcol(width = 3, align = "right")] /// x: u8, - /// #[fixcol(width=3, align="right")] + /// #[fixcol(width = 3, align = "right")] /// y: u8, /// } /// @@ -315,9 +318,9 @@ pub trait ReadFixed { /// # use fixcol::FieldDescription; /// #[derive(ReadFixed)] /// struct Point { - /// #[fixcol(width=3, align="right")] + /// #[fixcol(width = 3, align = "right")] /// x: u8, - /// #[fixcol(width=3, align="right")] + /// #[fixcol(width = 3, align = "right")] /// y: u8, /// } /// @@ -417,10 +420,11 @@ mod tests { str::from_utf8(inp.as_slice()).unwrap().to_string() } - use crate as fixcol; #[cfg(feature = "experimental-write")] use fixcol::WriteFixed; + use crate as fixcol; + #[cfg_attr(feature = "experimental-write", derive(WriteFixed))] #[derive(ReadFixed, Eq, PartialEq, Debug)] struct MyStruct { diff --git a/src/lib.rs b/src/lib.rs index 83f60bd..e0cd699 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,21 +57,51 @@ //! # Shanghai 24870895 31.229 121.475 //! # Sao Paulo 12252023 -23.550 -46.333 //! # Mexico City 9209944 19.433 -99.133".as_bytes(); -//! let cities: Vec = City::read_fixed_all(file).map(|res| match res { -//! Ok(city) => city, -//! Err(err) => { -//! eprintln!("{}", err); -//! std::process::exit(1); -//! } -//! }).collect(); -//! -//! assert_eq!(cities, vec![ -//! City { name: "Tokyo".into(), population: 13515271, lat: 35.689, lon: 139.692 }, -//! City { name: "Delhi".into(), population: 16753235, lat: 28.610, lon: 77.230 }, -//! City { name: "Shanghai".into(), population: 24870895, lat: 31.229, lon: 121.475 }, -//! City { name: "Sao Paulo".into(), population: 12252023, lat: -23.550, lon: -46.333 }, -//! City { name: "Mexico City".into(), population: 9209944, lat: 19.433, lon: -99.133 }, -//! ]); +//! let cities: Vec = City::read_fixed_all(file) +//! .map(|res| match res { +//! Ok(city) => city, +//! Err(err) => { +//! eprintln!("{}", err); +//! std::process::exit(1); +//! } +//! }) +//! .collect(); +//! +//! assert_eq!( +//! cities, +//! vec![ +//! City { +//! name: "Tokyo".into(), +//! population: 13515271, +//! lat: 35.689, +//! lon: 139.692 +//! }, +//! City { +//! name: "Delhi".into(), +//! population: 16753235, +//! lat: 28.610, +//! lon: 77.230 +//! }, +//! City { +//! name: "Shanghai".into(), +//! population: 24870895, +//! lat: 31.229, +//! lon: 121.475 +//! }, +//! City { +//! name: "Sao Paulo".into(), +//! population: 12252023, +//! lat: -23.550, +//! lon: -46.333 +//! }, +//! City { +//! name: "Mexico City".into(), +//! population: 9209944, +//! lat: 19.433, +//! lon: -99.133 +//! }, +//! ] +//! ); //! ``` //! //! ### Multiple Record Types @@ -104,7 +134,7 @@ //! #[fixcol(skip = 1, width = 3)] //! id: u8, //! #[fixcol(skip = 1, width = 6)] -//! name: String +//! name: String, //! }, //! #[fixcol(key = "EDGE")] //! Edge { @@ -112,7 +142,7 @@ //! from_id: u8, //! #[fixcol(skip = 1, width = 3)] //! to_id: u8, -//! } +//! }, //! } //! # let mut buf = "NODE 001 Item A //! # NODE 002 Item B @@ -142,7 +172,7 @@ //! #[fixcol(skip = 1, width = 3)] //! id: u8, //! #[fixcol(skip = 1, width = 6)] -//! name: String +//! name: String, //! } //! //! # #[derive(PartialEq, Debug)] @@ -333,7 +363,6 @@ //! **Default**: Must be set **explicitly**. //! //! **Example**: `#[fixcol(width = 12)]` -//! pub mod error; mod fixcol; @@ -346,17 +375,15 @@ mod write; extern crate fixcol_derive; pub use fixcol::{Iter, ReadFixed}; -pub use format::{Alignment, FieldDescription}; -pub use parse::FixedDeserializer; - #[cfg(feature = "experimental-write")] pub use fixcol::{WriteFixed, WriteFixedAll}; -#[cfg(feature = "experimental-write")] -pub use write::FixedSerializer; - pub use fixcol_derive::ReadFixed; #[cfg(feature = "experimental-write")] pub use fixcol_derive::WriteFixed; +pub use format::{Alignment, FieldDescription}; +pub use parse::FixedDeserializer; +#[cfg(feature = "experimental-write")] +pub use write::FixedSerializer; #[cfg(test)] mod tests { diff --git a/src/parse.rs b/src/parse.rs index 409621c..0c3f585 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -48,11 +48,11 @@ use crate::ReadFixed; /// /// #[derive(ReadFixed)] /// struct Person { -/// #[fixcol(width=10)] +/// #[fixcol(width = 10)] /// pub name: String, /// #[fixcol(width=3, align=right)] /// pub age: u8, -/// #[fixcol(width=2)] +/// #[fixcol(width = 2)] /// pub eye_color: EyeColor, /// } /// @@ -128,9 +128,9 @@ use crate::ReadFixed; /// #[derive(ReadFixed)] /// # #[derive(Eq, PartialEq, Debug)] /// struct Person { -/// #[fixcol(width=12)] +/// #[fixcol(width = 12)] /// name: String, -/// #[fixcol(width=10, skip=1)] +/// #[fixcol(width = 10, skip = 1)] /// birthday: Birthday, /// } /// @@ -140,20 +140,23 @@ use crate::ReadFixed; /// /// impl FixedDeserializer for Birthday { /// fn parse_fixed(s: &str, desc: &FieldDescription) -> Result { -/// let text = &s[desc.skip..desc.skip+desc.len]; +/// let text = &s[desc.skip..desc.skip + desc.len]; /// let mut parts = text.split(' ').filter(|x| *x != ""); /// -/// let year = parts.next() +/// let year = parts +/// .next() /// .ok_or(DataError::custom(&text, "Could not find year"))? /// .parse() /// .map_err(|e| DataError::custom(&text, "Could not decode year"))?; /// -/// let month = parts.next() +/// let month = parts +/// .next() /// .ok_or(DataError::custom(&text, "Could not find month"))? /// .parse() /// .map_err(|e| DataError::custom(&text, "Could not decode month"))?; /// -/// let day = parts.next() +/// let day = parts +/// .next() /// .ok_or(DataError::custom(&text, "Could not find day"))? /// .parse() /// .map_err(|e| DataError::custom(&text, "Could not decode day"))?; From 13b1ed51af828de10d0868ee9c869e9549dbc1b1 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Fri, 30 Aug 2024 14:18:21 -0400 Subject: [PATCH 10/12] minor spacing --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e0cd699..a37c994 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -377,9 +377,11 @@ extern crate fixcol_derive; pub use fixcol::{Iter, ReadFixed}; #[cfg(feature = "experimental-write")] pub use fixcol::{WriteFixed, WriteFixedAll}; + pub use fixcol_derive::ReadFixed; #[cfg(feature = "experimental-write")] pub use fixcol_derive::WriteFixed; + pub use format::{Alignment, FieldDescription}; pub use parse::FixedDeserializer; #[cfg(feature = "experimental-write")] From 6944feac42ec501c1aa9d926130e32c1810f86e4 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Fri, 30 Aug 2024 16:48:34 -0400 Subject: [PATCH 11/12] added Debug for Iter --- src/fixcol.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/fixcol.rs b/src/fixcol.rs index 76b4cc1..b8b74a2 100644 --- a/src/fixcol.rs +++ b/src/fixcol.rs @@ -125,6 +125,7 @@ impl> WriteFixedAll for Iter { /// [`read_fixed_all`]. /// /// [`read_fixed_all`]: ReadFixed::read_fixed_all +#[derive(Debug)] pub struct Iter where T: ReadFixed, @@ -410,6 +411,13 @@ mod tests { assert_eq!(actual, expected); } + #[test] + fn iter_debug() { + let buf = "foo\nbar\nbaz"; + let iter = Foo::read_fixed_all(buf.as_bytes()); + assert_ne!(format!("{:?}", iter), ""); + } + // Derive tests (struct) //////////////////////////////// From fb9e3b300451ed2893f4f7a5b840234c209dea01 Mon Sep 17 00:00:00 2001 From: BrianLondon Date: Fri, 30 Aug 2024 19:35:58 -0400 Subject: [PATCH 12/12] remove unwrap from doc examples --- src/error.rs | 10 ++++++---- src/fixcol.rs | 52 +++++++++++++++++++++++++++++++++++---------------- src/parse.rs | 11 ++++++++--- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/error.rs b/src/error.rs index 5d4b790..26e94a9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,8 +24,8 @@ //! } //! //! use std::fs::File; -//! # fn f() { -//! let mut file = File::open("my_file.txt").unwrap(); +//! # fn f() -> Result<(), fixcol::error::Error> { +//! let mut file = File::open("my_file.txt")?; //! for row in MyType::read_fixed_all(file) { //! match row { //! Ok(my_type) => { @@ -37,6 +37,7 @@ //! } //! } //! } +//! # Ok(()) //! # } //! ``` use std::fmt::{Display, Formatter}; @@ -69,8 +70,8 @@ use std::string::FromUtf8Error; /// } /// /// use std::fs::File; -/// # fn f() { -/// let mut file = File::open("my_file.txt").unwrap(); +/// # fn f() -> Result<(), fixcol::error::Error> { +/// let mut file = File::open("my_file.txt")?; /// /// for row in MyType::read_fixed_all(file) { /// match row { @@ -83,6 +84,7 @@ use std::string::FromUtf8Error; /// } /// } /// } +/// # Ok(()) /// # } /// ``` /// diff --git a/src/fixcol.rs b/src/fixcol.rs index b8b74a2..342b9b9 100644 --- a/src/fixcol.rs +++ b/src/fixcol.rs @@ -90,8 +90,9 @@ pub trait WriteFixedAll { /// # Point { x: 123, y: 42}, /// # Point { x: 42, y: 123}, /// ]; - /// # fn f() { - /// let mut file = File::open("my_file.txt").unwrap(); + /// # fn f() -> Result<(), fixcol::error::Error> { + /// let mut file = File::open("my_file.txt")?; + /// # Ok(()) /// # } /// # let mut file: Vec = Vec::new(); /// @@ -202,6 +203,7 @@ pub trait ReadFixed { /// use std::io; /// /// use fixcol::ReadFixed; + /// use fixcol::error::Error; /// /// #[derive(ReadFixed)] /// struct Foo { @@ -212,9 +214,10 @@ pub trait ReadFixed { /// } /// /// let mut buffer: &[u8] = "foobar".as_bytes(); - /// let my_foo: Foo = Foo::read_fixed(&mut buffer).unwrap(); - /// # assert_eq!(my_foo.foo, "foo".to_string()); - /// # assert_eq!(my_foo.bar, "bar".to_string()); + /// let res: Result = Foo::read_fixed(&mut buffer); + /// # let foo = res.unwrap(); + /// # assert_eq!(foo.foo, "foo".to_string()); + /// # assert_eq!(foo.bar, "bar".to_string()); /// ``` fn read_fixed(buf: &mut R) -> Result where @@ -236,16 +239,19 @@ pub trait ReadFixed { /// // ... /// } /// - /// # fn f() { - /// let mut file = File::open("my_file.txt").unwrap(); + /// # fn f() -> Result<(), fixcol::error::Error> { + /// let mut file = File::open("my_file.txt")?; /// for res in MyType::read_fixed_all(file) { /// match res { - /// Ok(my_type) => // my_type is of type MyType ... do something with it here - /// # {}, - /// Err(_) => // handle error - /// # {}, + /// Ok(my_type) => { + /// // my_type is of type MyType ... do something with it here + /// } + /// Err(_) => { + /// // handle error + /// } /// } /// } + /// # Ok(()) /// # } /// ``` fn read_fixed_all(buf: R) -> Iter @@ -276,12 +282,17 @@ pub trait ReadFixed { /// y: u8, /// } /// - /// let point = Point::read_fixed_str(" 42 7").unwrap(); + /// # fn f() -> Result<(), fixcol::error::Error> { + /// let point = Point::read_fixed_str(" 42 7")?; /// assert_eq!(point.x, 42); - /// assert_eq!(point.y, 7) + /// assert_eq!(point.y, 7); + /// # Ok(()) + /// # } + /// # assert!(f().is_ok()); /// ``` /// /// It can also be useful to pull directly from slices. + /// /// ``` /// # use fixcol::{FixedDeserializer, FieldDescription, ReadFixed}; /// # #[derive(ReadFixed)] @@ -291,11 +302,16 @@ pub trait ReadFixed { /// # #[fixcol(width=3)] /// # y: u8, /// # } + /// # + /// # fn f() -> Result<(), fixcol::error::Error> { /// let s = ">>12361 <<"; - /// let point = Point::read_fixed_str(&s[2..8]).unwrap(); + /// let point = Point::read_fixed_str(&s[2..8])?; /// /// assert_eq!(point.x, 123); /// assert_eq!(point.y, 61); + /// # Ok(()) + /// # } + /// # assert!(f().is_ok()); /// ``` fn read_fixed_str(s: &str) -> Result where @@ -325,10 +341,14 @@ pub trait ReadFixed { /// y: u8, /// } /// + /// # fn f() -> Result<(), fixcol::error::Error> { /// let s = String::from(" 42 7"); - /// let point = Point::read_fixed_string(s).unwrap(); + /// let point = Point::read_fixed_string(s)?; /// assert_eq!(point.x, 42); - /// assert_eq!(point.y, 7) + /// assert_eq!(point.y, 7); + /// # Ok(()) + /// # } + /// # assert!(f().is_ok()); /// ``` fn read_fixed_string(s: String) -> Result where diff --git a/src/parse.rs b/src/parse.rs index 0c3f585..8f61642 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -56,7 +56,11 @@ use crate::ReadFixed; /// pub eye_color: EyeColor, /// } /// -/// let person = Person::read_fixed_str("Harold 42Gr").unwrap(); +/// # fn f() -> Result<(), fixcol::error::Error> { +/// let person = Person::read_fixed_str("Harold 42Gr")?; +/// # Ok(()) +/// # } +/// # let person = Person::read_fixed_str("Harold 42Gr").unwrap(); /// assert_eq!(person.eye_color, EyeColor::Green); /// ``` /// @@ -101,8 +105,9 @@ use crate::ReadFixed; /// } /// /// // Note we are being sloppy with error handling to keep the example simple -/// # fn f() { -/// let mut file = File::open("my_file.txt").unwrap(); +/// # fn f() -> Result<(), fixcol::error::Error> { +/// let mut file = File::open("my_file.txt")?; +/// # Ok(()) /// # } /// # let mut file = "George 1989 3 12\nClaire 2001 11 26".as_bytes(); /// let people: Vec = Person::read_fixed_all(file)