1use std::io::Read;
2
3use rgb::Rgb;
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
7#[serde(rename = "layer-properties")]
8pub struct KlayoutLayerProperties {
9 pub name: String,
10 #[serde(rename = "properties")]
11 #[serde(default)]
12 pub layers: Vec<LayerProperties>,
13 #[serde(rename = "custom-dither-pattern")]
14 #[serde(default)]
15 pub custom_dither_patterns: Vec<CustomDitherPattern>,
16 #[serde(rename = "custom-line-style")]
17 #[serde(default)]
18 pub custom_line_styles: Vec<CustomLineStyle>,
19}
20
21#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
22#[serde(rename = "properties")]
23#[serde(rename_all = "kebab-case")]
24pub struct LayerProperties {
25 pub name: String,
26 pub source: String,
27 #[serde(with = "lyp_rgb_format")]
28 pub frame_color: Rgb<u8>,
29 #[serde(with = "lyp_rgb_format")]
30 pub fill_color: Rgb<u8>,
31 pub frame_brightness: u8,
32 pub fill_brightness: u8,
33 pub dither_pattern: String,
34 pub line_style: String,
35 pub valid: bool,
36 pub visible: bool,
37 pub transparent: bool,
38 pub width: i32,
39 pub marked: bool,
40 pub xfill: bool,
41 pub animation: i32,
42}
43
44#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
45#[serde(rename = "custom-dither-pattern")]
46pub struct CustomDitherPattern {
47 pub pattern: DitherPattern,
48 pub order: i32,
49 pub name: String,
50}
51
52#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
53#[serde(rename = "pattern")]
54pub struct DitherPattern {
55 #[serde(rename = "line")]
56 pub lines: Vec<String>,
57}
58
59#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
60#[serde(rename = "custom-line-style")]
61pub struct CustomLineStyle {
62 pub pattern: String,
63 pub order: i32,
64 pub name: String,
65}
66
67pub fn from_str(lyp: &str) -> Result<KlayoutLayerProperties, serde_xml_rs::Error> {
68 serde_xml_rs::from_str(lyp)
69}
70
71pub fn from_reader<R: Read>(reader: R) -> Result<KlayoutLayerProperties, serde_xml_rs::Error> {
72 serde_xml_rs::from_reader(reader)
73}
74
75mod lyp_rgb_format {
76 use crate::*;
77 use serde::{Deserializer, Serializer};
78
79 pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Rgb<u8>, D::Error>
80 where
81 D: Deserializer<'de>,
82 {
83 let s = String::deserialize(deserializer)?;
84 let r = u8::from_str_radix(&s[1..3], 16).map_err(serde::de::Error::custom)?;
85 let g = u8::from_str_radix(&s[3..5], 16).map_err(serde::de::Error::custom)?;
86 let b = u8::from_str_radix(&s[5..7], 16).map_err(serde::de::Error::custom)?;
87
88 Ok(Rgb { r, g, b })
89 }
90
91 pub(crate) fn serialize<S>(value: &Rgb<u8>, serializer: S) -> Result<S::Ok, S::Error>
92 where
93 S: Serializer,
94 {
95 String::serialize(&format!("#{}{}{}", value.r, value.g, value.b), serializer)
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::*;
102 const SKY130_LYP: &str =
103 include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/examples/sky130.lyp"));
104
105 #[test]
106 fn parse_sky130_lyp() {
107 let lyp = from_str(SKY130_LYP).unwrap();
108 println!("{lyp:#?}");
109 }
110}