bubbles 4.2.0

Bubble integration server for powder diffraction
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>;