Skip to content

Commit 328f5d5

Browse files
committed
feat: introduce FieldNameIterator for field path traversal
- Add FieldNameIterator struct with domain-specific methods - Replace core::slice::Iter usage in FieldGetter trait - Implement From traits for easy creation from XPath and [String] - Update all FieldGetter implementations across gene and gene_derive - Add comprehensive documentation and examples
1 parent ac06166 commit 328f5d5

7 files changed

Lines changed: 138 additions & 37 deletions

File tree

derive/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ impl EventDerive {
213213
///
214214
/// ```rust
215215
/// use gene_derive::{Event, FieldGetter};
216-
/// use gene::{Event,FieldGetter,FieldValue};
216+
/// use gene::{Event, FieldGetter, FieldValue, FieldNameIterator};
217217
/// use std::borrow::Cow;
218218
///
219219
/// #[derive(Event, FieldGetter)]
@@ -287,7 +287,7 @@ impl FieldGetterDerive {
287287
span =>
288288
#(#fields)|* => {
289289
#[allow(clippy::redundant_closure_call)]
290-
|x: &#flt dyn FieldGetter<#flt>, i: core::slice::Iter<'_, std::string::String>| -> Option<FieldValue<#flt>> {
290+
|x: &#flt dyn FieldGetter<#flt>, i: FieldNameIterator| -> Option<FieldValue<#flt>> {
291291
x.get_from_iter(i)
292292
}(&self.#field_name, i)
293293
}});
@@ -358,14 +358,14 @@ impl FieldGetterDerive {
358358
let expand = quote! {
359359
impl #trait_generics FieldGetter<#flt> for #struct_name #generics #generic_trait_bound{
360360
#[inline(always)]
361-
fn get_from_iter(&#flt self, mut i: core::slice::Iter<'_, std::string::String>) -> Option<FieldValue<#flt>> {
361+
fn get_from_iter(&#flt self, mut i: FieldNameIterator) -> Option<FieldValue<#flt>> {
362362

363-
let field = match i.next() {
363+
let field = match i.next_field_name() {
364364
Some(s) => s,
365365
None => return Some(FieldValue::Some),
366366
};
367367

368-
match field.as_str() {
368+
match field {
369369
#(#arms)*
370370
_ => None,
371371
}

gene/benches/engine_benchmark.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
};
66

77
use criterion::{criterion_group, criterion_main, Criterion, Throughput};
8-
use gene::{Compiler, Engine, Event, FieldGetter, FieldValue, Rule};
8+
use gene::{Compiler, Engine, Event, FieldGetter, FieldValue, Rule, FieldNameIterator};
99
use gene_derive::{Event, FieldGetter};
1010
use libflate::gzip;
1111
use serde::{Deserialize, Deserializer};

gene/src/engine.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ struct RuleCacheEntry {
491491
///
492492
/// ```
493493
/// use gene_derive::{Event, FieldGetter};
494-
/// use gene::{Compiler, Engine, Event,FieldGetter,FieldValue};
494+
/// use gene::{Compiler, Engine, Event, FieldGetter, FieldValue, FieldNameIterator};
495495
/// use std::borrow::Cow;
496496
///
497497
/// #[derive(FieldGetter)]
@@ -786,14 +786,15 @@ impl Engine {
786786
#[cfg(test)]
787787
mod test {
788788
use super::*;
789+
use crate::{FieldGetter, FieldNameIterator};
789790

790791
macro_rules! fake_event {
791792
($name:tt, id=$id:literal, source=$source:literal, $(($path:literal, $value:expr)),*) => {
792793
struct $name {}
793794

794795
impl<'f> FieldGetter<'f> for $name{
795796

796-
fn get_from_iter(&'f self, _: core::slice::Iter<'_, std::string::String>) -> Option<$crate::FieldValue<'f>>{
797+
fn get_from_iter(&'f self, _: FieldNameIterator<'_>) -> Option<$crate::FieldValue<'f>>{
797798
unimplemented!()
798799
}
799800

gene/src/event.rs

Lines changed: 124 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,115 @@ use std::{
77

88
use crate::{FieldValue, XPath};
99

10+
/// An iterator over field names in an [`XPath`]-like path.
11+
///
12+
/// This iterator is used to traverse field names in structured data paths,
13+
/// such as `.parent.child.field` in jq notation. It provides methods to
14+
/// access the current field name and advance to the next one.
15+
///
16+
/// The iterator can be created from an [`XPath`] using the [`From`] trait,
17+
/// or directly from a slice of field names.
18+
///
19+
/// # Examples
20+
///
21+
/// ```
22+
/// use gene::{FieldNameIterator, XPath};
23+
///
24+
/// // From XPath
25+
/// let path = XPath::parse(".parent.child.field").unwrap();
26+
/// let mut iter = FieldNameIterator::from(&path);
27+
///
28+
/// assert_eq!(iter.next_field_name(), Some("parent"));
29+
/// assert_eq!(iter.next_field_name(), Some("child"));
30+
/// assert_eq!(iter.next_field_name(), Some("field"));
31+
/// assert_eq!(iter.next_field_name(), None);
32+
///
33+
/// // From field names directly
34+
/// let field_names = vec!["parent".to_string(), "child".to_string(), "field".to_string()];
35+
/// let mut iter = FieldNameIterator::from(field_names.as_slice());
36+
///
37+
/// assert_eq!(iter.next_field_name(), Some("parent"));
38+
/// assert_eq!(iter.next_field_name(), Some("child"));
39+
/// assert_eq!(iter.next_field_name(), Some("field"));
40+
/// assert_eq!(iter.next_field_name(), None);
41+
/// ```
42+
pub struct FieldNameIterator<'f> {
43+
i: Option<usize>,
44+
field_names: &'f [String],
45+
}
46+
47+
impl<'f> From<&'f [String]> for FieldNameIterator<'f> {
48+
fn from(value: &'f [String]) -> Self {
49+
Self {
50+
i: None,
51+
field_names: value,
52+
}
53+
}
54+
}
55+
56+
impl<'f> From<&'f XPath> for FieldNameIterator<'f> {
57+
fn from(value: &'f XPath) -> Self {
58+
value.segments().into()
59+
}
60+
}
61+
62+
impl<'f> FieldNameIterator<'f> {
63+
/// Advances the iterator and returns the next field name.
64+
///
65+
/// This method moves the iterator to the next position and returns the field name
66+
/// at that position. On the first call, it returns the first field name.
67+
/// When the iterator reaches the end, it returns `None`.
68+
///
69+
/// # Examples
70+
///
71+
/// ```
72+
/// use gene::FieldNameIterator;
73+
///
74+
/// let field_names = vec!["field1".to_string(), "field2".to_string()];
75+
/// let mut iter = FieldNameIterator::from(field_names.as_slice());
76+
///
77+
/// assert_eq!(iter.next_field_name(), Some("field1"));
78+
/// assert_eq!(iter.next_field_name(), Some("field2"));
79+
/// assert_eq!(iter.next_field_name(), None);
80+
/// ```
81+
#[inline]
82+
pub fn next_field_name(&mut self) -> Option<&str> {
83+
match self.i.as_mut() {
84+
Some(i) => {
85+
*i = i.checked_add(1)?;
86+
self.field_names.get(*i).map(|s| s.as_ref())
87+
}
88+
None => {
89+
self.i = Some(0);
90+
self.field_names.first().map(|s| s.as_ref())
91+
}
92+
}
93+
}
94+
95+
/// Checks if the iterator is at the last field name.
96+
///
97+
/// Returns `true` if the current position is at the last field name in the path,
98+
/// indicating that this is a terminal field access. Returns `false` otherwise.
99+
///
100+
/// # Examples
101+
///
102+
/// ```
103+
/// use gene::FieldNameIterator;
104+
///
105+
/// let field_names = vec!["field1".to_string(), "field2".to_string()];
106+
/// let mut iter = FieldNameIterator::from(field_names.as_slice());
107+
///
108+
/// assert_eq!(iter.next_field_name(), Some("field1"));
109+
/// assert!(!iter.is_terminal()); // At start, not terminal
110+
/// assert_eq!(iter.next_field_name(), Some("field2"));
111+
/// assert!(iter.is_terminal()); // After advancing once, at last field
112+
/// ```
113+
#[inline(always)]
114+
pub fn is_terminal(&self) -> bool {
115+
self.i.unwrap_or_default() == self.field_names.len() - 1
116+
}
117+
}
118+
10119
/// Trait representing a log event that can be scanned by the engine.
11120
///
12121
/// Events provide access to their unique identifier, source, and field values
@@ -16,7 +125,7 @@ use crate::{FieldValue, XPath};
16125
/// # Examples
17126
///
18127
/// ```
19-
/// use gene::{FieldValue, Event, FieldGetter};
128+
/// use gene::{FieldValue, Event, FieldGetter, FieldNameIterator};
20129
/// use gene_derive::{Event, FieldGetter};
21130
/// use std::borrow::Cow;
22131
///
@@ -54,7 +163,7 @@ pub trait Event<'event>: FieldGetter<'event> {
54163
/// # Examples
55164
///
56165
/// ```
57-
/// use gene::{FieldGetter, FieldValue};
166+
/// use gene::{FieldGetter, FieldValue, FieldNameIterator};
58167
/// use std::net::IpAddr;
59168
///
60169
/// struct NetworkEvent {
@@ -66,9 +175,9 @@ pub trait Event<'event>: FieldGetter<'event> {
66175
/// impl<'f> FieldGetter<'f> for NetworkEvent {
67176
/// fn get_from_iter(
68177
/// &'f self,
69-
/// mut i: core::slice::Iter<'_, std::string::String>,
178+
/// mut i: FieldNameIterator,
70179
/// ) -> Option<FieldValue<'f>> {
71-
/// match i.next().map(|s| s.as_str()) {
180+
/// match i.next_field_name() {
72181
/// Some("source_ip") => Some(self.source_ip.to_string().into()),
73182
/// Some("destination_ip") => Some(self.destination_ip.to_string().into()),
74183
/// Some("port") => Some(self.port.into()),
@@ -109,7 +218,7 @@ pub trait FieldGetter<'field> {
109218
/// override [`Self::get_from_iter`] instead of overriding this method.
110219
#[inline]
111220
fn get_from_path(&'field self, path: &XPath) -> Option<FieldValue<'field>> {
112-
self.get_from_iter(path.iter_segments())
221+
self.get_from_iter(FieldNameIterator::from(path))
113222
}
114223

115224
/// Gets a field value using an iterator of path segments.
@@ -126,19 +235,16 @@ pub trait FieldGetter<'field> {
126235
///
127236
/// * `Some(FieldValue)` if the field exists and can be accessed
128237
/// * `None` if the field does not exist or cannot be accessed
129-
fn get_from_iter(
130-
&'field self,
131-
i: core::slice::Iter<'_, std::string::String>,
132-
) -> Option<FieldValue<'field>>;
238+
fn get_from_iter(&'field self, i: FieldNameIterator<'_>) -> Option<FieldValue<'field>>;
133239
}
134240

135241
macro_rules! impl_with_getter {
136242
($(($type:ty, $getter:tt)),*) => {
137243
$(
138244
impl<'f> FieldGetter<'f> for $type {
139245
#[inline]
140-
fn get_from_iter(&'f self, i: core::slice::Iter<'_, std::string::String>) -> Option<FieldValue<'f>> {
141-
if i.len() > 0 {
246+
fn get_from_iter(&'f self, i: FieldNameIterator<'_>) -> Option<FieldValue<'f>> {
247+
if !i.is_terminal() {
142248
return None;
143249
}
144250
Some(self.$getter().into())
@@ -153,8 +259,8 @@ macro_rules! impl_for_type {
153259
$(
154260
impl<'f> FieldGetter<'f> for $type {
155261
#[inline]
156-
fn get_from_iter(&'f self, i: core::slice::Iter<'_, std::string::String>) -> Option<FieldValue<'f>> {
157-
if i.len() > 0 {
262+
fn get_from_iter(&'f self, i: FieldNameIterator<'_>) -> Option<FieldValue<'f>> {
263+
if !i.is_terminal() {
158264
return None;
159265
}
160266
Some(self.into())
@@ -196,10 +302,7 @@ where
196302
T: FieldGetter<'field>,
197303
{
198304
#[inline]
199-
fn get_from_iter(
200-
&'field self,
201-
i: core::slice::Iter<'_, std::string::String>,
202-
) -> Option<FieldValue<'field>> {
305+
fn get_from_iter(&'field self, i: FieldNameIterator<'_>) -> Option<FieldValue<'field>> {
203306
match self {
204307
Some(v) => v.get_from_iter(i),
205308
None => Some(FieldValue::None),
@@ -212,11 +315,8 @@ where
212315
T: FieldGetter<'f>,
213316
{
214317
#[inline]
215-
fn get_from_iter(
216-
&'f self,
217-
mut i: core::slice::Iter<'_, std::string::String>,
218-
) -> Option<FieldValue<'f>> {
219-
let k = match i.next() {
318+
fn get_from_iter(&'f self, mut i: FieldNameIterator<'_>) -> Option<FieldValue<'f>> {
319+
let k = match i.next_field_name() {
220320
Some(s) => s,
221321
None => {
222322
// No key to look up, return Some to indicate map existence
@@ -238,9 +338,9 @@ macro_rules! impl_field_getter_for_vec {
238338
#[inline]
239339
fn get_from_iter(
240340
&'f self,
241-
i: core::slice::Iter<'_, std::string::String>,
341+
i: FieldNameIterator<'_>,
242342
) -> Option<FieldValue<'f>> {
243-
if i.len() > 0 {
343+
if !i.is_terminal(){
244344
return None;
245345
}
246346

gene/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
//! ## Quickstart
4545
//!
4646
//! ```
47-
//! use gene::{Compiler, Engine, Event, FieldGetter, FieldValue};
47+
//! use gene::{Compiler, Engine, Event, FieldGetter, FieldValue, FieldNameIterator};
4848
//! use gene_derive::{Event, FieldGetter};
4949
//!
5050
//! // 1. Define your event structure
@@ -177,7 +177,7 @@
177177
//! Gene is licensed under the **GPL-3.0** - see the `LICENSE` file for details.
178178
179179
mod event;
180-
pub use event::{Event, FieldGetter};
180+
pub use event::{Event, FieldGetter, FieldNameIterator};
181181

182182
pub mod rules;
183183
pub use rules::Rule;

gene/src/paths.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub enum PathError {
1818
///
1919
/// ```rust
2020
/// use ::gene_derive::FieldGetter;
21-
/// use ::gene::{XPath, FieldGetter, FieldValue};
21+
/// use ::gene::{XPath, FieldGetter, FieldValue, FieldNameIterator};
2222
///
2323
/// #[derive(FieldGetter)]
2424
/// struct LogData

gene/src/rules.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ mod test {
718718
use std::path::PathBuf;
719719

720720
use super::*;
721-
use crate::{Event, FieldGetter, FieldValue};
721+
use crate::{Event, FieldGetter, FieldValue, FieldNameIterator};
722722
use gene_derive::{Event, FieldGetter};
723723

724724
macro_rules! def_event {
@@ -741,7 +741,7 @@ mod test {
741741

742742
impl<'f> FieldGetter<'f> for $name{
743743

744-
fn get_from_iter(&self, _: core::slice::Iter<'_, std::string::String>) -> Option<$crate::FieldValue<'_>>{
744+
fn get_from_iter(&self, _: FieldNameIterator<'_>) -> Option<$crate::FieldValue<'_>>{
745745
unimplemented!()
746746
}
747747

0 commit comments

Comments
 (0)