use std::ffi::{c_char, CString};
use std::fmt;
use crate::error::{check, check_nst, MSError};
use crate::{raw, MSResult};
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MSTimeFormat {
IsoMonthDay,
IsoMonthDayZ,
IsoMontDayDoy,
IsoMonthDayDoyZ,
IsoMonthDaySpace,
IsoMonthDaySpaceZ,
SeedOrdinal,
UnixEpoch,
NanoSecondEpoch,
}
impl MSTimeFormat {
pub fn as_raw(&self) -> raw::ms_timeformat_t {
use MSTimeFormat::*;
match *self {
IsoMonthDay => raw::ms_timeformat_t_ISOMONTHDAY,
IsoMonthDayZ => raw::ms_timeformat_t_ISOMONTHDAY_Z,
IsoMontDayDoy => raw::ms_timeformat_t_ISOMONTHDAY_DOY,
IsoMonthDayDoyZ => raw::ms_timeformat_t_ISOMONTHDAY_DOY_Z,
IsoMonthDaySpace => raw::ms_timeformat_t_ISOMONTHDAY_SPACE,
IsoMonthDaySpaceZ => raw::ms_timeformat_t_ISOMONTHDAY_SPACE_Z,
SeedOrdinal => raw::ms_timeformat_t_SEEDORDINAL,
UnixEpoch => raw::ms_timeformat_t_UNIXEPOCH,
NanoSecondEpoch => raw::ms_timeformat_t_NANOSECONDEPOCH,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MSSubSeconds {
None,
Micro,
Nano,
MicroNone,
NanoNone,
NanoMicro,
NanoMicroNone,
}
impl MSSubSeconds {
pub fn as_raw(&self) -> raw::ms_subseconds_t {
use MSSubSeconds::*;
match *self {
None => raw::ms_subseconds_t_NONE,
Micro => raw::ms_subseconds_t_MICRO,
Nano => raw::ms_subseconds_t_NANO,
MicroNone => raw::ms_subseconds_t_MICRO_NONE,
NanoNone => raw::ms_subseconds_t_NANO_NONE,
NanoMicro => raw::ms_subseconds_t_NANO_MICRO,
NanoMicroNone => raw::ms_subseconds_t_NANO_MICRO_NONE,
}
}
}
pub fn nstime_to_time(nst: i64) -> MSResult<time::OffsetDateTime> {
let mut year = 0;
let mut yday = 0;
let mut hour = 0;
let mut min = 0;
let mut sec = 0;
let mut nsec = 0;
unsafe {
check(raw::ms_nstime2time(
nst, &mut year, &mut yday, &mut hour, &mut min, &mut sec, &mut nsec,
))?
};
let date = time::Date::from_ordinal_date(year.into(), yday)
.map_err(|e| MSError::from_str(&e.to_string()))?;
let datetime = date
.with_hms_nano(hour, min, sec, nsec)
.map_err(|e| MSError::from_str(&e.to_string()))?;
Ok(datetime.assume_utc())
}
pub fn nstime_to_string(
nst: i64,
time_format: MSTimeFormat,
subsecond_format: MSSubSeconds,
) -> MSResult<String> {
let time = CString::new(" ")
.unwrap()
.into_raw();
unsafe {
if raw::ms_nstime2timestr(nst, time, time_format.as_raw(), subsecond_format.as_raw())
.is_null()
{
return Err(MSError::from_str("failed to convert nstime to string"));
}
Ok(CString::from_raw(time).into_string().unwrap())
}
}
pub fn time_to_nstime(t: &time::OffsetDateTime) -> i64 {
unsafe {
check_nst(raw::ms_time2nstime(
t.year(),
t.ordinal().into(),
t.hour().into(),
t.minute().into(),
t.second().into(),
t.nanosecond(),
))
}
.unwrap()
}
pub(crate) fn to_string(buf: &[c_char]) -> String {
let v: Vec<u8> = buf
.iter()
.map(|x| *x as u8) .filter(|x| *x != 0u8) .collect();
String::from_utf8_lossy(&v).to_string()
}
#[derive(Debug, Clone)]
pub(crate) struct NetStaLocCha {
pub net: String,
pub sta: String,
pub loc: String,
pub cha: String,
}
impl NetStaLocCha {
pub fn from_sid(sid: &[c_char]) -> MSResult<Self> {
let s0 = " ";
let s1 = " ";
let sid = CString::new(to_string(sid)).unwrap().into_raw();
let xnet = CString::new(s0).unwrap().into_raw();
let xsta = CString::new(s0).unwrap().into_raw();
let xloc = CString::new(s0).unwrap().into_raw();
let xcha = CString::new(s1).unwrap().into_raw();
let rv = unsafe {
check(raw::ms_sid2nslc(sid, xnet, xsta, xloc, xcha))?;
let net = CString::from_raw(xnet).into_string().unwrap();
let sta = CString::from_raw(xsta).into_string().unwrap();
let loc = CString::from_raw(xloc).into_string().unwrap();
let cha = CString::from_raw(xcha).into_string().unwrap();
Self { net, sta, loc, cha }
};
Ok(rv)
}
}
impl fmt::Display for NetStaLocCha {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let net = CString::new(self.net.as_str()).unwrap().into_raw();
let sta = CString::new(self.sta.as_str()).unwrap().into_raw();
let loc = CString::new(self.loc.as_str()).unwrap().into_raw();
let cha = CString::new(self.cha.as_str()).unwrap().into_raw();
let sid = CString::new(Vec::with_capacity(64)).unwrap().into_raw();
let sid = unsafe {
check(raw::ms_nslc2sid(sid, 64, 0, net, sta, loc, cha)).map_err(|_| fmt::Error)?;
let sid = CString::from_raw(sid);
sid.into_string().map_err(|_| fmt::Error)?
};
write!(f, "{}", sid)
}
}
pub fn xchan2seedchan(xchan: &str) -> MSResult<String> {
let xcha = CString::new(xchan)
.map_err(|_| MSError::from_str(&format!("failed to convert xchan: {}", xchan)))?;
let buf = b" ".to_vec();
let cha = unsafe {
let buf = CString::from_vec_unchecked(buf).into_raw();
check(raw::ms_xchan2seedchan(buf, xcha.as_ptr() as *const _))?;
CString::from_raw(buf)
};
let rv = cha
.into_string()
.map_err(|_| MSError::from_str(&format!("failed to convert xchan: {}", xchan)))?;
Ok(rv)
}
pub fn seedchan2xchan(seed_chan: &str) -> MSResult<String> {
let cha = CString::new(seed_chan)
.map_err(|_| MSError::from_str(&format!("failed to convert seed_chan: {}", seed_chan)))?;
let buf = b" ".to_vec();
let xcha = unsafe {
let buf = CString::from_vec_unchecked(buf).into_raw();
check(raw::ms_seedchan2xchan(buf, cha.as_ptr() as *const _))?;
CString::from_raw(buf)
};
let rv = xcha
.into_string()
.map_err(|_| MSError::from_str(&format!("failed to convert seed_chan: {}", seed_chan)))?;
Ok(rv)
}
pub fn factor_multiplier_to_sample_rate(factor: i32, multiplier: i32) -> f64 {
let mut samp_rate: f64 = 0.0;
if factor > 0 {
samp_rate = factor as f64;
} else if factor < 0 {
samp_rate = -1.0 / factor as f64;
}
if multiplier > 0 {
samp_rate = samp_rate * multiplier as f64;
} else if multiplier < 0 {
samp_rate = -1.0 * (samp_rate as f64 / multiplier as f64);
}
return samp_rate;
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_xchan2seedchan() {
assert_eq!(xchan2seedchan("B_H_Z").unwrap(), "BHZ");
}
#[test]
fn test_seedchan2xchan() {
assert_eq!(seedchan2xchan("BHZ").unwrap(), "B_H_Z");
}
#[test]
fn test_factor_multiplier_to_sample_rate() {
assert_eq!(factor_multiplier_to_sample_rate(0, 0), 0f64);
assert_eq!(factor_multiplier_to_sample_rate(33, 10), 330f64);
assert_eq!(factor_multiplier_to_sample_rate(330, 1), 330f64);
assert_eq!(factor_multiplier_to_sample_rate(3306, -10), 330.6);
assert_eq!(factor_multiplier_to_sample_rate(-60, 1), 1.0 / 60.0);
assert_eq!(factor_multiplier_to_sample_rate(1, -10), 0.1);
assert_eq!(factor_multiplier_to_sample_rate(-10, 1), 0.1);
assert_eq!(factor_multiplier_to_sample_rate(-1, -10), 0.1);
}
}