use std::{
io::{self, BufRead, Lines, Write},
marker::PhantomData,
str::FromStr,
};
use byteorder::WriteBytesExt;
use log::error;
use quick_error::quick_error;
use nom::{
branch::alt,
character::complete::{char, digit1, multispace0},
combinator::{cut, map, map_res, opt},
error::{convert_error, FromExternalError, ParseError, VerboseError},
multi::many1,
sequence::{preceded, terminated, tuple},
IResult,
};
use crate::{
elem::{cell::Cell, cellcellrange::CellOrCellRange, cellrange::CellRange},
elemset::cellcellrange::{CellOrCellRanges, MocCellOrCellRanges},
idx::Idx,
moc::{
cellcellrange::CellOrCellRangeMOC, CellOrCellRangeMOCIterator, HasMaxDepth, MOCProperties,
NonOverlapping, ZSorted,
},
moc2d::{
cellcellrange::{CellOrCellRangeMOC2, CellOrCellRangeMOC2Elem},
CellOrCellRangeMOC2ElemIt, CellOrCellRangeMOC2Iterator,
},
qty::MocQty,
};
quick_error! {
#[derive(Debug)]
pub enum AsciiError {
Io(err: io::Error) {
from()
display("I/O error: {}", err)
}
ParseError(error: String) {
display("Parse error: {}", error)
}
EmptyReader {
display("Empty reader!")
}
NoData {
display("No data to be read!")
}
QtyExpectedAtFirstLine(qty: String, line: String) {
display("Error at first line. Expected: 'qty={}'. Actual: {}", qty, line)
}
DepthExpectedAtSecondLine(line: String) {
display("Error at second line. Expected: 'depth=DEPTH'. Actual: {}", line)
}
WrongFirstTokenDepthExpected(token: String) {
display("Wrong first token. Expected: depth. Actual: {}", token)
}
RemainingData {
display("No all data have been parsed!")
}
WrongDepthType(depth: String) {
display("Wrong depth type. Expected type: u8. Actual value: {}", depth)
}
ElemNotFound(elem: String, line: String) {
display("Element '{}' not found in '{}'.", elem, line)
}
IndexIsNotValid(depth: u8, icell: u64) {
display("Too large index '{}' for depth '{}'.", icell, depth)
}
NotValid {
display("The ascci MOC is not valid (contains overlapping elements)")
}
}
}
pub fn to_ascii_ivoa<T, Q, I, W>(
it: I,
fold: &Option<usize>,
use_range_len: bool,
mut writer: W,
) -> std::io::Result<()>
where
T: Idx,
Q: MocQty<T>,
I: CellOrCellRangeMOCIterator<T, Qty = Q>,
W: Write,
{
let d_max = it.depth_max() as usize;
let mut s_by_depth: Vec<String> = (0..=d_max).map(|i| format!("{}/", i)).collect();
for e in it {
let (d, s) = match e {
CellOrCellRange::Cell(c) => (c.depth as usize, format!("{} ", c.idx)),
CellOrCellRange::CellRange(r) => {
let s = if use_range_len {
format!(
"{}+{} ",
r.range.start,
r.range.end - r.range.start - T::one()
)
} else {
format!("{}-{} ", r.range.start, r.range.end - T::one())
};
(r.depth as usize, s)
}
};
match fold {
Some(n_chars) => {
let l = s_by_depth[d].rfind('\n').unwrap_or(0);
if s_by_depth[d].len() - l + s.len() > *n_chars {
s_by_depth[d].push_str("\n ");
}
s_by_depth[d].push_str(&s)
}
None => s_by_depth[d].push_str(&s),
}
}
for (d, s) in s_by_depth.into_iter().enumerate() {
if fold.is_none() {
if s.ends_with('/') {
if d == d_max {
write!(writer, "{} ", &s)?;
}
} else {
writer.write_all(s.as_bytes())?;
}
} else if !s.ends_with('/') || d == d_max {
writeln!(writer, "{}", &s)?;
}
}
Ok(())
}
pub fn from_ascii_ivoa<T, Q>(input: &str) -> Result<CellOrCellRangeMOC<T, Q>, AsciiError>
where
T: Idx,
Q: MocQty<T>,
{
enum ValType<T> {
Depth,
RangeWithEnd(T), RangeWithLen(T), }
fn parse_val<'a, T: Idx, E>(buf: &'a str) -> IResult<&'a str, T, E>
where
E: ParseError<&'a str> + FromExternalError<&'a str, <T as FromStr>::Err>,
{
map_res(digit1, |s: &str| s.parse::<T>())(buf)
}
fn parse_range_end<'a, T: Idx, E>(buf: &'a str) -> IResult<&'a str, ValType<T>, E>
where
E: ParseError<&'a str> + FromExternalError<&'a str, <T as FromStr>::Err>,
{
preceded(
char('-'),
cut(map(parse_val, ValType::RangeWithEnd)), )(buf)
}
fn parse_range_len<'a, T: Idx, E>(buf: &'a str) -> IResult<&'a str, ValType<T>, E>
where
E: ParseError<&'a str> + FromExternalError<&'a str, <T as FromStr>::Err>,
{
preceded(
char('+'),
cut(map(parse_val, ValType::RangeWithLen)), )(buf)
}
fn parse_depth_delim<'a, T: Idx, E>(buf: &'a str) -> IResult<&'a str, ValType<T>, E>
where
E: ParseError<&'a str>,
{
map(char('/'), |_| ValType::Depth)(buf)
}
fn parse_depth_delim_or_range_end_or_range_len<'a, T: Idx, E>(
buf: &'a str,
) -> IResult<&'a str, ValType<T>, E>
where
E: ParseError<&'a str> + FromExternalError<&'a str, <T as FromStr>::Err>,
{
alt((parse_depth_delim, parse_range_end, parse_range_len))(buf)
}
#[derive(Debug)]
enum Token<T> {
Depth(T), Cell(T),
Range { start: T, end: T },
}
fn parse_token<'a, T: Idx, E>(buf: &'a str) -> IResult<&'a str, Token<T>, E>
where
E: ParseError<&'a str> + FromExternalError<&'a str, <T as FromStr>::Err>,
{
map(
tuple((
parse_val::<T, E>,
opt(parse_depth_delim_or_range_end_or_range_len::<T, E>),
)),
|(val, void_or_range)| match void_or_range {
None => Token::Cell(val),
Some(ValType::Depth) => Token::Depth(val),
Some(ValType::RangeWithEnd(end)) => Token::Range {
start: val,
end: end + T::one(),
},
Some(ValType::RangeWithLen(len)) => Token::Range {
start: val,
end: val + len + T::one(),
},
},
)(buf)
}
fn tokenizer<'a, T: Idx, E>(buf: &'a str) -> IResult<&'a str, Vec<Token<T>>, E>
where
E: ParseError<&'a str> + FromExternalError<&'a str, <T as FromStr>::Err>,
{
terminated(
many1(preceded(multispace0, parse_token::<T, E>)),
multispace0,
)(buf)
}
let (remain, tokens) = tokenizer::<T, VerboseError<&str>>(input).map_err(|err| match err {
nom::Err::Error(e) | nom::Err::Failure(e) => AsciiError::ParseError(convert_error(input, e)),
nom::Err::Incomplete(e) => {
AsciiError::ParseError(format!("Missing data to be parsed: {:?}", e))
}
})?;
if !remain.is_empty() {
return Err(AsciiError::RemainingData);
}
let mut tokens = tokens.into_iter();
let mut cur_depth = match tokens.next() {
Some(Token::Depth(depth)) => depth
.to_u8()
.ok_or_else(|| AsciiError::WrongDepthType(depth.to_string())),
None => Ok(0),
Some(token) => Err(AsciiError::WrongFirstTokenDepthExpected(format!(
"{:?}",
token
))),
}?;
let mut depth_max = cur_depth;
let mut curr_end_max = Q::n_cells(cur_depth);
let mut moc: Vec<CellOrCellRange<T>> = Vec::with_capacity(tokens.len());
for token in tokens {
match token {
Token::Depth(depth) => {
cur_depth = depth
.to_u8()
.ok_or_else(|| AsciiError::WrongDepthType(depth.to_string()))?;
depth_max = depth_max.max(cur_depth);
curr_end_max = Q::n_cells(cur_depth);
}
Token::Cell(icell) => {
if icell > curr_end_max {
return Err(AsciiError::IndexIsNotValid(cur_depth, icell.to_u64()));
}
moc.push(CellOrCellRange::Cell(Cell::new(cur_depth, icell)))
}
Token::Range { start, end } => {
if end > curr_end_max {
return Err(AsciiError::IndexIsNotValid(cur_depth, end.to_u64()));
}
moc.push(CellOrCellRange::CellRange(CellRange::new(
cur_depth, start, end,
)))
}
}
}
moc.sort_by(|a, b| a.flat_cmp::<Q>(b));
for (e1, e2) in moc.iter().zip(moc.iter().skip(1)) {
if e1.overlap::<Q>(e2) {
return Err(AsciiError::NotValid);
}
}
Ok(CellOrCellRangeMOC::new(
depth_max,
MocCellOrCellRanges::new(CellOrCellRanges::new(moc)),
))
}
pub fn moc2d_from_ascii_ivoa<T, Q, U, R>(
input: &str,
) -> Result<CellOrCellRangeMOC2<T, Q, U, R>, AsciiError>
where
T: Idx,
Q: MocQty<T>,
U: Idx,
R: MocQty<U>,
{
let mut depth_max_l = 0_u8;
let mut depth_max_r = 0_u8;
let mut elems: Vec<CellOrCellRangeMOC2Elem<T, Q, U, R>> = Vec::with_capacity(100);
for elem in input.trim().split(Q::PREFIX) {
if elem.is_empty() {
continue;
}
if let Some((l, r)) = elem.split_once(R::PREFIX) {
let l: CellOrCellRangeMOC<T, Q> = from_ascii_ivoa(l)?;
let r: CellOrCellRangeMOC<U, R> = from_ascii_ivoa(r)?;
depth_max_l = depth_max_l.max(l.depth_max());
depth_max_r = depth_max_r.max(r.depth_max());
if !l.is_empty() && !r.is_empty() {
elems.push(CellOrCellRangeMOC2Elem::new(l, r));
}
} else {
return Err(AsciiError::ElemNotFound(
R::PREFIX.to_string(),
elem.to_string(),
));
}
}
Ok(CellOrCellRangeMOC2::new(depth_max_l, depth_max_r, elems))
}
pub fn moc2d_to_ascii_ivoa<T, Q, I, U, R, J, K, L, W>(
moc2_it: L,
fold: &Option<usize>,
use_range_len: bool,
mut writer: W,
) -> Result<(), AsciiError>
where
T: Idx,
Q: MocQty<T>,
I: CellOrCellRangeMOCIterator<T, Qty = Q>,
U: Idx,
R: MocQty<U>,
J: CellOrCellRangeMOCIterator<U, Qty = R>,
K: CellOrCellRangeMOC2ElemIt<T, Q, U, R, It1 = I, It2 = J>,
L: CellOrCellRangeMOC2Iterator<T, Q, I, U, R, J, K>,
W: Write,
{
let d1 = moc2_it.depth_max_1();
let d2 = moc2_it.depth_max_2();
for e in moc2_it {
writer.write_u8(Q::PREFIX as u8)?;
let (moc1_it, moc2_it) = e.cellcellrange_mocs_it();
to_ascii_ivoa(moc1_it, fold, use_range_len, &mut writer)?;
writer.write_u8(R::PREFIX as u8)?;
to_ascii_ivoa(moc2_it, fold, use_range_len, &mut writer)?;
}
writeln!(&mut writer, "{}{}/ {}{}/", Q::PREFIX, d1, R::PREFIX, d2).map_err(AsciiError::Io)
}
pub fn to_ascii_stream<T, Q, I, W>(it: I, use_range_len: bool, mut writer: W) -> std::io::Result<()>
where
T: Idx,
Q: MocQty<T>,
I: CellOrCellRangeMOCIterator<T, Qty = Q>,
W: Write,
{
writeln!(writer, "qty={}", Q::NAME)?;
writeln!(writer, "depth={}", it.depth_max())?;
if use_range_len {
for e in it {
match e {
CellOrCellRange::Cell(c) => writeln!(writer, "{}/{}", c.depth, c.idx)?,
CellOrCellRange::CellRange(r) => writeln!(
writer,
"{}/{}+{}",
r.depth,
r.range.start,
r.range.end - r.range.start
)?,
}
}
} else {
for e in it {
match e {
CellOrCellRange::Cell(c) => writeln!(writer, "{}/{}", c.depth, c.idx)?,
CellOrCellRange::CellRange(r) => {
writeln!(writer, "{}/{}-{}", r.depth, r.range.start, r.range.end)?
}
}
}
}
Ok(())
}
pub fn from_ascii_stream<T, Q, R>(reader: R) -> Result<MOCFromAsciiStream<T, Q, R>, AsciiError>
where
T: Idx,
Q: MocQty<T>,
R: BufRead,
{
let mut lines = reader.lines();
if let Some(line) = lines.next().transpose()? {
match line
.trim()
.split_once('=')
.map(|(l, r)| (l.trim(), r.trim()))
{
Some(("qty", name)) if name == Q::NAME => (),
_ => {
return Err(AsciiError::QtyExpectedAtFirstLine(
Q::NAME.to_string(),
line,
))
}
}
} else {
return Err(AsciiError::EmptyReader);
}
if let Some(line) = lines.next().transpose()? {
let depth = match line
.trim()
.split_once('=')
.map(|(l, r)| (l.trim(), r.trim().parse::<u8>()))
{
Some(("depth", Ok(depth))) => Ok(depth),
_ => Err(AsciiError::DepthExpectedAtSecondLine(line)),
}?;
Ok(MOCFromAsciiStream {
lines,
depth_max: depth,
_t_type: PhantomData,
_q_type: PhantomData,
})
} else {
Err(AsciiError::NoData)
}
}
pub struct MOCFromAsciiStream<T: Idx, Q: MocQty<T>, R: BufRead> {
lines: Lines<R>,
depth_max: u8,
_t_type: PhantomData<T>,
_q_type: PhantomData<Q>,
}
impl<T: Idx, Q: MocQty<T>, R: BufRead> HasMaxDepth for MOCFromAsciiStream<T, Q, R> {
fn depth_max(&self) -> u8 {
self.depth_max
}
}
impl<T: Idx, Q: MocQty<T>, R: BufRead> ZSorted for MOCFromAsciiStream<T, Q, R> {}
impl<T: Idx, Q: MocQty<T>, R: BufRead> NonOverlapping for MOCFromAsciiStream<T, Q, R> {}
impl<T: Idx, Q: MocQty<T>, R: BufRead> MOCProperties for MOCFromAsciiStream<T, Q, R> {}
impl<T: Idx, Q: MocQty<T>, R: BufRead> Iterator for MOCFromAsciiStream<T, Q, R> {
type Item = CellOrCellRange<T>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.lines.next().transpose() {
Ok(Some(line)) => {
let line = line.trim();
if !line.is_empty() {
match line.split_once('/') {
Some((depth, cell_or_range)) => {
if let Ok(depth) = depth.parse::<u8>() {
if cell_or_range.contains('-') {
let (start, end) = cell_or_range.split_once('-').unwrap();
if let (Ok(start), Ok(end)) = (start.parse::<T>(), end.parse::<T>()) {
return Some(CellOrCellRange::CellRange(CellRange::new(
depth, start, end,
)));
}
} else if cell_or_range.contains('+') {
let (start, len) = cell_or_range.split_once('+').unwrap();
if let (Ok(start), Ok(len)) = (start.parse::<T>(), len.parse::<T>()) {
return Some(CellOrCellRange::CellRange(CellRange::new(
depth,
start,
start + len,
)));
}
} else if let Ok(idx) = cell_or_range.parse::<T>() {
return Some(CellOrCellRange::Cell(Cell::new(depth, idx)));
}
}
error!("Error parsing ascii stream at line: {:?}", line);
}
_ => error!(
"Error parsing ascii stream at line: {:?}. Separator '/' not found.",
line
),
}
}
}
Ok(None) => return None,
Err(e) => error!("Error reading ascii stream: {:?}", e),
}
}
}
}
impl<T: Idx, Q: MocQty<T>, R: BufRead> CellOrCellRangeMOCIterator<T>
for MOCFromAsciiStream<T, Q, R>
{
type Qty = Q;
fn peek_last(&self) -> Option<&CellOrCellRange<T>> {
None
}
}
#[cfg(test)]
mod tests {
use std::str::{self, from_utf8};
use crate::{
deser::ascii::{
from_ascii_ivoa, from_ascii_stream, moc2d_from_ascii_ivoa, moc2d_to_ascii_ivoa,
},
elem::cell::Cell,
elemset::range::MocRanges,
moc::{
range::RangeMOC, CellMOCIterator, CellOrCellRangeMOCIntoIterator, CellOrCellRangeMOCIterator,
HasMaxDepth, RangeMOCIntoIterator, RangeMOCIterator,
},
moc2d::{
range::RangeMOC2, CellOrCellRangeMOC2IntoIterator, CellOrCellRangeMOC2Iterator,
HasTwoMaxDepth, RangeMOC2IntoIterator, RangeMOC2Iterator,
},
qty::{Hpx, Time},
};
#[test]
fn test_from_ascii_ivoa() {
let smoc_ascii = "3/3 10 4/16-18 22 5/19-20 17/222 28/123456789 29/";
let smoc = from_ascii_ivoa::<u64, Hpx<u64>>(&smoc_ascii).unwrap();
let mut rit = smoc.into_cellcellrange_moc_iter().ranges();
assert_eq!(rit.depth_max(), 29);
assert_eq!(rit.next(), Some(493827156..493827160));
assert_eq!(rit.next(), Some(3724541952..3741319168));
assert_eq!(rit.next(), Some(5348024557502464..5910974510923776));
assert_eq!(rit.next(), Some(13510798882111488..21392098230009856));
assert_eq!(rit.next(), Some(24769797950537728..25895697857380352));
assert_eq!(rit.next(), Some(45035996273704960..49539595901075456));
assert_eq!(rit.next(), None);
let tmoc_ascii = "31/1 32/4 35/";
let tmoc = from_ascii_ivoa::<u64, Time<u64>>(&tmoc_ascii).unwrap();
let mut cellit = tmoc.into_cellcellrange_moc_iter().ranges();
assert_eq!(cellit.depth_max(), 35);
assert_eq!(cellit.next(), Some(1073741824..2684354560));
assert_eq!(cellit.next(), None);
let tmoc_ascii = "31/1 32/4 35/";
let tmoc = from_ascii_ivoa::<u64, Time<u64>>(&tmoc_ascii).unwrap();
let mut cellit = tmoc.into_cellcellrange_moc_iter().ranges().cells();
assert_eq!(cellit.depth_max(), 35);
assert_eq!(cellit.next(), Some(Cell::new(31, 1)));
assert_eq!(cellit.next(), Some(Cell::new(32, 4)));
assert_eq!(cellit.next(), None);
}
#[test]
fn test_from_ascii_ivoa_v2() {
let smoc_ascii = "5/8-10 42-46 54 6/4500 8/45";
let smoc = from_ascii_ivoa::<u64, Hpx<u64>>(&smoc_ascii).unwrap();
let mut rit = smoc.into_cellcellrange_moc_iter().ranges();
assert_eq!(rit.depth_max(), 8);
assert_eq!(rit.next(), Some(197912092999680..202310139510784));
assert_eq!(rit.next(), Some(2251799813685248..3096224743817216));
assert_eq!(rit.next(), Some(11821949021847552..13229323905400832));
assert_eq!(rit.next(), Some(15199648742375424..15481123719086080));
assert_eq!(rit.next(), Some(316659348799488000..316729717543665664));
assert_eq!(rit.next(), None);
}
#[test]
fn test_from_ascii_ivoa_not_valid() {
let smoc_ascii = "5/8-10 42-46 54 8 6/4500 8/45";
assert!(from_ascii_ivoa::<u64, Hpx::<u64>>(&smoc_ascii).is_err());
}
#[test]
fn test_from_ascii_ivoa_not_valid_2() {
let smoc_ascii = "1/1000";
assert!(from_ascii_ivoa::<u64, Hpx::<u64>>(&smoc_ascii).is_err());
}
#[test]
fn test_fromto_ascii_ivoa() {
let rm = RangeMOC::new(
29,
MocRanges::<u64, Hpx<u64>>::new_unchecked(vec![
0..5,
6..59,
78..6953,
12458..55587,
55787..65587,
]),
);
let mut res_ascii_1 = Vec::new();
(&rm)
.into_range_moc_iter()
.cells()
.cellranges()
.to_ascii_ivoa(None, false, &mut res_ascii_1)
.unwrap();
println!("{}\n", str::from_utf8(&res_ascii_1).unwrap());
let mut res_ascii_2 = Vec::new();
from_ascii_ivoa::<u64, Hpx<u64>>(str::from_utf8(&res_ascii_1).unwrap())
.unwrap()
.into_cellcellrange_moc_iter()
.to_ascii_ivoa(None, false, &mut res_ascii_2)
.unwrap();
assert_eq!(res_ascii_1, res_ascii_2);
let mut res_ascii_1 = Vec::new();
(&rm)
.into_range_moc_iter()
.cells()
.cellranges()
.to_ascii_ivoa(None, true, &mut res_ascii_1)
.unwrap();
let mut res_ascii_2 = Vec::new();
from_ascii_ivoa::<u64, Hpx<u64>>(str::from_utf8(&res_ascii_1).unwrap())
.unwrap()
.into_cellcellrange_moc_iter()
.to_ascii_ivoa(None, true, &mut res_ascii_2)
.unwrap();
assert_eq!(res_ascii_1, res_ascii_2);
let mut res_ascii_1 = Vec::new();
(&rm)
.into_range_moc_iter()
.cells()
.cellranges()
.to_ascii_ivoa(Some(30), false, &mut res_ascii_1)
.unwrap();
let mut res_ascii_2 = Vec::new();
from_ascii_ivoa::<u64, Hpx<u64>>(str::from_utf8(&res_ascii_1).unwrap())
.unwrap()
.into_cellcellrange_moc_iter()
.to_ascii_ivoa(Some(30), false, &mut res_ascii_2)
.unwrap();
assert_eq!(res_ascii_1, res_ascii_2);
let mut res_ascii_1 = Vec::new();
(&rm)
.into_range_moc_iter()
.cells()
.cellranges()
.to_ascii_ivoa(Some(30), true, &mut res_ascii_1)
.unwrap();
let mut res_ascii_2 = Vec::new();
from_ascii_ivoa::<u64, Hpx<u64>>(str::from_utf8(&res_ascii_1).unwrap())
.unwrap()
.into_cellcellrange_moc_iter()
.to_ascii_ivoa(Some(30), true, &mut res_ascii_2)
.unwrap();
assert_eq!(res_ascii_1, res_ascii_2);
}
#[test]
fn test_fromto_ascii_stream() {
let rm = RangeMOC::new(
29,
MocRanges::<u64, Hpx<u64>>::new_unchecked(vec![
0..5,
6..59,
78..6953,
12458..55587,
55787..65587,
]),
);
let mut res_ascii_1 = Vec::new();
(&rm)
.into_range_moc_iter()
.cells()
.cellranges()
.to_ascii_stream(false, &mut res_ascii_1)
.unwrap();
let mut res_ascii_2 = Vec::new();
from_ascii_stream::<u64, Hpx<u64>, _>(&res_ascii_1[..])
.unwrap()
.to_ascii_stream(false, &mut res_ascii_2)
.unwrap();
assert_eq!(res_ascii_1, res_ascii_2);
let mut res_ascii_1 = Vec::new();
rm.into_range_moc_iter()
.cells()
.cellranges()
.to_ascii_stream(true, &mut res_ascii_1)
.unwrap();
let mut res_ascii_2 = Vec::new();
from_ascii_stream::<u64, Hpx<u64>, _>(&res_ascii_1[..])
.unwrap()
.to_ascii_stream(true, &mut res_ascii_2)
.unwrap();
assert_eq!(res_ascii_1, res_ascii_2);
}
#[test]
fn test_moc2d_fromto_ascii_ivoa() {
let input = "t61/1 3 5 s3/1-3 t61/50 52 s4/25";
let stmoc = moc2d_from_ascii_ivoa::<u64, Time<u64>, u64, Hpx<u64>>(input).unwrap();
let mut res_ascii_1 = Vec::new();
moc2d_to_ascii_ivoa(
(&stmoc).into_cellcellrange_moc2_iter(),
&Some(20),
false,
&mut res_ascii_1,
)
.unwrap();
println!("{}\n", str::from_utf8(&res_ascii_1).unwrap());
let mut res_ascii_1 = Vec::new();
moc2d_to_ascii_ivoa(
(&stmoc).into_cellcellrange_moc2_iter(),
&None,
true,
&mut res_ascii_1,
)
.unwrap();
println!("{}\n", str::from_utf8(&res_ascii_1).unwrap());
}
#[test]
fn test_moc2d_empty_to_ascii_ivoa() {
let stmoc = RangeMOC2::<u64, Time<u64>, u64, Hpx<u64>>::new_empty(12, 8);
let mut res_ascii = Vec::new();
stmoc
.into_range_moc2_iter()
.into_cellcellrange_moc2_iter()
.to_ascii_ivoa(None, false, &mut res_ascii)
.unwrap();
let ascii = from_utf8(res_ascii.as_ref()).unwrap();
let expected = "t12/ s8/\n";
assert_eq!(ascii, expected);
let stmoc2 = moc2d_from_ascii_ivoa::<u64, Time<u64>, u64, Hpx<u64>>(expected)
.unwrap()
.into_cellcellrange_moc2_iter()
.into_range_moc2_iter()
.into_range_moc2();
assert!(stmoc2.is_empty());
assert_eq!(stmoc2.depth_max_1(), 12);
assert_eq!(stmoc2.depth_max_2(), 8);
}
}