gef-file-to-map 0.1.1__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,13 +2,44 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.2.0] - 2025-03-10
6
+
7
+ ### Bug Fixes
8
+
9
+ #### Deps
10
+
11
+ - Update nom to 8
12
+
13
+
14
+ ### Refactor
15
+
16
+ #### Deps
17
+
18
+ - Migrate nom to winnow 0.3
19
+
20
+ #### Parsing
21
+
22
+ - [**breaking**] Improve parsing of headers and add better error reporting
23
+
24
+
5
25
  ## [0.1.1] - 2025-03-10
6
26
 
27
+ ### Bug Fixes
28
+
29
+ #### Ci
30
+
31
+ - Fix publishing CI
32
+
33
+
7
34
  ### Documentation
8
35
 
9
36
 
10
37
  ### Miscellaneous Tasks
11
38
 
39
+ #### Ci
40
+
41
+ - Only run release CI on tags
42
+
12
43
 
13
44
  ## [0.1.0] - 2023-02-22
14
45
 
@@ -22,12 +22,12 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
22
22
 
23
23
  [[package]]
24
24
  name = "gef-file-to-map"
25
- version = "0.1.1"
25
+ version = "0.2.0"
26
26
  dependencies = [
27
27
  "itertools",
28
- "nom",
29
28
  "pyo3",
30
29
  "thiserror",
30
+ "winnow",
31
31
  ]
32
32
 
33
33
  [[package]]
@@ -72,22 +72,6 @@ dependencies = [
72
72
  "autocfg",
73
73
  ]
74
74
 
75
- [[package]]
76
- name = "minimal-lexical"
77
- version = "0.2.1"
78
- source = "registry+https://github.com/rust-lang/crates.io-index"
79
- checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
80
-
81
- [[package]]
82
- name = "nom"
83
- version = "7.1.3"
84
- source = "registry+https://github.com/rust-lang/crates.io-index"
85
- checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
86
- dependencies = [
87
- "memchr",
88
- "minimal-lexical",
89
- ]
90
-
91
75
  [[package]]
92
76
  name = "once_cell"
93
77
  version = "1.20.3"
@@ -229,3 +213,12 @@ name = "unindent"
229
213
  version = "0.2.4"
230
214
  source = "registry+https://github.com/rust-lang/crates.io-index"
231
215
  checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
216
+
217
+ [[package]]
218
+ name = "winnow"
219
+ version = "0.7.3"
220
+ source = "registry+https://github.com/rust-lang/crates.io-index"
221
+ checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
222
+ dependencies = [
223
+ "memchr",
224
+ ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "gef-file-to-map"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  edition = "2024"
5
5
  rust-version = "1.85.0"
6
6
 
@@ -11,9 +11,9 @@ crate-type = ["cdylib"]
11
11
 
12
12
  [dependencies]
13
13
  itertools = "0.14.0"
14
- nom = "7.1.3"
15
14
  pyo3 = { version = "0.24.0", features = ["extension-module", "abi3-py37"] }
16
15
  thiserror = "2.0.12"
16
+ winnow = "0.7.3"
17
17
 
18
18
  [profile.release]
19
19
  lto = true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gef-file-to-map
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gef-file-to-map"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  description = "Utility library for parsing .gef files"
5
5
  requires-python = ">=3.7"
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  //! Error primitives.
2
2
 
3
- use pyo3::{exceptions::PyException, PyErr};
3
+ use pyo3::{PyErr, exceptions::PyException};
4
4
  use std::convert::From;
5
5
 
6
6
  /// Alias result to always return our own error type.
@@ -1,12 +1,14 @@
1
1
  //! Header parsing.
2
2
 
3
- use crate::{
4
- error::{Error, Result},
5
- nom::IResult,
3
+ use winnow::{
4
+ ascii::*,
5
+ combinator::*,
6
+ error::{StrContext, StrContextValue},
7
+ prelude::*,
8
+ token::*,
6
9
  };
7
10
 
8
- /// Separator used between values in the GEF file.
9
- const VALUE_SEPARATOR: char = ',';
11
+ use crate::error::{Error, Result};
10
12
 
11
13
  /// References to the strings of the file where the header is and it's values.
12
14
  #[derive(Debug, Clone, PartialEq)]
@@ -27,69 +29,71 @@ impl<'a> Header<'a> {
27
29
  ///
28
30
  /// The string shouldn't contain a '#' character at the begin nor a newline
29
31
  /// at the end.
30
- fn from_str(s: &'a str) -> IResult<'a, Self> {
31
- // Get the name of the header, the left hand side
32
- let (s, name) = nom::error::context(
33
- "the name of the header",
34
- nom::character::complete::alphanumeric1,
35
- )(s)?;
36
-
37
- let (s, values) = nom::sequence::preceded(
38
- // Take all whitespace between the column header name and the = symbol
39
- nom::character::complete::space0,
40
- // Get the values between the '=' char and the end of the line
41
- nom::sequence::preceded(
42
- // Take all spaces and = characters
43
- nom::sequence::preceded(
44
- nom::character::complete::char('='),
45
- nom::character::complete::space0,
46
- ),
47
- nom::error::context(
48
- "the header values",
49
- // Get the comma-space separated values
50
- nom::multi::separated_list0(
51
- // Get until the ',' character and trim the spaces
52
- nom::sequence::delimited(
53
- nom::character::complete::space0,
54
- nom::character::complete::char(VALUE_SEPARATOR),
55
- nom::character::complete::space0,
56
- ),
57
- // Take until the end of the line or until a separator is found
58
- nom::bytes::complete::take_till(|c: char| {
59
- c == VALUE_SEPARATOR || c.is_control()
60
- }),
61
- ),
62
- ),
63
- ),
64
- )(s)?;
65
-
66
- // Make empty lists actually empty
67
- let values = if values == [""] { Vec::new() } else { values };
68
-
69
- Ok((s, Self { name, values }))
32
+ fn from_str(input: &mut &'a str) -> winnow::Result<Self> {
33
+ // Take the hash symbol at the beginning and the whitespace
34
+ ('#', space0)
35
+ .context(StrContext::Label("header hash"))
36
+ .parse_next(input)?;
37
+
38
+ // Take the name
39
+ let name = alphanumeric1
40
+ .context(StrContext::Label("header name"))
41
+ .parse_next(input)?;
42
+
43
+ // Ignore the '=' symbol, although it is required
44
+ (space0, '=', space0)
45
+ .context(StrContext::Label("header equality symbol"))
46
+ .parse_next(input)?;
47
+
48
+ // Get the comma-space separated values
49
+ let mut values: Vec<&str> = separated(
50
+ 0..,
51
+ // Get until the ',' character and trim the spaces
52
+ delimited(space0, take_till(1.., (',', '\r', '\n')), space0)
53
+ .context(StrContext::Label("header value")),
54
+ ',',
55
+ )
56
+ .context(StrContext::Label("header values"))
57
+ .context(StrContext::Expected(StrContextValue::Description(
58
+ "comma separated list of values",
59
+ )))
60
+ .parse_next(input)?;
61
+
62
+ // Remove empty values
63
+ if values == [""] {
64
+ values.clear();
65
+ }
66
+
67
+ // Take the newline
68
+ (space0, take_while(0.., ('\n', '\r'))).parse_next(input)?;
69
+
70
+ Ok(Self { name, values })
70
71
  }
71
72
  }
72
73
 
73
74
  /// Parse the headers of the GEF file.
74
75
  ///
75
76
  /// Return the parsed headers and a reference to the rest of the file.
76
- pub(crate) fn parse_headers(gef: &'_ str) -> Result<(&'_ str, Vec<Header<'_>>)> {
77
- nom::sequence::preceded(
78
- // Ignore the whitespace before the first header line
79
- nom::character::complete::multispace0,
80
- // Loop over all sequences starting with # until the newline character
81
- nom::multi::many0(nom::error::context(
82
- "a header line",
83
- nom::sequence::delimited(
84
- nom::character::complete::char('#'),
85
- Header::from_str,
86
- // Allow multiple lines
87
- nom::multi::many1(nom::character::complete::line_ending),
88
- ),
89
- )),
90
- )(gef)
91
- // Convert the nom error to our own error type
92
- .map_err(|err| Error::Parsing(err.to_string()))
77
+ pub(crate) fn parse_headers(mut gef: &'_ str) -> Result<(&'_ str, Vec<Header<'_>>)> {
78
+ parse_headers_impl
79
+ .parse_next(&mut gef)
80
+ // Convert the winnow error to our own error type
81
+ .map_err(|err| Error::Parsing(err.to_string()))
82
+ }
83
+
84
+ /// Parse the list of headers implementation.
85
+ pub(crate) fn parse_headers_impl<'a>(
86
+ input: &mut &'a str,
87
+ ) -> winnow::Result<(&'a str, Vec<Header<'a>>)> {
88
+ // Skip initial whitespace
89
+ multispace0.parse_next(input)?;
90
+
91
+ // Loop over all sequences starting with # until the newline character
92
+ let (values, _) = repeat_till(0.., Header::from_str, not('#'))
93
+ .context(StrContext::Label("headers"))
94
+ .parse_next(input)?;
95
+
96
+ Ok((input, values))
93
97
  }
94
98
 
95
99
  #[cfg(test)]
@@ -99,7 +103,7 @@ mod tests {
99
103
  #[test]
100
104
  fn test_single_header() {
101
105
  assert_eq!(
102
- Header::from_str("A= 1").unwrap().1,
106
+ Header::from_str(&mut "#A= 1").unwrap(),
103
107
  Header {
104
108
  name: "A",
105
109
  values: vec!["1"]
@@ -107,7 +111,7 @@ mod tests {
107
111
  );
108
112
 
109
113
  assert_eq!(
110
- Header::from_str("A = 1").unwrap().1,
114
+ Header::from_str(&mut "#A = 1").unwrap(),
111
115
  Header {
112
116
  name: "A",
113
117
  values: vec!["1"]
@@ -115,7 +119,7 @@ mod tests {
115
119
  );
116
120
 
117
121
  assert_eq!(
118
- Header::from_str("A= 1, 2").unwrap().1,
122
+ Header::from_str(&mut "#A= 1, 2").unwrap(),
119
123
  Header {
120
124
  name: "A",
121
125
  values: vec!["1", "2"]
@@ -126,7 +130,7 @@ mod tests {
126
130
  #[test]
127
131
  fn test_empty_header() {
128
132
  assert_eq!(
129
- Header::from_str("A=").unwrap().1,
133
+ Header::from_str(&mut "#A=").unwrap(),
130
134
  Header {
131
135
  name: "A",
132
136
  values: vec![]
@@ -134,7 +138,6 @@ mod tests {
134
138
  );
135
139
 
136
140
  let (rest, headers) = parse_headers("#A=\nrest").unwrap();
137
- assert_eq!(rest, "rest");
138
141
  assert_eq!(
139
142
  headers,
140
143
  vec![Header {
@@ -142,6 +145,7 @@ mod tests {
142
145
  values: vec![]
143
146
  },]
144
147
  );
148
+ assert_eq!(rest, "rest");
145
149
  }
146
150
 
147
151
  #[test]
@@ -15,7 +15,6 @@
15
15
 
16
16
  pub mod error;
17
17
  mod header;
18
- mod nom;
19
18
 
20
19
  use itertools::Itertools;
21
20
  use pyo3::prelude::*;
@@ -1,7 +0,0 @@
1
- //! Tools and utilities for the nom crate.
2
-
3
- use nom::error::VerboseError;
4
-
5
- /// Remove a lot of boilerplate for nom, use the verbose error type for span
6
- /// information.
7
- pub(crate) type IResult<'a, T> = nom::IResult<&'a str, T, VerboseError<&'a str>>;
File without changes