blob: 4ee6364eeb8fc02c3a732b32ffa648a368c770a0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
//! HFile record types for MDT (Metadata Table) operations.
//!
//! This module provides simple, owned record types for HFile key-value pairs.
//! These are designed for use in MDT operations where:
//! - Records need to be passed around and stored
//! - Key-based lookups and merging are primary operations
//! - Values are Avro-serialized payloads decoded on demand
//!
//! Unlike the `KeyValue` type which references into file bytes,
//! `HFileRecord` owns its data and can be freely moved.
use std::cmp::Ordering;
/// An owned HFile record with key and value.
///
/// This is a simple struct designed for MDT operations. The key is the
/// UTF-8 record key (content only, without HFile key structure), and
/// the value is the raw bytes (typically Avro-serialized payload).
///
/// # Example
/// ```ignore
/// let record = HFileRecord::new("my-key".into(), value_bytes);
/// println!("Key: {}", record.key_as_str());
/// // Decode value on demand
/// let payload = decode_avro(&record.value);
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HFileRecord {
/// Record key (UTF-8 string content only, no length prefix)
pub key: Vec<u8>,
/// Record value (raw bytes, typically Avro-serialized)
pub value: Vec<u8>,
}
impl HFileRecord {
/// Create a new HFile record.
pub fn new(key: Vec<u8>, value: Vec<u8>) -> Self {
Self { key, value }
}
/// Create a record from string key and value bytes.
pub fn from_str_key(key: &str, value: Vec<u8>) -> Self {
Self {
key: key.as_bytes().to_vec(),
value,
}
}
/// Returns the key as a UTF-8 string.
///
/// Returns `None` if the key is not valid UTF-8.
pub fn key_as_str(&self) -> Option<&str> {
std::str::from_utf8(&self.key).ok()
}
/// Returns the key as bytes.
pub fn key(&self) -> &[u8] {
&self.key
}
/// Returns the value as bytes.
pub fn value(&self) -> &[u8] {
&self.value
}
/// Returns whether this record represents a deletion.
///
/// In MDT, a deleted record has an empty value.
pub fn is_deleted(&self) -> bool {
self.value.is_empty()
}
}
impl PartialOrd for HFileRecord {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for HFileRecord {
fn cmp(&self, other: &Self) -> Ordering {
self.key.cmp(&other.key)
}
}
impl std::fmt::Display for HFileRecord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.key_as_str() {
Some(key) => write!(
f,
"HFileRecord{{key={}, value_len={}}}",
key,
self.value.len()
),
None => write!(
f,
"HFileRecord{{key=<binary {} bytes>, value_len={}}}",
self.key.len(),
self.value.len()
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hfile_record_creation() {
let record = HFileRecord::new(b"test-key".to_vec(), b"test-value".to_vec());
assert_eq!(record.key_as_str(), Some("test-key"));
assert_eq!(record.value(), b"test-value");
assert!(!record.is_deleted());
}
#[test]
fn test_hfile_record_from_str() {
let record = HFileRecord::from_str_key("my-key", b"my-value".to_vec());
assert_eq!(record.key_as_str(), Some("my-key"));
assert_eq!(record.value(), b"my-value");
}
#[test]
fn test_hfile_record_deleted() {
let record = HFileRecord::new(b"deleted-key".to_vec(), vec![]);
assert!(record.is_deleted());
}
#[test]
fn test_hfile_record_ordering() {
let r1 = HFileRecord::from_str_key("aaa", vec![1]);
let r2 = HFileRecord::from_str_key("bbb", vec![2]);
let r3 = HFileRecord::from_str_key("aaa", vec![3]);
assert!(r1 < r2);
assert_eq!(r1.cmp(&r3), Ordering::Equal); // Same key, ordering only by key
}
#[test]
fn test_hfile_record_display() {
let record = HFileRecord::from_str_key("test", b"value".to_vec());
let display = format!("{}", record);
assert!(display.contains("test"));
assert!(display.contains("value_len=5"));
}
}