lasprs 0.14.0

Library for Acoustic Signal Processing (Rust edition, with optional Python bindings via pyo3)
use crate::measurement::{Measurement, MeasurementError, SharedMeasurement, error::*};
use parking_lot::{Mutex, RwLock};
use std::{
    collections::HashMap,
    ops::Deref,
    path::Path,
    sync::{Arc, LazyLock, Weak},
};
use uuid::Uuid;

/// Weak reference to a measurement
pub struct WeakMeasurement(Weak<RwLock<Measurement>>);
impl Deref for WeakMeasurement {
    type Target = Weak<RwLock<Measurement>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

// Store weak references to open measurements
static OPEN_MEASUREMENTS: LazyLock<Mutex<HashMap<Uuid, WeakMeasurement>>> =
    LazyLock::new(|| Mutex::new(HashMap::new()));

fn cleanup_closed_measurements() {
    let mut lck = OPEN_MEASUREMENTS.lock();
    lck.retain(|_, measurement| measurement.0.upgrade().is_some());
}

/// Find an open measurement by its UUID.
///
/// Returns a shared reference to the measurement if already open, otherwise
/// None.
pub fn find_open_measurement_by_uuid(uuid: &Uuid) -> Option<SharedMeasurement> {
    cleanup_closed_measurements();
    if let Some(measurement) = OPEN_MEASUREMENTS.lock().get(uuid)
        && let Some(shared) = measurement.0.upgrade()
    {
        return Some(SharedMeasurement(shared));
    }
    None
}
/// Find an open measurement by its path. File path should be absolute, panics
/// if its not.
pub fn find_open_measurement_by_path(path: &Path) -> Option<SharedMeasurement> {
    let mut lck = OPEN_MEASUREMENTS.lock();
    let mut result = None;
    if !path.is_absolute() {
        panic!("path must be absolute");
    }
    // Iterate over the measurements, removes any measurements that are no
    // longer open as well.
    lck.retain(|_, meas| {
        if let Some(meas) = meas.0.upgrade() {
            if meas.read().filepath() == path {
                result = Some(SharedMeasurement(meas));
            }
            true
        } else {
            false
        }
    });
    result
}
/// Add new open measurement to list of open measurements. Should only be called
/// when Measurement::fromFile() has to create a new shared measurement object.
///
/// Returns - Some(SharedMeasurement) in case the same measurement with the
/// given UUID was already open.
///
/// # Errors
///  Returns an error if the measurement has a different
/// filename, but another measurement with the same UUID is already open.
///
pub fn add_new_open_measurement(
    m: &SharedMeasurement,
) -> std::result::Result<Option<SharedMeasurement>, MeasurementError> {
    cleanup_closed_measurements();
    let m_readlock = m.read();
    let uuid = m_readlock.uuid();
    let filepath = m_readlock.filepath();

    // Add it to the list of open measurements, returns existing measurement if
    // it exists
    let mut open_meas = OPEN_MEASUREMENTS.lock();
    if let Some(existing) = open_meas.get(uuid) {
        if let Some(existing) = existing.upgrade() {
            if existing.read().filepath() == filepath {
                Ok(Some(SharedMeasurement(existing)))
            } else {
                DuplicateUUIDSnafu {
                    path1: existing.read().filepath().to_string_lossy(),
                    path2: m_readlock.filepath().to_string_lossy(),
                    uuid: uuid.to_string(),
                }
                .fail()
            }
        } else {
            Ok(None)
        }
    } else {
        open_meas.insert(*uuid, WeakMeasurement(Arc::downgrade(&m.0)));
        Ok(None)
    }
}