use std::fs;
use std::path::Path;
use ctddump::handle_dispatch;
use polars::prelude::*;
fn dispatch(args: &[&str]) {
handle_dispatch(&args.iter().map(|s| s.to_string()).collect::<Vec<_>>())
.expect("dropna should succeed");
}
fn profiles(path: &Path) -> Vec<(String, u32)> {
let df = ParquetReader::new(fs::File::open(path).unwrap()).finish().unwrap();
let pc = df.column("platform_code").unwrap().str().unwrap();
let pn = df.column("profile_no").unwrap().u32().unwrap();
let mut pairs: Vec<(String, u32)> = pc
.into_iter()
.zip(pn)
.map(|(c, n)| (c.unwrap().to_string(), n.unwrap()))
.collect();
pairs.sort();
pairs.dedup();
pairs
}
fn write_fixture(path: &Path) {
let n = f32::NAN;
let mut df = DataFrame::new(vec![
Series::new(
"platform_code".into(),
vec!["A", "A", "A", "A", "B", "B", "B", "B", "C", "C"],
),
Series::new("profile_no".into(), vec![1u32, 1, 2, 2, 1, 1, 2, 2, 1, 1]),
Series::new("observation_no".into(), vec![1u32, 2, 1, 2, 1, 2, 1, 2, 1, 2]),
Series::new("temp".into(), vec![n, 2.0, 3.0, 4.0, n, n, 7.0, 8.0, 9.0, n]),
Series::new("psal".into(), vec![10.0, 11.0, n, n, 12.0, 13.0, 14.0, 15.0, n, 16.0]),
Series::new("pres".into(), vec![0.0, 5.0, 0.0, 5.0, 0.0, 5.0, n, n, 0.0, n]),
])
.unwrap();
let f = fs::File::create(path).unwrap();
ParquetWriter::new(f).finish(&mut df).unwrap();
}
#[test]
fn test_dropna_drops_all_na_params() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("in.parquet");
let out = dir.path().join("out.parquet");
write_fixture(&src);
dispatch(&["dropna", src.to_str().unwrap(), out.to_str().unwrap()]);
assert_eq!(profiles(&out), vec![("A".to_string(), 1), ("C".to_string(), 1)]);
}
#[test]
fn test_dropna_preserves_partial_na_rows() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("in.parquet");
let out = dir.path().join("out.parquet");
write_fixture(&src);
dispatch(&["dropna", src.to_str().unwrap(), out.to_str().unwrap()]);
let df = ParquetReader::new(fs::File::open(&out).unwrap()).finish().unwrap();
assert_eq!(df.height(), 4);
}
#[test]
fn test_dropna_streaming_merges_across_chunks() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("in.parquet");
let out = dir.path().join("out.parquet");
write_fixture(&src);
std::env::set_var("CTDDUMP_CHUNK_ROWS", "1");
dispatch(&["dropna", src.to_str().unwrap(), out.to_str().unwrap()]);
std::env::remove_var("CTDDUMP_CHUNK_ROWS");
assert_eq!(profiles(&out), vec![("A".to_string(), 1), ("C".to_string(), 1)]);
}
#[test]
fn test_dropna_all_dropped_is_valid_empty() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("in.parquet");
let out = dir.path().join("out.parquet");
let n = f32::NAN;
let mut df = DataFrame::new(vec![
Series::new("platform_code".into(), vec!["A", "A"]),
Series::new("profile_no".into(), vec![1u32, 1]),
Series::new("observation_no".into(), vec![1u32, 2]),
Series::new("temp".into(), vec![n, n]), Series::new("psal".into(), vec![10.0f32, 11.0]),
Series::new("pres".into(), vec![0.0f32, 5.0]),
])
.unwrap();
ParquetWriter::new(fs::File::create(&src).unwrap()).finish(&mut df).unwrap();
dispatch(&["dropna", src.to_str().unwrap(), out.to_str().unwrap()]);
let out_df = ParquetReader::new(fs::File::open(&out).unwrap()).finish().unwrap();
assert_eq!(out_df.height(), 0);
assert!(out_df.get_column_names().iter().any(|c| c.as_str() == "temp"));
}