virtuoso_cli/client/
layout_ops.rs1use crate::client::bridge::escape_skill_string;
2
3#[cfg(test)]
4mod tests {
5 use super::*;
6
7 fn ops() -> LayoutOps {
8 LayoutOps::new()
9 }
10
11 #[test]
12 fn create_rect_skill_format() {
13 let s = ops().create_rect("M1", "drawing", &[(0, 0), (100, 200)]);
14 assert_eq!(
15 s,
16 r#"rodCreateRect(?layer "M1" ?purpose "drawing" ?bBox ((0 0) (100 200)))"#
17 );
18 }
19
20 #[test]
21 fn create_rect_escapes_layer() {
22 let s = ops().create_rect(r#"M"1"#, "drawing", &[(0, 0), (1, 1)]);
23 assert!(s.contains(r#""M\"1""#), "layer must be escaped: {s}");
24 }
25
26 #[test]
27 fn create_polygon_skill_format() {
28 let pts = vec![(0, 0), (10, 0), (10, 10)];
29 let s = ops().create_polygon("poly", "drawing", &pts);
30 assert!(s.contains("rodCreatePolygon"), "{s}");
31 assert!(s.contains("0 0"), "{s}");
32 assert!(s.contains("10 10"), "{s}");
33 }
34
35 #[test]
36 fn create_path_includes_width() {
37 let pts = vec![(0, 0), (50, 0)];
38 let s = ops().create_path("M2", "drawing", 5, &pts);
39 assert!(s.contains("?width 5"), "{s}");
40 }
41
42 #[test]
43 fn create_instance_orientation() {
44 let s = ops().create_instance("tsmc", "nmos", "layout", (10, 20), "MY");
45 assert!(s.contains("\"MY\""), "orient must appear: {s}");
46 assert!(
47 s.contains("10:20") || s.contains("10 20"),
48 "origin must appear: {s}"
49 );
50 }
51}
52
53#[derive(Default)]
54pub struct LayoutOps;
55
56impl LayoutOps {
57 pub fn new() -> Self {
58 Self
59 }
60
61 pub fn create_rect(&self, layer: &str, purpose: &str, bbox: &[(i64, i64); 2]) -> String {
64 let layer = escape_skill_string(layer);
65 let purpose = escape_skill_string(purpose);
66 let ((x1, y1), (x2, y2)) = (bbox[0], bbox[1]);
67 format!(
68 r#"rodCreateRect(?layer "{layer}" ?purpose "{purpose}" ?bBox (({x1} {y1}) ({x2} {y2})))"#
69 )
70 }
71
72 pub fn create_polygon(&self, layer: &str, purpose: &str, points: &[(i64, i64)]) -> String {
73 let layer = escape_skill_string(layer);
74 let purpose = escape_skill_string(purpose);
75 let pts: String = points
76 .iter()
77 .map(|(x, y)| format!("{x} {y}"))
78 .collect::<Vec<_>>()
79 .join(" ");
80 format!(r#"rodCreatePolygon(?layer "{layer}" ?purpose "{purpose}" ?points list({pts}))"#)
81 }
82
83 pub fn create_path(
84 &self,
85 layer: &str,
86 purpose: &str,
87 width: i64,
88 points: &[(i64, i64)],
89 ) -> String {
90 let layer = escape_skill_string(layer);
91 let purpose = escape_skill_string(purpose);
92 let pts: String = points
93 .iter()
94 .map(|(x, y)| format!("{x} {y}"))
95 .collect::<Vec<_>>()
96 .join(" ");
97 format!(
98 r#"rodCreatePath(?layer "{layer}" ?purpose "{purpose}" ?width {width} ?points list({pts}))"#
99 )
100 }
101
102 pub fn create_via(&self, via_def: &str, origin: (i64, i64)) -> String {
103 let via_def = escape_skill_string(via_def);
104 let (x, y) = origin;
105 format!(r#"rodCreateVia(?viaHeader "{via_def}" ?origin {x}:{y})"#)
106 }
107
108 pub fn create_label(
109 &self,
110 layer: &str,
111 _purpose: &str,
112 text: &str,
113 origin: (i64, i64),
114 ) -> String {
115 let layer = escape_skill_string(layer);
116 let text = escape_skill_string(text);
117 let (x, y) = origin;
118 format!(
119 r#"dbCreateLabel(dbGetCurCellView() dbGetLayerByName(dbGetCurCellView() "{layer}") {x}:{y} "{text}" "centerCenter" "R0" "stick" 0.0625)"#
120 )
121 }
122
123 pub fn create_instance(
124 &self,
125 lib: &str,
126 cell: &str,
127 view: &str,
128 origin: (i64, i64),
129 orient: &str,
130 ) -> String {
131 let lib = escape_skill_string(lib);
132 let cell = escape_skill_string(cell);
133 let view = escape_skill_string(view);
134 let orient = escape_skill_string(orient);
135 let (x, y) = origin;
136 format!(
137 r#"dbCreateInst(dbOpenCellViewByType("{lib}" "{cell}" "{view}" nil "r") nil nil {x}:{y} "{orient}" 1)"#
138 )
139 }
140
141 pub fn set_active_lpp(&self, layer: &str, purpose: &str) -> String {
142 let layer = escape_skill_string(layer);
143 let purpose = escape_skill_string(purpose);
144 format!(r#"leSetEntryLayer(list("{layer}" "{purpose}"))"#)
145 }
146
147 pub fn fit_view(&self) -> String {
148 r#"hiRedraw() hiZoomBox(hiGetCurrentWindow() geGetWindowBox(hiGetCurrentWindow()) geGetEditCellView()~>bBox)"#.into()
149 }
150
151 pub fn read_summary(&self) -> String {
152 r#"let((cv) cv = geGetEditCellView() list(cv~>libName cv~>cellName cv~>viewName cv~>bBox length(cv~>instances) length(cv~>nets)))"#.into()
153 }
154
155 pub fn read_geometry(&self, layer: &str, purpose: &str) -> String {
156 let layer = escape_skill_string(layer);
157 let purpose = escape_skill_string(purpose);
158 format!(
159 r#"let((cv shapes) cv = geGetEditCellView() shapes = nil foreach(shape cv~>shapes when(shape~>lpp == list("{layer}" "{purpose}") shapes = cons(shape~>bBox shapes))) shapes)"#
160 )
161 }
162
163 pub fn delete_shapes_on_layer(&self, layer: &str, purpose: &str) -> String {
164 let layer = escape_skill_string(layer);
165 let purpose = escape_skill_string(purpose);
166 format!(
167 r#"let((cv) cv = geGetEditCellView() foreach(shape cv~>shapes when(shape~>lpp == list("{layer}" "{purpose}") dbDeleteObject(shape))))"#
168 )
169 }
170
171 pub fn highlight_net(&self, net_name: &str) -> String {
172 let net_name = escape_skill_string(net_name);
173 format!(
174 r#"let((cv net) cv = geGetEditCellView() net = dbFindNetByName(cv "{net_name}") when(net hiHighlight(net)))"#
175 )
176 }
177}