1use std::convert::{TryFrom, TryInto};
83use std::error::Error;
84use std::fs::File;
85#[allow(unused_imports)]
86use std::io::prelude::*;
87use std::io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write};
88use std::path::Path;
89use std::{fmt, mem, str};
90
91use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
93use chrono::prelude::*;
94use chrono::{Datelike, NaiveDate, NaiveDateTime};
95use derive_more::{self, Add, AddAssign, Sub, SubAssign};
96use num_derive::FromPrimitive;
97use num_traits::FromPrimitive;
98use serde::{Deserialize, Serialize};
99#[macro_use]
100extern crate derive_builder;
101
102use layout21utils as utils;
104#[doc(inline)]
105pub use utils::{SerdeFile, SerializationFormat};
106
107#[doc(hidden)]
109mod read;
110use read::{GdsParser, GdsScanner, GdsStructScan};
111#[doc(hidden)]
112mod write;
113use write::GdsWriter;
114#[cfg(test)]
115mod tests;
116
117#[derive(FromPrimitive, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
123pub enum GdsRecordType {
124 Header = 0x00,
125 BgnLib,
126 LibName,
127 Units,
128 EndLib,
129 BgnStruct,
130 StructName, EndStruct,
132 Boundary,
133 Path,
134 StructRef,
135 ArrayRef,
136 Text,
137 Layer,
138 DataType,
139 Width,
140 Xy,
141 EndElement,
142 StructRefName, ColRow,
144 TextNode, Node,
146 TextType,
147 Presentation,
148 Spacing, String,
150 Strans,
151 Mag,
152 Angle,
153 Uinteger, Ustring, RefLibs,
156 Fonts,
157 PathType,
158 Generations,
159 AttrTable,
160 StypTable, StrType, ElemFlags,
163 ElemKey, LinkType, LinkKeys, Nodetype,
167 PropAttr,
168 PropValue,
169 Box,
170 BoxType,
171 Plex,
172 BeginExtn, EndExtn, TapeNum,
175 TapeCode,
176 StrClass, Reserved, Format,
179 Mask,
180 EndMasks,
181 LibDirSize,
182 SrfName,
183 LibSecur,
184}
185impl GdsRecordType {
186 pub fn valid(&self) -> bool {
190 match self {
191 Self::TextNode | Self::Spacing | Self::Uinteger | Self::Ustring | Self::StypTable | Self::StrType | Self::ElemKey | Self::LinkType | Self::LinkKeys | Self::StrClass | Self::Reserved => false,
203 _ => true,
204 }
205 }
206}
207
208#[derive(FromPrimitive, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
211pub enum GdsDataType {
212 NoData = 0,
213 BitArray = 1,
214 I16 = 2,
215 I32 = 3,
216 F32 = 4,
217 F64 = 5,
218 Str = 6,
219}
220
221#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
225pub struct GdsRecordHeader {
226 rtype: GdsRecordType,
227 dtype: GdsDataType,
228 len: u16,
229}
230
231#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub enum GdsRecord {
241 Header { version: i16 },
242 BgnLib { dates: Vec<i16> }, LibName(String),
244 Units(f64, f64),
245 EndLib,
246 BgnStruct { dates: Vec<i16> }, StructName(String), StructRefName(String), EndStruct,
250 Boundary,
251 Path,
252 StructRef,
253 ArrayRef,
254 Text,
255 Layer(i16),
256 DataType(i16),
257 Width(i32),
258 Xy(Vec<i32>),
259 EndElement,
260 ColRow { cols: i16, rows: i16 },
261 Node,
262 TextType(i16),
263 Presentation(u8, u8),
264 String(String),
265 Strans(u8, u8),
266 Mag(f64),
267 Angle(f64),
268 RefLibs(String),
269 Fonts(String),
270 PathType(i16),
271 Generations(i16),
272 AttrTable(String),
273 ElemFlags(u8, u8),
274 Nodetype(i16),
275 PropAttr(i16),
276 PropValue(String),
277 Box,
278 BoxType(i16),
279 Plex(i32),
280 BeginExtn(i32),
281 EndExtn(i32),
282 TapeNum(i16),
283 TapeCode(Vec<i16>), Format(i16),
285 Mask(String),
286 EndMasks,
287 LibDirSize(i16),
288 SrfName(String),
289 LibSecur(i16),
290}
291
292pub struct GdsFloat64;
301impl GdsFloat64 {
302 pub fn decode(val: u64) -> f64 {
304 let neg = (val & 0x8000_0000_0000_0000) != 0;
306 let exp: i32 = ((val & 0x7F00_0000_0000_0000) >> 8 * 7) as i32 - 64;
308 let mantissa: u64 = val & 0x00FF_FFFF_FFFF_FFFF;
310 let mantissa: f64 = mantissa as f64 / 2f64.powi(8 * 7);
312 if neg {
314 -1.0 * mantissa * 16f64.powi(exp)
315 } else {
316 mantissa * 16f64.powi(exp)
317 }
318 }
319 pub fn encode(mut val: f64) -> u64 {
321 if val == 0.0 {
322 return 0;
323 };
324 let mut top: u8 = 0;
325 if val < 0.0 {
326 top = 0x80;
327 val = -val;
328 }
329 let fexp: f64 = 0.25 * val.log2();
330 let mut exponent = fexp.ceil() as i32;
331 if fexp == fexp.ceil() {
332 exponent += 1;
333 }
334 let mantissa: u64 = (val * 16_f64.powi(14 - exponent)).round() as u64;
335 top += (64 + exponent) as u8;
336 let result: u64 = (top as u64).wrapping_shl(56) | (mantissa & 0x00FF_FFFF_FFFF_FFFF);
337 result
338 }
339}
340
341#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
343pub struct Unsupported;
344
345#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
349pub struct GdsStrans {
350 pub reflected: bool,
354 pub abs_mag: bool,
356 pub abs_angle: bool,
358
359 #[serde(default, skip_serializing_if = "Option::is_none")]
362 pub mag: Option<f64>,
363 #[serde(default, skip_serializing_if = "Option::is_none")]
365 pub angle: Option<f64>,
366}
367
368#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
372pub struct GdsPresentation(u8, u8);
373
374#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
378pub struct GdsElemFlags(u8, u8);
379
380#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
385pub struct GdsPlex(i32);
386
387#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
404pub struct GdsUnits(f64, f64);
405impl GdsUnits {
406 pub fn new(num1: f64, num2: f64) -> Self {
408 Self(num1, num2)
409 }
410 pub fn db_unit(&self) -> f64 {
412 self.1
413 }
414 pub fn user_unit(&self) -> f64 {
416 self.0 / self.1
417 }
418}
419impl Default for GdsUnits {
420 fn default() -> Self {
424 Self(1e-3, 1e-9)
425 }
426}
427
428#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
432pub struct GdsPoint {
433 pub x: i32,
434 pub y: i32,
435}
436impl GdsPoint {
437 pub fn new(x: i32, y: i32) -> Self {
439 GdsPoint { x, y }
440 }
441 pub fn vec(pts: &[(i32, i32)]) -> Vec<Self> {
443 pts.iter().map(|pt| Self::new(pt.0, pt.1)).collect()
444 }
445 fn parse(from: &Vec<i32>) -> GdsResult<Self> {
447 if from.len() != 2 {
448 return Err(GdsError::Str(
449 "GdsPoint coordinate vector: Invalid number of elements".into(),
450 ));
451 }
452 Ok(GdsPoint {
453 x: from[0],
454 y: from[1],
455 })
456 }
457 fn parse_vec(from: &[i32]) -> GdsResult<Vec<GdsPoint>> {
459 if from.len() % 2 != 0 {
460 return Err(GdsError::Str(
461 "GdsPoint coordinate vector: Invalid number of elements".into(),
462 ));
463 }
464 let mut rv = Vec::with_capacity(from.len() / 2);
465 for i in 0..from.len() / 2 {
466 rv.push(GdsPoint {
467 x: from[i * 2],
468 y: from[i * 2 + 1],
469 });
470 }
471 Ok(rv)
472 }
473 fn flatten(&self) -> Vec<i32> {
475 vec![self.x, self.y]
476 }
477 fn flatten_vec(src: &Vec<GdsPoint>) -> Vec<i32> {
479 let mut rv = Vec::with_capacity(src.len() * 2);
480 for pt in src.iter() {
481 rv.push(pt.x);
482 rv.push(pt.y);
483 }
484 rv
485 }
486}
487#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
490pub enum GdsFormatType {
491 Archive,
493 Filtered(Vec<Unsupported>),
495}
496#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
502pub struct GdsProperty {
503 pub attr: i16,
505 pub value: String,
507}
508
509#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
518#[builder(pattern = "owned", setter(into), private)]
519pub struct GdsPath {
520 pub layer: i16,
523 pub datatype: i16,
525 pub xy: Vec<GdsPoint>,
527
528 #[serde(default, skip_serializing_if = "Option::is_none")]
530 #[builder(default, setter(strip_option))]
531 pub width: Option<i32>,
532 #[serde(default, skip_serializing_if = "Option::is_none")]
533 #[builder(default, setter(strip_option))]
534 pub path_type: Option<i16>,
535 #[serde(default, skip_serializing_if = "Option::is_none")]
536 #[builder(default, setter(strip_option))]
537 pub begin_extn: Option<i32>,
538 #[serde(default, skip_serializing_if = "Option::is_none")]
539 #[builder(default, setter(strip_option))]
540 pub end_extn: Option<i32>,
541 #[serde(default, skip_serializing_if = "Option::is_none")]
542 #[builder(default, setter(strip_option))]
543 pub elflags: Option<GdsElemFlags>,
544 #[serde(default, skip_serializing_if = "Option::is_none")]
545 #[builder(default, setter(strip_option))]
546 pub plex: Option<GdsPlex>,
547 #[serde(default, skip_serializing_if = "Vec::is_empty")]
548 #[builder(default, setter(strip_option))]
549 pub properties: Vec<GdsProperty>,
550}
551
552#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
567#[builder(pattern = "owned", setter(into), private)]
568pub struct GdsBoundary {
569 pub layer: i16,
572 pub datatype: i16,
574 pub xy: Vec<GdsPoint>,
576
577 #[serde(default, skip_serializing_if = "Option::is_none")]
579 #[builder(default, setter(strip_option))]
580 pub elflags: Option<GdsElemFlags>,
581 #[serde(default, skip_serializing_if = "Option::is_none")]
582 #[builder(default, setter(strip_option))]
583 pub plex: Option<GdsPlex>,
584 #[serde(default, skip_serializing_if = "Vec::is_empty")]
585 #[builder(default, setter(strip_option))]
586 pub properties: Vec<GdsProperty>,
587}
588#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
602#[builder(pattern = "owned", setter(into), private)]
603pub struct GdsStructRef {
604 pub name: String,
607 pub xy: GdsPoint,
609
610 #[serde(default, skip_serializing_if = "Option::is_none")]
612 #[builder(default, setter(strip_option))]
613 pub strans: Option<GdsStrans>,
615 #[serde(default, skip_serializing_if = "Option::is_none")]
616 #[builder(default, setter(strip_option))]
617 pub elflags: Option<GdsElemFlags>,
618 #[serde(default, skip_serializing_if = "Option::is_none")]
619 #[builder(default, setter(strip_option))]
620 pub plex: Option<GdsPlex>,
621 #[serde(default, skip_serializing_if = "Vec::is_empty")]
622 #[builder(default, setter(strip_option))]
623 pub properties: Vec<GdsProperty>,
624}
625#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
636#[builder(pattern = "owned", setter(into), private)]
637pub struct GdsArrayRef {
638 pub name: String,
641 pub xy: [GdsPoint; 3],
643 pub cols: i16,
645 pub rows: i16,
647 #[serde(default, skip_serializing_if = "Option::is_none")]
650 #[builder(default)]
651 pub strans: Option<GdsStrans>,
652 #[serde(default, skip_serializing_if = "Option::is_none")]
653 #[builder(default, setter(strip_option))]
654 pub elflags: Option<GdsElemFlags>,
655 #[serde(default, skip_serializing_if = "Option::is_none")]
656 #[builder(default, setter(strip_option))]
657 pub plex: Option<GdsPlex>,
658 #[serde(default, skip_serializing_if = "Vec::is_empty")]
659 #[builder(default, setter(strip_option))]
660 pub properties: Vec<GdsProperty>,
661}
662#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
671#[builder(pattern = "owned", setter(into), private)]
672pub struct GdsTextElem {
673 pub string: String,
676 pub layer: i16,
678 pub texttype: i16,
680 pub xy: GdsPoint,
682
683 #[serde(default, skip_serializing_if = "Option::is_none")]
685 #[builder(default, setter(strip_option))]
686 pub presentation: Option<GdsPresentation>,
687 #[serde(default, skip_serializing_if = "Option::is_none")]
688 #[builder(default, setter(strip_option))]
689 pub path_type: Option<i16>,
690 #[serde(default, skip_serializing_if = "Option::is_none")]
691 #[builder(default, setter(strip_option))]
692 pub width: Option<i32>,
693 #[serde(default, skip_serializing_if = "Option::is_none")]
694 #[builder(default)]
695 pub strans: Option<GdsStrans>,
697 #[serde(default, skip_serializing_if = "Option::is_none")]
698 #[builder(default, setter(strip_option))]
699 pub elflags: Option<GdsElemFlags>,
700 #[serde(default, skip_serializing_if = "Option::is_none")]
701 #[builder(default, setter(strip_option))]
702 pub plex: Option<GdsPlex>,
703 #[serde(default, skip_serializing_if = "Vec::is_empty")]
704 #[builder(default, setter(strip_option))]
705 pub properties: Vec<GdsProperty>,
706}
707#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
716#[builder(pattern = "owned", setter(into), private)]
717pub struct GdsNode {
718 pub layer: i16,
721 pub nodetype: i16,
723 pub xy: Vec<GdsPoint>,
725
726 #[serde(default, skip_serializing_if = "Option::is_none")]
728 #[builder(default, setter(strip_option))]
729 pub elflags: Option<GdsElemFlags>,
730 #[serde(default, skip_serializing_if = "Option::is_none")]
731 #[builder(default, setter(strip_option))]
732 pub plex: Option<GdsPlex>,
733 #[serde(default, skip_serializing_if = "Vec::is_empty")]
734 #[builder(default, setter(strip_option))]
735 pub properties: Vec<GdsProperty>,
736}
737#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
746#[builder(pattern = "owned", setter(into), private)]
747pub struct GdsBox {
748 pub layer: i16,
751 pub boxtype: i16,
753 pub xy: [GdsPoint; 5],
755
756 #[serde(default, skip_serializing_if = "Option::is_none")]
758 #[builder(default, setter(strip_option))]
759 pub elflags: Option<GdsElemFlags>,
760 #[serde(default, skip_serializing_if = "Option::is_none")]
761 #[builder(default, setter(strip_option))]
762 pub plex: Option<GdsPlex>,
763 #[serde(default, skip_serializing_if = "Vec::is_empty")]
764 #[builder(default, setter(strip_option))]
765 pub properties: Vec<GdsProperty>,
766}
767#[derive(derive_more::From, Debug, Clone, Deserialize, Serialize, PartialEq)]
780pub enum GdsElement {
781 GdsBoundary(GdsBoundary),
782 GdsPath(GdsPath),
783 GdsStructRef(GdsStructRef),
784 GdsArrayRef(GdsArrayRef),
785 GdsTextElem(GdsTextElem),
786 GdsNode(GdsNode),
787 GdsBox(GdsBox),
788}
789
790#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Add, AddAssign, Sub, SubAssign)]
795pub struct GdsStats {
796 libraries: usize,
797 structs: usize,
798 boundaries: usize,
799 paths: usize,
800 struct_refs: usize,
801 array_refs: usize,
802 text_elems: usize,
803 nodes: usize,
804 boxes: usize,
805}
806
807#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
809pub struct GdsDateTimes {
810 pub modified: NaiveDateTime,
812 pub accessed: NaiveDateTime,
814}
815
816impl Default for GdsDateTimes {
817 fn default() -> Self {
819 let now = Utc::now().naive_utc();
820 Self {
821 modified: now.clone(),
822 accessed: now.clone(),
823 }
824 }
825}
826#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
845#[builder(pattern = "owned", setter(into), private)]
846pub struct GdsStruct {
847 pub name: String,
849 pub dates: GdsDateTimes,
851 pub elems: Vec<GdsElement>,
853}
854impl GdsStruct {
855 pub fn new(name: impl Into<String>) -> Self {
857 Self {
858 name: name.into(),
859 ..Default::default()
860 }
861 }
862 fn stats(&self) -> GdsStats {
864 let mut stats = GdsStats::default();
865 stats.structs += 1;
866 for elem in &self.elems {
867 use GdsElement::*;
868 match elem {
869 GdsBoundary(_) => stats.boundaries += 1,
870 GdsPath(_) => stats.paths += 1,
871 GdsStructRef(_) => stats.struct_refs += 1,
872 GdsArrayRef(_) => stats.array_refs += 1,
873 GdsTextElem(_) => stats.text_elems += 1,
874 GdsNode(_) => stats.nodes += 1,
875 GdsBox(_) => stats.boxes += 1,
876 };
877 }
878 stats
879 }
880}
881
882#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
900#[builder(pattern = "owned", setter(into), private)]
901pub struct GdsLibrary {
902 pub name: String,
905 pub version: i16,
907 pub dates: GdsDateTimes,
909 pub units: GdsUnits,
911 pub structs: Vec<GdsStruct>,
913
914 #[serde(default, skip_serializing)]
916 #[builder(default)]
917 pub libdirsize: Unsupported,
918 #[serde(default, skip_serializing)]
919 #[builder(default)]
920 pub srfname: Unsupported,
921 #[serde(default, skip_serializing)]
922 #[builder(default)]
923 pub libsecur: Unsupported,
924 #[serde(default, skip_serializing)]
925 #[builder(default)]
926 pub reflibs: Unsupported,
927 #[serde(default, skip_serializing)]
928 #[builder(default)]
929 pub fonts: Unsupported,
930 #[serde(default, skip_serializing)]
931 #[builder(default)]
932 pub attrtable: Unsupported,
933 #[serde(default, skip_serializing)]
934 #[builder(default)]
935 pub generations: Unsupported,
936 #[serde(default, skip_serializing)]
937 #[builder(default)]
938 pub format_type: Unsupported,
939}
940impl GdsLibrary {
941 pub fn new(name: impl Into<String>) -> Self {
943 Self {
944 name: name.into(),
945 version: 3,
946 ..Default::default()
947 }
948 }
949 pub fn load(fname: impl AsRef<Path>) -> GdsResult<GdsLibrary> {
951 GdsParser::open(fname)?.parse_lib()
953 }
954 pub fn from_bytes(bytes: Vec<u8>) -> GdsResult<GdsLibrary> {
956 GdsParser::from_bytes(bytes)?.parse_lib()
958 }
959 #[allow(dead_code)] fn scan(fname: impl AsRef<Path>) -> GdsResult<Vec<GdsStructScan>> {
963 GdsScanner::scan(fname)
964 }
965 pub fn stats(&self) -> GdsStats {
968 let mut stats = GdsStats::default();
969 stats.libraries += 1;
970 for strukt in self.structs.iter() {
971 stats += strukt.stats();
972 }
973 stats
974 }
975 pub fn save(&self, fname: impl AsRef<Path>) -> GdsResult<()> {
977 let mut wr = GdsWriter::open(fname)?;
978 wr.write_lib(self)
979 }
980 pub fn write(&self, file: impl Write) -> GdsResult<()> {
982 let mut wr = GdsWriter::new(file);
983 wr.write_lib(self)
984 }
985}
986impl SerdeFile for GdsLibrary {}
988impl SerdeFile for GdsStruct {}
989
990pub struct GdsLayerSpec {
1000 pub layer: i16,
1002 pub xtype: i16,
1004}
1005pub trait HasLayer {
1008 fn layerspec(&self) -> GdsLayerSpec;
1009}
1010impl GdsLayerSpec {
1011 pub fn new(layer: i16, xtype: i16) -> GdsLayerSpec {
1013 GdsLayerSpec { layer, xtype }
1014 }
1015}
1016impl HasLayer for GdsBoundary {
1017 fn layerspec(&self) -> GdsLayerSpec {
1018 GdsLayerSpec::new(self.layer, self.datatype)
1019 }
1020}
1021impl HasLayer for GdsTextElem {
1022 fn layerspec(&self) -> GdsLayerSpec {
1023 GdsLayerSpec::new(self.layer, self.texttype)
1024 }
1025}
1026impl HasLayer for GdsNode {
1027 fn layerspec(&self) -> GdsLayerSpec {
1028 GdsLayerSpec::new(self.layer, self.nodetype)
1029 }
1030}
1031impl HasLayer for GdsBox {
1032 fn layerspec(&self) -> GdsLayerSpec {
1033 GdsLayerSpec::new(self.layer, self.boxtype)
1034 }
1035}
1036impl HasLayer for GdsPath {
1037 fn layerspec(&self) -> GdsLayerSpec {
1038 GdsLayerSpec::new(self.layer, self.datatype)
1039 }
1040}
1041
1042#[derive(Debug, Clone)]
1044pub enum GdsContext {
1045 Library,
1046 Struct,
1047 StructRef,
1048 ArrayRef,
1049 Boundary,
1050 Box,
1051 Path,
1052 Text,
1053 Node,
1054 Property,
1055}
1056pub type GdsResult<T> = Result<T, GdsError>;
1058#[derive(Debug)]
1062pub enum GdsError {
1063 RecordDecode(GdsRecordType, GdsDataType, u16),
1065 RecordLen(usize),
1067 InvalidDataType(u8),
1069 InvalidRecordType(u8),
1071 Unsupported(Option<GdsRecord>, Option<GdsContext>),
1073 Parse {
1075 msg: String,
1076 record: GdsRecord,
1077 recordnum: usize,
1078 bytepos: u64,
1079 ctx: Vec<GdsContext>,
1080 },
1081 Boxed(Box<dyn Error>),
1083 Str(String),
1085}
1086impl std::fmt::Display for GdsError {
1087 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1091 write!(f, "{:?}", self)
1092 }
1093}
1094impl std::error::Error for GdsError {}
1095impl From<std::io::Error> for GdsError {
1096 fn from(e: std::io::Error) -> Self {
1097 Self::Boxed(Box::new(e))
1098 }
1099}
1100impl From<std::str::Utf8Error> for GdsError {
1101 fn from(e: std::str::Utf8Error) -> Self {
1102 Self::Boxed(Box::new(e))
1103 }
1104}
1105impl From<String> for GdsError {
1106 fn from(e: String) -> Self {
1107 GdsError::Str(e)
1108 }
1109}
1110impl From<&str> for GdsError {
1111 fn from(e: &str) -> Self {
1112 GdsError::Str(e.to_string())
1113 }
1114}
1115#[cfg(any(test, feature = "selftest"))]
1116pub fn roundtrip(lib: &GdsLibrary) -> GdsResult<()> {
1118 use tempfile::tempfile;
1119
1120 let mut file = tempfile()?;
1122 lib.write(&mut file)?;
1123
1124 file.seek(SeekFrom::Start(0))?;
1126 let mut bytes = Vec::new();
1127 file.read_to_end(&mut bytes)?;
1128 let lib2 = GdsLibrary::from_bytes(bytes)?;
1129
1130 assert_eq!(*lib, lib2);
1132 Ok(())
1133}