use crate::{DataContainer, FileExtension, PolarsViewError, PolarsViewResult};
use egui::Context;
use polars::prelude::*;
use rfd::AsyncFileDialog;
use std::{fs::File, io::BufWriter, path::PathBuf, sync::Arc};
use tokio::sync::oneshot;
use tracing::error;
pub async fn open_file() -> PolarsViewResult<PathBuf> {
let opt_file = AsyncFileDialog::new().pick_file().await;
opt_file
.map(|file| file.path().to_path_buf()) .ok_or_else(|| PolarsViewError::FileNotFound(PathBuf::new())) }
pub async fn save(container: Arc<DataContainer>, ctx: Context) -> PolarsViewResult<()> {
let path = container.filter.absolute_path.clone();
let file_extension = FileExtension::from_path(&path);
let (tx, rx) = oneshot::channel::<PolarsViewResult<()>>();
let _handle = tokio::task::spawn_blocking(move || {
let file = File::create(&path)?;
let mut writer = BufWriter::new(file);
let result: PolarsViewResult<()> = match (&file_extension, container.extension.as_ref()) {
(FileExtension::Csv, FileExtension::Csv) | (FileExtension::Csv, FileExtension::Missing) => CsvWriter::new(&mut writer)
.finish(&mut container.df.as_ref().clone()) .map_err(PolarsViewError::from), (FileExtension::Json, FileExtension::Json) | (FileExtension::Json, FileExtension::Missing) => JsonWriter::new(&mut writer)
.finish(&mut container.df.as_ref().clone()) .map_err(PolarsViewError::from), (FileExtension::NDJson, FileExtension::NDJson) | (FileExtension::NDJson, FileExtension::Missing) => JsonWriter::new(&mut writer)
.finish(&mut container.df.as_ref().clone()) .map_err(PolarsViewError::from), (FileExtension::Parquet, FileExtension::Parquet) | (FileExtension::Parquet, FileExtension::Missing) => {
ParquetWriter::new(&mut writer).finish(&mut container.df.as_ref().clone())?; Ok(())
}
_ => {
let file_name = path
.file_name()
.ok_or_else(|| PolarsViewError::Other("Could not get file name".into()))? .to_str() .ok_or_else(|| PolarsViewError::Other("Invalid UTF-8 in file name.".into()))?;
Err(PolarsViewError::UnsupportedFileType(format!(
"`{file_name}`: file type and extension do not match",
)))?
}
};
if tx.send(result).is_err() {
error!("The receiver has been dropped."); }
ctx.request_repaint();
Ok::<(), PolarsViewError>(()) });
let _ = rx
.await
.map_err(|e| PolarsViewError::ChannelReceive(e.to_string()))?;
Ok(()) }
pub async fn save_as(container: Arc<DataContainer>, ctx: Context) -> PolarsViewResult<()> {
let default_file_name = container
.filter
.absolute_path
.file_name() .and_then(|name| name.to_str()) .unwrap_or("dataframe.csv");
let file = AsyncFileDialog::new()
.add_filter("CSV", &["csv"]) .add_filter("Json", &["json"]) .add_filter("NDJson", &["ndjson"]) .add_filter("Parquet", &["parquet"]) .set_file_name(default_file_name) .save_file() .await;
if let Some(file) = file {
let mut df = container.df.as_ref().clone();
let (tx, rx) = oneshot::channel::<PolarsViewResult<()>>();
let _handle = tokio::task::spawn_blocking(move || {
let result: PolarsViewResult<()> = match FileExtension::from_path(file.path()) {
FileExtension::Csv => {
let delimiter = match container.filter.csv_delimiter.as_bytes().first() {
Some(byte) => *byte,
None => {
return Err(PolarsViewError::InvalidDelimiter(
container.filter.csv_delimiter.clone(),
));
}
};
let mut file = File::create(file.path())?;
CsvWriter::new(&mut file)
.with_separator(delimiter) .finish(&mut df) .map_err(PolarsViewError::from) }
FileExtension::Json => {
let mut file = File::create(file.path())?;
JsonWriter::new(&mut file)
.with_json_format(JsonFormat::Json)
.finish(&mut df)
.map_err(PolarsViewError::from) }
FileExtension::NDJson => {
let mut file = File::create(file.path())?;
JsonWriter::new(&mut file)
.with_json_format(JsonFormat::JsonLines) .finish(&mut df)
.map_err(PolarsViewError::from) }
FileExtension::Parquet => {
let mut file = File::create(file.path())?;
ParquetWriter::new(&mut file)
.finish(&mut df)
.map_err(PolarsViewError::from)?; Ok(()) }
FileExtension::Unknown(_) | FileExtension::Missing => {
Err(PolarsViewError::UnsupportedFileType(
"Unsupported file extension for saving".to_string(),
))
}
};
if tx.send(result).is_err() {
error!("The receiver has been dropped."); }
ctx.request_repaint();
Ok::<(), PolarsViewError>(()) });
let _ = rx
.await
.map_err(|e| PolarsViewError::ChannelReceive(e.to_string()))?; }
Ok(()) }