use cryiorust::frame::{Array, FrameError};
use integrustio::integrator::Pattern;
use itertools::izip;
use std::fmt::{Debug, Formatter};
use std::path::PathBuf;
use std::{fmt, fs, io};
pub const KEY_DONT_TOUCH: &'static str = "Bubble_normalized";
pub const KEY_DUBBLE_MONITOR: &'static str = "Monitor";
pub const KEY_DUBBLE_PHOTO: &'static str = "Photo";
pub const KEY_BUBBLE_CAKE: &'static str = "Bubble_cake";
pub trait Decompressor {
fn decompress(&self) -> io::Result<Vec<u8>>;
}
impl Decompressor for str {
fn decompress(&self) -> io::Result<Vec<u8>> {
let packed = match base64::decode(self) {
Ok(packed) => packed,
Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
};
match zstd::decode_all(packed.as_slice()) {
Ok(decompressed) => Ok(decompressed),
Err(e) => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("could not decompress: {:?}", e),
)),
}
}
}
pub trait Compressor {
fn compress(&self) -> String;
}
impl Compressor for [u8] {
fn compress(&self) -> String {
let packed = match zstd::encode_all(self, 0) {
Ok(p) => p,
Err(err) => {
error!("Failed to pack data with zstd: {}", err);
return String::new();
}
};
base64::encode(packed)
}
}
pub trait Texter {
fn to_text<P: io::Write>(&self, writer: &mut P) -> io::Result<()>;
}
impl Texter for Pattern {
fn to_text<P: io::Write>(&self, writer: &mut P) -> io::Result<()> {
for (position, intensity, sigma) in
izip!(self.positions.iter(), &self.intensity, &self.sigma)
{
writeln!(writer, "{} {} {}", position, intensity, sigma)?;
}
Ok(())
}
}
pub trait Resizer {
fn resize(&self, bin: usize) -> Option<Array>;
fn bindim(bin: usize, mut dim: usize) -> usize {
while dim % bin != 0 {
dim -= 1;
}
dim / bin
}
}
impl Resizer for Array {
fn resize(&self, bin: usize) -> Option<Array> {
if bin <= 1 {
return None;
}
let binf = bin as f64;
let dim1 = Self::bindim(bin, self.dim1());
let dim2 = Self::bindim(bin, self.dim2());
let dim1bin = dim1 * bin;
let dim2bin = dim2 * bin;
let mut data = vec![0.; dim1 * dim2];
for i in 0..dim1 {
if i + bin >= dim1bin {
continue;
}
let l = i * dim2;
let ibin = i * bin;
for j in 0..dim2 {
if j + bin >= dim2bin {
continue;
}
let k = l + j;
let jbin = j * bin;
for x in 0..bin {
let offset = (ibin + x) * self.dim2() + jbin;
for y in 0..bin {
data[k] += self.data()[offset + y];
}
}
data[k] /= binf;
}
}
Some(Array::with_data(dim1, dim2, data))
}
}
pub enum WalkResult {
None(io::Error),
Dir(PathBuf),
File(PathBuf),
}
pub trait EntryProcessor {
fn process(&self) -> WalkResult;
}
impl EntryProcessor for fs::DirEntry {
fn process(&self) -> WalkResult {
let path = self.path();
match self.metadata() {
Ok(md) => {
if md.is_dir() {
WalkResult::Dir(path)
} else {
WalkResult::File(path)
}
}
Err(e) => WalkResult::None(e),
}
}
}
pub fn py_stamp() -> f64 {
chrono::Local::now().timestamp_nanos() as f64 / 1e9
}
pub enum BubbleError {
FrameError(FrameError),
AlreadyProcessed,
}
impl fmt::Display for BubbleError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
BubbleError::FrameError(fe) => Debug::fmt(&fe, f),
BubbleError::AlreadyProcessed => write!(f, "frame already processed"),
}
}
}
pub type BubbleResult<T> = Result<T, BubbleError>;