use crate::aws::realtime::{ChunkType, VolumeIndex};
use chrono::{DateTime, Utc};
#[derive(Debug, Clone)]
pub struct ChunkIdentifier {
site: String,
volume: VolumeIndex,
name: String,
date_time: Option<DateTime<Utc>>,
}
impl ChunkIdentifier {
pub fn new(
site: String,
volume: VolumeIndex,
name: String,
date_time: Option<DateTime<Utc>>,
) -> Self {
Self {
site,
volume,
name,
date_time,
}
}
pub fn with_sequence(&self, sequence: usize) -> Self {
let name = format!(
"{}-{:03}-{}",
self.name_prefix(),
sequence,
match sequence {
1 => "S",
55 => "E",
_ => "I",
}
);
Self {
site: self.site.clone(),
volume: self.volume,
name,
date_time: None,
}
}
pub fn site(&self) -> &str {
&self.site
}
pub fn volume(&self) -> &VolumeIndex {
&self.volume
}
pub fn name(&self) -> &str {
&self.name
}
pub fn name_prefix(&self) -> &str {
&self.name[..15]
}
pub fn sequence(&self) -> Option<usize> {
self.name.split('-').nth(2).and_then(|s| s.parse().ok())
}
pub fn chunk_type(&self) -> Option<ChunkType> {
match self.name.chars().last() {
Some('S') => Some(ChunkType::Start),
Some('I') => Some(ChunkType::Intermediate),
Some('E') => Some(ChunkType::End),
_ => None,
}
}
pub fn date_time(&self) -> Option<DateTime<Utc>> {
self.date_time
}
pub fn next_chunk(&self) -> Option<NextChunk> {
let sequence = self.sequence()?;
let mut chunk_type;
if sequence < 55 {
let next_sequence = sequence + 1;
chunk_type = ChunkType::Intermediate;
if next_sequence == 55 {
chunk_type = ChunkType::End;
}
let name = format!(
"{}-{:03}-{}",
self.name_prefix(),
next_sequence,
match chunk_type {
ChunkType::Start => "S",
ChunkType::Intermediate => "I",
ChunkType::End => "E",
}
);
let next_chunk = ChunkIdentifier::new(self.site().to_string(), self.volume, name, None);
return Some(NextChunk::Sequence(next_chunk));
}
let mut volume = self.volume.as_number() + 1;
if volume > 999 {
volume = 1;
}
Some(NextChunk::Volume(VolumeIndex::new(volume)))
}
}
pub enum NextChunk {
Sequence(ChunkIdentifier),
Volume(VolumeIndex),
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::TimeZone;
#[test]
fn test_chunk_identifier() {
let site = "KTLX";
let volume = 50;
let name = "20240813-123330-014-I";
let date_time = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
let chunk = ChunkIdentifier::new(
site.to_string(),
VolumeIndex::new(volume),
name.to_string(),
Some(date_time),
);
assert_eq!(chunk.site(), site);
assert_eq!(chunk.volume().as_number(), 50);
assert_eq!(chunk.name(), name);
assert_eq!(chunk.name_prefix(), "20240813-123330");
assert_eq!(chunk.chunk_type(), Some(ChunkType::Intermediate));
assert_eq!(chunk.sequence(), Some(14));
assert_eq!(chunk.date_time(), Some(date_time));
}
#[test]
fn test_next_chunk_start() {
let site = "KTLX";
let volume = 50;
let name = "20240813-123330-001-S";
let date_time = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
let chunk = ChunkIdentifier::new(
site.to_string(),
VolumeIndex::new(volume),
name.to_string(),
Some(date_time),
);
let next_chunk = chunk.next_chunk().expect("Expected next chunk");
match next_chunk {
NextChunk::Sequence(next_chunk) => {
assert_eq!(next_chunk.site(), site);
assert_eq!(next_chunk.volume().as_number(), 50);
assert_eq!(next_chunk.name(), "20240813-123330-002-I");
assert_eq!(next_chunk.name_prefix(), "20240813-123330");
assert_eq!(next_chunk.sequence(), Some(2));
assert_eq!(next_chunk.chunk_type(), Some(ChunkType::Intermediate));
assert_eq!(next_chunk.date_time(), None);
}
_ => panic!("Expected sequence"),
}
}
#[test]
fn test_next_chunk_intermediate() {
let site = "KTLX";
let volume = 999;
let name = "20240813-123330-014-I";
let date_time = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
let chunk = ChunkIdentifier::new(
site.to_string(),
VolumeIndex::new(volume),
name.to_string(),
Some(date_time),
);
let next_chunk = chunk.next_chunk().expect("Expected next chunk");
match next_chunk {
NextChunk::Sequence(next_chunk) => {
assert_eq!(next_chunk.site(), site);
assert_eq!(next_chunk.volume().as_number(), 999);
assert_eq!(next_chunk.name(), "20240813-123330-015-I");
assert_eq!(next_chunk.name_prefix(), "20240813-123330");
assert_eq!(next_chunk.sequence(), Some(15));
assert_eq!(next_chunk.chunk_type(), Some(ChunkType::Intermediate));
assert_eq!(next_chunk.date_time(), None);
}
_ => panic!("Expected sequence"),
}
}
#[test]
fn test_next_chunk_end() {
let site = "KTLX";
let volume = 50;
let name = "20240813-123330-055-E";
let date_time = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
let chunk = ChunkIdentifier::new(
site.to_string(),
VolumeIndex::new(volume),
name.to_string(),
Some(date_time),
);
let next_chunk = chunk.next_chunk().expect("Expected next chunk");
match next_chunk {
NextChunk::Volume(next_volume) => {
assert_eq!(next_volume.as_number(), 51);
}
_ => panic!("Expected volume"),
}
}
#[test]
fn test_next_chunk_last_volume() {
let site = "KTLX";
let volume = 999;
let name = "20240813-123330-055-E";
let date_time = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
let chunk = ChunkIdentifier::new(
site.to_string(),
VolumeIndex::new(volume),
name.to_string(),
Some(date_time),
);
let next_chunk = chunk.next_chunk().expect("Expected next chunk");
match next_chunk {
NextChunk::Volume(next_volume) => {
assert_eq!(next_volume.as_number(), 1);
}
_ => panic!("Expected volume"),
}
}
#[test]
fn test_chunk_from_sequence() {
let site = "KTLX";
let volume = 50;
let name = "20240813-123330-014-I";
let date_time = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
let chunk = ChunkIdentifier::new(
site.to_string(),
VolumeIndex::new(volume),
name.to_string(),
Some(date_time),
);
let next_chunk = chunk.with_sequence(15);
assert_eq!(next_chunk.site(), site);
assert_eq!(next_chunk.volume().as_number(), 50);
assert_eq!(next_chunk.name(), "20240813-123330-015-I");
assert_eq!(next_chunk.name_prefix(), "20240813-123330");
assert_eq!(next_chunk.sequence(), Some(15));
assert_eq!(next_chunk.chunk_type(), Some(ChunkType::Intermediate));
assert_eq!(next_chunk.date_time(), None);
assert_eq!(chunk.with_sequence(1).chunk_type(), Some(ChunkType::Start));
assert_eq!(chunk.with_sequence(55).chunk_type(), Some(ChunkType::End));
}
}