reifydb-codec 0.9.0

Unified encoding and decoding for all ReifyDB values, frames, rows, keys, and boundaries
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026 ReifyDB

use reifydb_codec::row::shape::{RowFamily, RowShape};
use reifydb_value::value::{duration::Duration, value_type::ValueType};

#[test]
fn test_set_get_duration() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);
	let mut row = shape.allocate_pod();

	let value = Duration::from_seconds(-7200).unwrap();
	shape.set::<Duration>(&mut row, 0, value.clone());
	assert_eq!(shape.get::<Duration>(&row, 0), value);
}

#[test]
fn test_try_get_duration() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);
	let mut row = shape.allocate_pod();

	assert_eq!(shape.try_get::<Duration>(&row, 0), None);

	let test_duration = Duration::from_days(30).unwrap();
	shape.set::<Duration>(&mut row, 0, test_duration.clone());
	assert_eq!(shape.try_get::<Duration>(&row, 0), Some(test_duration));
}

#[test]
fn test_zero() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);
	let mut row = shape.allocate_pod();

	let zero = Duration::default(); // Zero duration
	shape.set::<Duration>(&mut row, 0, zero.clone());
	assert_eq!(shape.get::<Duration>(&row, 0), zero);
}

#[test]
fn test_various_durations() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);

	let test_durations = [
		Duration::from_seconds(0).unwrap(),     // Zero
		Duration::from_seconds(60).unwrap(),    // 1 minute
		Duration::from_seconds(3600).unwrap(),  // 1 hour
		Duration::from_seconds(86400).unwrap(), // 1 day
		Duration::from_days(7).unwrap(),        // 1 week
		Duration::from_days(30).unwrap(),       // ~1 month
		Duration::from_weeks(52).unwrap(),      // ~1 year
	];

	for duration in test_durations {
		let mut row = shape.allocate_pod();
		shape.set::<Duration>(&mut row, 0, duration.clone());
		assert_eq!(shape.get::<Duration>(&row, 0), duration);
	}
}

#[test]
fn test_negative_durations() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);

	let negative_durations = [
		Duration::from_seconds(-60).unwrap(),    // -1 minute
		Duration::from_seconds(-3600).unwrap(),  // -1 hour
		Duration::from_seconds(-86400).unwrap(), // -1 day
		Duration::from_days(-7).unwrap(),        // -1 week
		Duration::from_weeks(-4).unwrap(),       // -1 month
	];

	for duration in negative_durations {
		let mut row = shape.allocate_pod();
		shape.set::<Duration>(&mut row, 0, duration.clone());
		assert_eq!(shape.get::<Duration>(&row, 0), duration);
	}
}

#[test]
fn test_complex_parts() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);
	let mut row = shape.allocate_pod();

	// All three components non-zero, so a wrong field offset shows up as a swap.
	let complex_duration = Duration::new(
		6,         // 6 months
		15,        // 15 days
		123456789, // nanoseconds
	)
	.unwrap();
	shape.set::<Duration>(&mut row, 0, complex_duration.clone());
	assert_eq!(shape.get::<Duration>(&row, 0), complex_duration);
}

#[test]
fn test_mixed_with_other_types() {
	let shape = RowShape::testing(
		RowFamily::Pod,
		&[ValueType::Duration, ValueType::Boolean, ValueType::Duration, ValueType::Int8],
	);
	let mut row = shape.allocate_pod();

	let duration1 = Duration::from_hours(24).unwrap();
	let duration2 = Duration::from_minutes(-30).unwrap();

	shape.set::<Duration>(&mut row, 0, duration1.clone());
	shape.set::<bool>(&mut row, 1, true);
	shape.set::<Duration>(&mut row, 2, duration2.clone());
	shape.set::<i64>(&mut row, 3, 987654321i64);

	assert_eq!(shape.get::<Duration>(&row, 0), duration1);
	assert_eq!(shape.get::<bool>(&row, 1), true);
	assert_eq!(shape.get::<Duration>(&row, 2), duration2);
	assert_eq!(shape.get::<i64>(&row, 3), 987654321);
}

#[test]
fn test_undefined_handling() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration, ValueType::Duration]);
	let mut row = shape.allocate_pod();

	let duration = Duration::from_days(100).unwrap();
	shape.set::<Duration>(&mut row, 0, duration.clone());

	assert_eq!(shape.try_get::<Duration>(&row, 0), Some(duration));
	assert_eq!(shape.try_get::<Duration>(&row, 1), None);

	shape.set_none(&mut row, 0);
	assert_eq!(shape.try_get::<Duration>(&row, 0), None);
}

#[test]
fn test_large_values() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);
	let mut row = shape.allocate_pod();

	let large_duration = Duration::new(
		120,             // 10 years in months
		3650,            // ~10 years in days
		123456789012345, // Large nanosecond value
	)
	.unwrap();
	shape.set::<Duration>(&mut row, 0, large_duration.clone());
	assert_eq!(shape.get::<Duration>(&row, 0), large_duration);
}

#[test]
fn test_precision_preservation() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);
	let mut row = shape.allocate_pod();

	let precise_duration = Duration::new(
		5,         // 5 months
		20,        // 20 days
		999999999, // 999,999,999 nanoseconds
	)
	.unwrap();
	shape.set::<Duration>(&mut row, 0, precise_duration.clone());

	let retrieved = shape.get::<Duration>(&row, 0);
	assert_eq!(retrieved, precise_duration);

	let orig_months = precise_duration.get_months();
	let orig_days = precise_duration.get_days();
	let orig_nanos = precise_duration.get_nanos();
	let ret_months = retrieved.get_months();
	let ret_days = retrieved.get_days();
	let ret_nanos = retrieved.get_nanos();
	assert_eq!(orig_months, ret_months);
	assert_eq!(orig_days, ret_days);
	assert_eq!(orig_nanos, ret_nanos);
}

#[test]
fn test_common_durations() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);

	let common_durations = [
		Duration::from_seconds(1).unwrap(),  // 1 second
		Duration::from_seconds(30).unwrap(), // 30 seconds
		Duration::from_minutes(5).unwrap(),  // 5 minutes
		Duration::from_minutes(15).unwrap(), // 15 minutes
		Duration::from_hours(1).unwrap(),    // 1 hour
		Duration::from_hours(8).unwrap(),    // Work day
		Duration::from_days(1).unwrap(),     // 1 day
		Duration::from_weeks(1).unwrap(),    // 1 week
		Duration::from_weeks(2).unwrap(),    // 2 weeks
	];

	for duration in common_durations {
		let mut row = shape.allocate_pod();
		shape.set::<Duration>(&mut row, 0, duration.clone());
		assert_eq!(shape.get::<Duration>(&row, 0), duration);
	}
}

#[test]
fn test_boundary_values() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Duration]);

	// The extremes of each component's own width, which is where a narrowed field truncates.
	let boundary_durations = [
		Duration::new(i32::MAX, 0, 0).unwrap(), // Max months
		Duration::new(i32::MIN, 0, 0).unwrap(), // Min months
		Duration::new(0, i32::MAX, 0).unwrap(), // Max days
		Duration::new(0, i32::MIN, 0).unwrap(), // Min days
		Duration::new(0, 0, i64::MAX).unwrap(), // Max nanoseconds
		Duration::new(0, 0, i64::MIN).unwrap(), // Min nanoseconds
	];

	for duration in boundary_durations {
		let mut row = shape.allocate_pod();
		shape.set::<Duration>(&mut row, 0, duration.clone());
		assert_eq!(shape.get::<Duration>(&row, 0), duration);
	}
}

#[test]
fn test_try_get_duration_wrong_type() {
	let shape = RowShape::testing(RowFamily::Pod, &[ValueType::Boolean]);
	let mut row = shape.allocate_pod();

	shape.set::<bool>(&mut row, 0, true);

	assert_eq!(shape.try_get::<Duration>(&row, 0), None);
}