use super::*;
const READER_BUFSIZE: usize = 65537;
pub struct GdsReader {
buf: [u8; READER_BUFSIZE],
file: Cursor<Vec<u8>>, }
impl GdsReader {
pub fn open(fname: impl AsRef<Path>) -> GdsResult<GdsReader> {
let bytes = std::fs::read(fname)?;
let cursor = Cursor::new(bytes);
Ok(Self::new(cursor))
}
pub fn from_bytes(bytes: Vec<u8>) -> GdsReader {
Self::new(Cursor::new(bytes))
}
pub fn new(file: Cursor<Vec<u8>>) -> GdsReader {
let buf = [0; READER_BUFSIZE];
GdsReader { file, buf }
}
fn read_record_header(&mut self) -> GdsResult<GdsRecordHeader> {
let len = match self.file.read_u16::<BigEndian>() {
Err(e) => return Err(GdsError::Boxed(Box::new(e))), Ok(num) if num < 4 => return Err(GdsError::RecordLen(num.into())), Ok(num) if num % 2 != 0 => return Err(GdsError::RecordLen(num.into())), Ok(num) => num, };
let len = len - 4; let record_type = self.file.read_u8()?;
let record_type: GdsRecordType =
FromPrimitive::from_u8(record_type).ok_or(GdsError::InvalidRecordType(record_type))?;
if !record_type.valid() {
return Err(GdsError::InvalidRecordType(record_type as u8));
}
let data_type = self.file.read_u8()?;
let data_type =
FromPrimitive::from_u8(data_type).ok_or(GdsError::InvalidDataType(data_type))?;
Ok(GdsRecordHeader {
rtype: record_type,
dtype: data_type,
len,
})
}
fn read_record(&mut self) -> GdsResult<GdsRecord> {
let header = self.read_record_header()?;
self.read_record_content(&header)
}
fn read_record_content(&mut self, header: &GdsRecordHeader) -> GdsResult<GdsRecord> {
use GdsDataType::{BitArray, NoData, Str, F64, I16, I32};
let len = header.len;
let record: GdsRecord = match (header.rtype, header.dtype, len) {
(GdsRecordType::Header, I16, 2) => GdsRecord::Header {
version: self.read_i16(len)?[0],
},
(GdsRecordType::BgnLib, I16, 24) => GdsRecord::BgnLib {
dates: self.read_i16(len)?,
},
(GdsRecordType::LibName, Str, _) => GdsRecord::LibName(self.read_str(len)?),
(GdsRecordType::Units, F64, 16) => {
let v = self.read_f64(len)?;
GdsRecord::Units(v[0], v[1])
}
(GdsRecordType::EndLib, NoData, 0) => GdsRecord::EndLib,
(GdsRecordType::BgnStruct, I16, 24) => GdsRecord::BgnStruct {
dates: self.read_i16(len)?,
},
(GdsRecordType::StructName, Str, _) => GdsRecord::StructName(self.read_str(len)?),
(GdsRecordType::StructRefName, Str, _) => GdsRecord::StructRefName(self.read_str(len)?),
(GdsRecordType::EndStruct, NoData, 0) => GdsRecord::EndStruct,
(GdsRecordType::Boundary, NoData, 0) => GdsRecord::Boundary,
(GdsRecordType::Path, NoData, 0) => GdsRecord::Path,
(GdsRecordType::StructRef, NoData, 0) => GdsRecord::StructRef,
(GdsRecordType::ArrayRef, NoData, 0) => GdsRecord::ArrayRef,
(GdsRecordType::Text, NoData, 0) => GdsRecord::Text,
(GdsRecordType::Layer, I16, 2) => GdsRecord::Layer(self.read_i16(len)?[0]),
(GdsRecordType::DataType, I16, 2) => GdsRecord::DataType(self.read_i16(len)?[0]),
(GdsRecordType::Width, I32, 4) => GdsRecord::Width(self.read_i32(len)?[0]),
(GdsRecordType::Xy, I32, _) => GdsRecord::Xy(self.read_i32(len)?),
(GdsRecordType::EndElement, NoData, 0) => GdsRecord::EndElement,
(GdsRecordType::ColRow, I16, 4) => {
let d = self.read_i16(len)?;
GdsRecord::ColRow {
cols: d[0],
rows: d[1],
}
}
(GdsRecordType::Node, NoData, 0) => GdsRecord::Node,
(GdsRecordType::TextType, I16, 2) => GdsRecord::TextType(self.read_i16(len)?[0]),
(GdsRecordType::Presentation, BitArray, 2) => {
let bytes = self.read_bytes(len)?;
GdsRecord::Presentation(bytes[0], bytes[1])
}
(GdsRecordType::String, Str, _) => GdsRecord::String(self.read_str(len)?),
(GdsRecordType::Strans, BitArray, 2) => {
let bytes = self.read_bytes(len)?;
GdsRecord::Strans(bytes[0], bytes[1])
}
(GdsRecordType::Mag, F64, 8) => GdsRecord::Mag(self.read_f64(len)?[0]),
(GdsRecordType::Angle, F64, 8) => GdsRecord::Angle(self.read_f64(len)?[0]),
(GdsRecordType::RefLibs, Str, _) => GdsRecord::RefLibs(self.read_str(len)?),
(GdsRecordType::Fonts, Str, _) => GdsRecord::Fonts(self.read_str(len)?),
(GdsRecordType::PathType, I16, 2) => GdsRecord::PathType(self.read_i16(len)?[0]),
(GdsRecordType::Generations, I16, 2) => GdsRecord::Generations(self.read_i16(len)?[0]),
(GdsRecordType::AttrTable, Str, _) => GdsRecord::AttrTable(self.read_str(len)?),
(GdsRecordType::ElemFlags, BitArray, 2) => {
let bytes = self.read_bytes(len)?;
GdsRecord::ElemFlags(bytes[0], bytes[1])
}
(GdsRecordType::Nodetype, I16, 2) => GdsRecord::Nodetype(self.read_i16(len)?[0]),
(GdsRecordType::PropAttr, I16, 2) => GdsRecord::PropAttr(self.read_i16(len)?[0]),
(GdsRecordType::PropValue, Str, _) => GdsRecord::PropValue(self.read_str(len)?),
(GdsRecordType::Box, NoData, 0) => GdsRecord::Box,
(GdsRecordType::BoxType, I16, 2) => GdsRecord::BoxType(self.read_i16(len)?[0]),
(GdsRecordType::Plex, I32, 4) => GdsRecord::Plex(self.read_i32(len)?[0]),
(GdsRecordType::BeginExtn, I32, 4) => GdsRecord::BeginExtn(self.read_i32(len)?[0]),
(GdsRecordType::EndExtn, I32, 4) => GdsRecord::EndExtn(self.read_i32(len)?[0]),
(GdsRecordType::TapeNum, I16, 2) => GdsRecord::TapeNum(self.read_i16(len)?[0]),
(GdsRecordType::TapeCode, I16, 12) => GdsRecord::TapeCode(self.read_i16(len)?),
(GdsRecordType::Format, I16, 2) => GdsRecord::Format(self.read_i16(len)?[0]),
(GdsRecordType::Mask, Str, _) => GdsRecord::Mask(self.read_str(len)?),
(GdsRecordType::EndMasks, NoData, 0) => GdsRecord::EndMasks,
(GdsRecordType::LibDirSize, I16, 2) => GdsRecord::LibDirSize(self.read_i16(len)?[0]),
(GdsRecordType::SrfName, Str, _) => GdsRecord::SrfName(self.read_str(len)?),
(GdsRecordType::LibSecur, I16, 2) => GdsRecord::LibSecur(self.read_i16(len)?[0]),
_ => return Err(GdsError::RecordDecode(header.rtype, header.dtype, len)),
};
Ok(record)
}
fn read_str(&mut self, len: u16) -> GdsResult<String> {
let len: usize = len.into();
let mut data = &mut self.buf[0..len];
self.file.read_exact(data)?;
let len = data.len();
if data[len - 1] == 0x00 {
data = &mut data[0..len - 1];
}
let s: String = std::str::from_utf8(&data)?.into();
Ok(s)
}
fn read_bytes(&mut self, len: u16) -> Result<Vec<u8>, std::io::Error> {
let len: usize = len.into();
let mut rv: Vec<u8> = vec![0; len];
self.file.read_exact(&mut rv[0..len])?;
Ok(rv)
}
fn read_i16(&mut self, len: u16) -> Result<Vec<i16>, std::io::Error> {
let len: usize = len.into();
self.file.read_exact(&mut self.buf[0..len])?;
let mut rv: Vec<i16> = vec![0; len / 2];
self.buf[0..len]
.as_ref()
.read_i16_into::<BigEndian>(&mut rv)?;
Ok(rv)
}
fn read_i32(&mut self, len: u16) -> Result<Vec<i32>, std::io::Error> {
let len: usize = len.into();
self.file.read_exact(&mut self.buf[0..len])?;
let mut rv: Vec<i32> = vec![0; len / 4];
self.buf[0..len]
.as_ref()
.read_i32_into::<BigEndian>(&mut rv)?;
Ok(rv)
}
fn read_f64(&mut self, len: u16) -> GdsResult<Vec<f64>> {
let len: usize = len.into();
let mut u64s = vec![0; len / 8];
self.file.read_u64_into::<BigEndian>(&mut u64s)?;
let rv = u64s.into_iter().map(GdsFloat64::decode).collect();
Ok(rv)
}
#[inline(always)]
fn pos(&mut self) -> u64 {
self.file.stream_position().unwrap()
}
}
#[derive(Debug, Default)]
pub struct GdsStructScan {
name: String,
start: u64,
end: u64,
}
pub struct GdsScanner {
rdr: GdsReader,
nxt: GdsRecordHeader,
}
impl GdsScanner {
pub fn new(mut rdr: GdsReader) -> GdsResult<Self> {
let nxt = rdr.read_record_header()?;
Ok(Self { rdr, nxt })
}
pub fn scan(fname: impl AsRef<Path>) -> GdsResult<Vec<GdsStructScan>> {
let rdr = GdsReader::open(fname)?;
let mut me = Self::new(rdr)?;
me.scan_lib()
}
fn expect(&mut self, rtype: GdsRecordType) -> GdsResult<()> {
if self.peek().rtype == rtype {
self.skip()
} else {
self.fail()
}
}
fn get(&mut self, rtype: GdsRecordType) -> GdsResult<&GdsRecordHeader> {
if self.peek().rtype == rtype {
Ok(self.peek())
} else {
self.fail()
}
}
fn next(&mut self) -> GdsResult<GdsRecordHeader> {
if self.peek().rtype == GdsRecordType::EndLib {
return Ok(self.peek().clone());
}
let mut rv = self.rdr.read_record_header()?;
mem::swap(&mut rv, &mut self.nxt);
Ok(rv)
}
fn skip(&mut self) -> GdsResult<()> {
let len = self.nxt.len.into();
self.rdr.file.seek(SeekFrom::Current(len))?;
self.next()?;
Ok(())
}
#[inline(always)]
fn peek(&self) -> &GdsRecordHeader {
&self.nxt
}
pub fn scan_lib(&mut self) -> GdsResult<Vec<GdsStructScan>> {
self.expect(GdsRecordType::Header)?;
self.expect(GdsRecordType::BgnLib)?;
let len = self.get(GdsRecordType::LibName)?.len;
let _lib_name = self.rdr.read_str(len)?;
self.next()?;
self.expect(GdsRecordType::Units)?;
let mut strukts = Vec::<GdsStructScan>::with_capacity(1024);
loop {
let GdsRecordHeader { rtype, .. } = self.peek();
match rtype {
GdsRecordType::EndLib => break,
GdsRecordType::BgnStruct => strukts.push(self.scan_struct()?),
_ => return self.fail(),
}
}
Ok(strukts)
}
fn scan_struct(&mut self) -> GdsResult<GdsStructScan> {
let mut s = GdsStructScan::default();
s.start = self.pos() - 4;
self.skip()?;
let len = self.get(GdsRecordType::StructName)?.len;
s.name = self.rdr.read_str(len)?;
self.next()?;
loop {
let GdsRecordHeader { rtype, .. } = self.peek();
match rtype {
GdsRecordType::EndStruct => {
s.end = self.pos();
self.skip()?;
break;
}
GdsRecordType::EndLib => return self.fail(),
_ => self.skip()?,
}
}
Ok(s)
}
#[inline(always)]
fn pos(&mut self) -> u64 {
self.rdr.pos()
}
fn err(&mut self) -> GdsError {
let pos = self.pos();
GdsError::Str(format!(
"Scanned Invalid Record {:?} at Byte Position {}",
self.nxt.rtype, pos
))
}
fn fail<T>(&mut self) -> GdsResult<T> {
Err(self.err())
}
}
pub struct GdsParser {
rdr: GdsReader,
nxt: GdsRecord,
numread: usize,
ctx: Vec<GdsContext>,
}
impl GdsParser {
pub fn open(fname: impl AsRef<Path>) -> GdsResult<GdsParser> {
let rdr = GdsReader::open(fname)?;
Self::new(rdr)
}
pub fn from_bytes(bytes: Vec<u8>) -> GdsResult<GdsParser> {
let rdr = GdsReader::from_bytes(bytes);
Self::new(rdr)
}
pub fn new(mut rdr: GdsReader) -> GdsResult<GdsParser> {
let nxt = rdr.read_record()?;
Ok(GdsParser {
rdr,
nxt,
numread: 1,
ctx: Vec::new(),
})
}
fn next(&mut self) -> GdsResult<GdsRecord> {
if self.nxt == GdsRecord::EndLib {
return Ok(GdsRecord::EndLib);
}
let mut rv = self.rdr.read_record()?;
mem::swap(&mut rv, &mut self.nxt);
self.numread += 1;
Ok(rv)
}
fn peek(&self) -> &GdsRecord {
&self.nxt
}
pub fn parse_lib(&mut self) -> GdsResult<GdsLibrary> {
self.ctx.push(GdsContext::Library);
let mut lib = GdsLibraryBuilder::default();
let mut structs = Vec::<GdsStruct>::with_capacity(1024);
lib = match self.next()? {
GdsRecord::Header { version: v } => lib.version(v),
_ => return self.fail("Invalid library: missing GDS HEADER record"),
};
lib = match self.next()? {
GdsRecord::BgnLib { dates: d } => lib.dates(self.parse_datetimes(d)?),
_ => return self.fail("Invalid library: missing GDS BGNLIB record"),
};
loop {
let r = self.next()?;
lib = match r {
GdsRecord::EndLib => break, GdsRecord::LibName(d) => lib.name(d),
GdsRecord::Units(d0, d1) => lib.units(GdsUnits(d0, d1)),
GdsRecord::BgnStruct { dates } => {
let strukt = self.parse_struct(dates)?;
structs.push(strukt);
lib
}
GdsRecord::LibDirSize(_)
| GdsRecord::SrfName(_)
| GdsRecord::LibSecur(_)
| GdsRecord::RefLibs(_)
| GdsRecord::Fonts(_)
| GdsRecord::AttrTable(_)
| GdsRecord::Generations(_)
| GdsRecord::Format(_) => {
return Err(GdsError::Unsupported(Some(r), Some(GdsContext::Library)))
}
_ => return self.invalid(r),
};
}
lib = lib.structs(structs);
Ok(lib.build()?)
}
fn parse_struct(&mut self, dates: Vec<i16>) -> GdsResult<GdsStruct> {
self.ctx.push(GdsContext::Struct);
let mut strukt = GdsStructBuilder::default();
strukt = strukt.dates(self.parse_datetimes(dates)?);
strukt = match self.next()? {
GdsRecord::StructName(d) => strukt.name(d),
_ => return self.fail("Missing Gds StructName"),
};
let mut elems = Vec::<GdsElement>::with_capacity(1024);
loop {
let r = self.next()?;
match r {
GdsRecord::EndStruct => break, GdsRecord::Boundary => elems.push(self.parse_boundary()?.into()),
GdsRecord::Text => elems.push(self.parse_text_elem()?.into()),
GdsRecord::Path => elems.push(self.parse_path()?.into()),
GdsRecord::Box => elems.push(self.parse_box()?.into()),
GdsRecord::StructRef => elems.push(self.parse_struct_ref()?.into()),
GdsRecord::ArrayRef => elems.push(self.parse_array_ref()?.into()),
GdsRecord::Node => elems.push(self.parse_node()?.into()),
_ => return self.invalid(r),
};
}
strukt = strukt.elems(elems);
let strukt = strukt.build()?;
self.ctx.pop();
Ok(strukt)
}
fn parse_boundary(&mut self) -> GdsResult<GdsBoundary> {
let mut b = GdsBoundaryBuilder::default();
let mut props: Vec<GdsProperty> = Vec::new();
loop {
let r = self.next()?;
b = match r {
GdsRecord::EndElement => break, GdsRecord::Layer(d) => b.layer(d),
GdsRecord::DataType(d) => b.datatype(d),
GdsRecord::Xy(d) => b.xy(GdsPoint::parse_vec(&d)?),
GdsRecord::Plex(d) => b.plex(GdsPlex(d)),
GdsRecord::ElemFlags(d0, d1) => b.elflags(GdsElemFlags(d0, d1)),
GdsRecord::PropAttr(attr) => {
props.push(self.parse_property(attr)?);
b
}
_ => return self.invalid(r),
};
}
b = b.properties(props);
let b = b.build()?;
self.ctx.pop();
Ok(b)
}
fn parse_path(&mut self) -> GdsResult<GdsPath> {
self.ctx.push(GdsContext::Path);
let mut b = GdsPathBuilder::default();
let mut props: Vec<GdsProperty> = Vec::new();
loop {
let r = self.next()?;
b = match r {
GdsRecord::EndElement => break, GdsRecord::Layer(d) => b.layer(d),
GdsRecord::DataType(d) => b.datatype(d),
GdsRecord::Xy(d) => b.xy(GdsPoint::parse_vec(&d)?),
GdsRecord::Width(d) => b.width(d),
GdsRecord::PathType(d) => b.path_type(d),
GdsRecord::BeginExtn(d) => b.begin_extn(d),
GdsRecord::EndExtn(d) => b.end_extn(d),
GdsRecord::Plex(d) => b.plex(GdsPlex(d)),
GdsRecord::ElemFlags(d0, d1) => b.elflags(GdsElemFlags(d0, d1)),
GdsRecord::PropAttr(attr) => {
props.push(self.parse_property(attr)?);
b
}
_ => return self.invalid(r),
};
}
b = b.properties(props);
let b = b.build()?;
self.ctx.pop();
Ok(b)
}
fn parse_text_elem(&mut self) -> GdsResult<GdsTextElem> {
self.ctx.push(GdsContext::Text);
let mut b = GdsTextElemBuilder::default();
let mut props: Vec<GdsProperty> = Vec::new();
loop {
let r = self.next()?;
b = match r {
GdsRecord::EndElement => break, GdsRecord::Layer(d) => b.layer(d),
GdsRecord::TextType(d) => b.texttype(d),
GdsRecord::Xy(d) => b.xy(GdsPoint::parse(&d)?),
GdsRecord::String(d) => b.string(d),
GdsRecord::Presentation(d0, d1) => b.presentation(GdsPresentation(d0, d1)),
GdsRecord::PathType(d) => b.path_type(d),
GdsRecord::Width(d) => b.width(d),
GdsRecord::Plex(d) => b.plex(GdsPlex(d)),
GdsRecord::ElemFlags(d0, d1) => b.elflags(GdsElemFlags(d0, d1)),
GdsRecord::Strans(d0, d1) => b.strans(self.parse_strans(d0, d1)?),
GdsRecord::PropAttr(attr) => {
props.push(self.parse_property(attr)?);
b
}
_ => return self.invalid(r),
};
}
b = b.properties(props);
let b = b.build()?;
self.ctx.pop();
Ok(b)
}
fn parse_node(&mut self) -> GdsResult<GdsNode> {
self.ctx.push(GdsContext::Node);
let mut b = GdsNodeBuilder::default();
let mut props: Vec<GdsProperty> = Vec::new();
loop {
let r = self.next()?;
b = match r {
GdsRecord::EndElement => break, GdsRecord::Layer(d) => b.layer(d),
GdsRecord::Nodetype(d) => b.nodetype(d),
GdsRecord::Xy(d) => b.xy(GdsPoint::parse_vec(&d)?),
GdsRecord::Plex(d) => b.plex(GdsPlex(d)),
GdsRecord::ElemFlags(d0, d1) => b.elflags(GdsElemFlags(d0, d1)),
GdsRecord::PropAttr(attr) => {
props.push(self.parse_property(attr)?);
b
}
_ => return self.invalid(r),
};
}
b = b.properties(props);
let b = b.build()?;
self.ctx.pop();
Ok(b)
}
fn parse_box(&mut self) -> GdsResult<GdsBox> {
self.ctx.push(GdsContext::Box);
let mut b = GdsBoxBuilder::default();
let mut props: Vec<GdsProperty> = Vec::new();
loop {
let r = self.next()?;
b = match r {
GdsRecord::EndElement => break, GdsRecord::Layer(d) => b.layer(d),
GdsRecord::BoxType(d) => b.boxtype(d),
GdsRecord::Xy(d) => {
let v = GdsPoint::parse_vec(&d)?;
let xy: [GdsPoint; 5] = match v.try_into() {
Ok(xy) => xy,
Err(_) => return self.fail("Invalid XY for GdsBox"),
};
b.xy(xy)
}
GdsRecord::Plex(d) => b.plex(GdsPlex(d)),
GdsRecord::ElemFlags(d0, d1) => b.elflags(GdsElemFlags(d0, d1)),
GdsRecord::PropAttr(attr) => {
props.push(self.parse_property(attr)?);
b
}
_ => return self.invalid(r),
};
}
b = b.properties(props);
let b = b.build()?;
self.ctx.pop();
Ok(b)
}
fn parse_struct_ref(&mut self) -> GdsResult<GdsStructRef> {
self.ctx.push(GdsContext::StructRef);
let mut b = GdsStructRefBuilder::default();
let mut props: Vec<GdsProperty> = Vec::new();
loop {
let r = self.next()?;
b = match r {
GdsRecord::EndElement => break, GdsRecord::StructRefName(d) => b.name(d),
GdsRecord::Xy(d) => b.xy(GdsPoint::parse(&d)?),
GdsRecord::Plex(d) => b.plex(GdsPlex(d)),
GdsRecord::ElemFlags(d0, d1) => b.elflags(GdsElemFlags(d0, d1)),
GdsRecord::Strans(d0, d1) => b.strans(self.parse_strans(d0, d1)?),
GdsRecord::PropAttr(attr) => {
props.push(self.parse_property(attr)?);
b
}
_ => return self.invalid(r),
};
}
b = b.properties(props);
let b = b.build()?;
self.ctx.pop();
Ok(b)
}
fn parse_array_ref(&mut self) -> GdsResult<GdsArrayRef> {
self.ctx.push(GdsContext::ArrayRef);
let mut b = GdsArrayRefBuilder::default();
let mut props: Vec<GdsProperty> = Vec::new();
loop {
let r = self.next()?;
b = match r {
GdsRecord::EndElement => break, GdsRecord::StructRefName(d) => b.name(d),
GdsRecord::ColRow { rows, cols } => b.rows(rows).cols(cols),
GdsRecord::Xy(d) => {
let v = GdsPoint::parse_vec(&d)?;
let xy: [GdsPoint; 3] = match v.try_into() {
Ok(xy) => xy,
Err(_) => return self.fail("Invalid XY for GdsArrayRef"),
};
b.xy(xy)
}
GdsRecord::Plex(d) => b.plex(GdsPlex(d)),
GdsRecord::ElemFlags(d0, d1) => b.elflags(GdsElemFlags(d0, d1)),
GdsRecord::Strans(d0, d1) => b.strans(self.parse_strans(d0, d1)?),
GdsRecord::PropAttr(attr) => {
props.push(self.parse_property(attr)?);
b
}
_ => return self.invalid(r),
};
}
b = b.properties(props);
let b = b.build()?;
self.ctx.pop();
Ok(b)
}
fn parse_strans(&mut self, d0: u8, d1: u8) -> GdsResult<GdsStrans> {
let mut s = GdsStrans {
reflected: d0 & 0x80 != 0,
abs_mag: d1 & 0x04 != 0,
abs_angle: d1 & 0x02 != 0,
..Default::default()
};
loop {
match self.peek() {
GdsRecord::Mag(d) => {
s.mag = Some(*d);
self.next()?; }
GdsRecord::Angle(d) => {
s.angle = Some(*d);
self.next()?; }
_ => break,
}
}
Ok(s)
}
fn parse_property(&mut self, attr: i16) -> GdsResult<GdsProperty> {
self.ctx.push(GdsContext::Property);
let value = if let GdsRecord::PropValue(v) = self.next()? {
v
} else {
return self.fail("Gds Property without PropValue");
};
self.ctx.pop();
Ok(GdsProperty { attr, value })
}
fn parse_datetimes(&mut self, d: Vec<i16>) -> GdsResult<GdsDateTimes> {
if d.len() != 12 {
return self.fail("Invalid length GdsDateTimes");
}
Ok(GdsDateTimes {
modified: NaiveDate::from_ymd(d[0] as i32, d[1] as u32, d[2] as u32).and_hms(
d[3] as u32,
d[4] as u32,
d[5] as u32,
),
accessed: NaiveDate::from_ymd(d[6] as i32, d[7] as u32, d[8] as u32).and_hms(
d[9] as u32,
d[10] as u32,
d[11] as u32,
),
})
}
fn invalid<T>(&mut self, record: GdsRecord) -> GdsResult<T> {
Err(GdsError::Parse {
msg: "Invalid GDS Record".into(),
record,
recordnum: self.numread,
bytepos: self.rdr.pos(),
ctx: self.ctx.clone(),
})
}
fn err(&mut self, msg: impl Into<String>) -> GdsError {
GdsError::Parse {
msg: msg.into(),
record: self.peek().clone(), recordnum: self.numread,
bytepos: self.rdr.pos(),
ctx: self.ctx.clone(),
}
}
fn fail<T>(&mut self, msg: impl Into<String>) -> GdsResult<T> {
Err(self.err(msg))
}
#[cfg(test)]
pub fn write_records(&mut self, writer: &mut impl Write) -> GdsResult<()> {
loop {
let r = self.next()?;
if r == GdsRecord::EndLib {
return Ok(());
}
let entry: (usize, GdsRecord) = (self.numread, r);
let s = serde_json::to_string(&entry).unwrap();
write!(writer, "\t")?;
writer.write_all(s.as_bytes()).unwrap();
write!(writer, ",\n")?;
}
}
#[cfg(test)]
pub fn dump(gds: &str, json: &str) -> GdsResult<()> {
let mut me = Self::open(gds)?;
let mut w = BufWriter::new(File::create(json)?);
write!(w, "[\n")?;
me.write_records(&mut w)?;
write!(w, "]\n")?;
Ok(())
}
}