Skip to content

Commit 6bdf66e

Browse files
committed
feat: add MappedFields
1 parent b57742d commit 6bdf66e

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use std::collections::HashMap;
2+
3+
use serde::{Deserialize, Serialize};
4+
5+
use crate::spec::MappedField;
6+
7+
/// TODO
8+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
9+
pub struct MappedFields {
10+
fields: Vec<MappedField>,
11+
name_to_id: HashMap<String, i32>,
12+
id_to_field: HashMap<i32, MappedField>,
13+
}
14+
15+
impl MappedFields {
16+
/// Create a new [`MappedFields`].
17+
pub fn new(fields: Vec<MappedField>) -> Self {
18+
let mut name_to_id = HashMap::new();
19+
let mut id_to_field = HashMap::new();
20+
21+
for field in &fields {
22+
if let Some(id) = field.field_id() {
23+
id_to_field.insert(id, field.clone());
24+
for name in field.names() {
25+
name_to_id.insert(name.to_string(), id);
26+
}
27+
}
28+
}
29+
30+
Self {
31+
fields,
32+
name_to_id,
33+
id_to_field,
34+
}
35+
}
36+
37+
/// Get a reference to the underlying fields.
38+
pub fn fields(&self) -> &[MappedField] {
39+
&self.fields
40+
}
41+
42+
/// Get a field, by name, returning its ID if it exists, otherwise `None`.
43+
pub fn id(&self, field_name: String) -> Option<&i32> {
44+
self.name_to_id.get(&field_name)
45+
}
46+
47+
/// Get a field, by ID, returning the underyling [`MappedField`] if it exists,
48+
/// otherwise `None`.
49+
pub fn field(&self, id: i32) -> Option<&MappedField> {
50+
self.id_to_field.get(&id)
51+
}
52+
}
53+
54+
#[cfg(test)]
55+
mod test {
56+
57+
use pretty_assertions::assert_eq;
58+
59+
use super::*;
60+
61+
#[test]
62+
fn mapped_fields() {
63+
let my_fields = vec![
64+
MappedField::new(Some(1), vec!["field_one".to_string()], Vec::new()),
65+
MappedField::new(
66+
Some(2),
67+
vec!["field_two".to_string(), "field_foo".to_string()],
68+
Vec::new(),
69+
),
70+
];
71+
72+
let mapped_fields = MappedFields::new(my_fields);
73+
74+
assert!(
75+
mapped_fields.field(1000).is_none(),
76+
"Field with ID '1000' was not inserted to the collection"
77+
);
78+
assert_eq!(
79+
*mapped_fields.field(1).unwrap(),
80+
MappedField::new(Some(1), vec!["field_one".to_string()], Vec::new()),
81+
);
82+
assert_eq!(
83+
*mapped_fields.field(2).unwrap(),
84+
MappedField::new(
85+
Some(2),
86+
vec!["field_two".to_string(), "field_foo".to_string()],
87+
Vec::new(),
88+
)
89+
);
90+
91+
assert!(
92+
mapped_fields.id("not_exist".to_string()).is_none(),
93+
"Field was not expected to exist in the collection"
94+
);
95+
assert_eq!(mapped_fields.id("field_two".to_string()).cloned(), Some(2));
96+
assert_eq!(
97+
mapped_fields.id("field_foo".to_string()).cloned(),
98+
Some(2),
99+
"Field with another name shares the same ID of '2'"
100+
);
101+
assert_eq!(mapped_fields.id("field_one".to_string()).cloned(), Some(1));
102+
}
103+
}

crates/iceberg/src/spec/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
mod datatypes;
2121
mod manifest;
2222
mod manifest_list;
23+
mod mapped_fields;
2324
mod name_mapping;
2425
mod partition;
2526
mod schema;
@@ -37,6 +38,7 @@ mod view_version;
3738
pub use datatypes::*;
3839
pub use manifest::*;
3940
pub use manifest_list::*;
41+
pub use mapped_fields::*;
4042
pub use name_mapping::*;
4143
pub use partition::*;
4244
pub use schema::*;

0 commit comments

Comments
 (0)