1use std::fmt;
7use std::io::{Read, Write};
8
9use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
10
11use crate::error::Result;
12use crate::io::reader::decode_windows_1252;
13use crate::io::writer::encode_windows_1252;
14use crate::types::{Coord, ParameterCollection};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
21#[repr(u16)]
22pub enum RuleKind {
23 #[default]
25 Clearance = 0,
26 ParallelSegment = 1,
28 Width = 2,
30 Length = 3,
32 MatchedLengths = 4,
34 StubLength = 5,
36 PlaneConnect = 6,
38 RoutingTopology = 7,
40 RoutingPriority = 8,
42 RoutingLayers = 9,
44 RoutingCorners = 10,
46 RoutingVias = 11,
48 PlaneClearance = 12,
50 SolderMaskExpansion = 13,
52 PasteMaskExpansion = 14,
54 ShortCircuit = 15,
56 UnRoutedNet = 16,
58 ViasUnderSMD = 17,
60 MaximumViaCount = 18,
62 MinimumAnnularRing = 19,
64 PolygonConnect = 20,
66 AcuteAngle = 21,
68 RoomDefinition = 22,
70 SMDToCorner = 23,
72 ComponentClearance = 24,
74 ComponentOrientations = 25,
76 PermittedLayers = 26,
78 NetsToIgnore = 27,
80 SignalStimulus = 28,
82 OvershootFalling = 29,
84 OvershootRising = 30,
86 UndershootFalling = 31,
88 UndershootRising = 32,
90 MaxMinImpedance = 33,
92 SignalTopValue = 34,
94 SignalBaseValue = 35,
96 FlightTimeRising = 36,
98 FlightTimeFalling = 37,
100 LayerStack = 38,
102 SlopeRising = 39,
104 SlopeFalling = 40,
106 SupplyNets = 41,
108 HoleSize = 42,
110 TestPointStyle = 43,
112 TestPointUsage = 44,
114 UnconnectedPin = 45,
116 SMDToPlane = 46,
118 SMDNeckDown = 47,
120 LayerPairs = 48,
122 FanoutControl = 49,
124 Height = 50,
126 DiffPairsRouting = 51,
128 HoleToHoleClearance = 52,
130 MinimumSolderMaskSliver = 53,
132 SilkToSolderMaskClearance = 54,
134 SilkToSilkClearance = 55,
136 NetAntennae = 56,
138 AssemblyTestpoint = 57,
140 AssemblyTestPointUsage = 58,
142 SilkToBoardRegionClearance = 59,
144 UnpouredPolygon = 62,
146 Unknown(u16),
148}
149
150impl RuleKind {
151 pub fn from_id(id: u16) -> Self {
153 match id {
154 0 => RuleKind::Clearance,
155 1 => RuleKind::ParallelSegment,
156 2 => RuleKind::Width,
157 3 => RuleKind::Length,
158 4 => RuleKind::MatchedLengths,
159 5 => RuleKind::StubLength,
160 6 => RuleKind::PlaneConnect,
161 7 => RuleKind::RoutingTopology,
162 8 => RuleKind::RoutingPriority,
163 9 => RuleKind::RoutingLayers,
164 10 => RuleKind::RoutingCorners,
165 11 => RuleKind::RoutingVias,
166 12 => RuleKind::PlaneClearance,
167 13 => RuleKind::SolderMaskExpansion,
168 14 => RuleKind::PasteMaskExpansion,
169 15 => RuleKind::ShortCircuit,
170 16 => RuleKind::UnRoutedNet,
171 17 => RuleKind::ViasUnderSMD,
172 18 => RuleKind::MaximumViaCount,
173 19 => RuleKind::MinimumAnnularRing,
174 20 => RuleKind::PolygonConnect,
175 21 => RuleKind::AcuteAngle,
176 22 => RuleKind::RoomDefinition,
177 23 => RuleKind::SMDToCorner,
178 24 => RuleKind::ComponentClearance,
179 25 => RuleKind::ComponentOrientations,
180 26 => RuleKind::PermittedLayers,
181 27 => RuleKind::NetsToIgnore,
182 28 => RuleKind::SignalStimulus,
183 29 => RuleKind::OvershootFalling,
184 30 => RuleKind::OvershootRising,
185 31 => RuleKind::UndershootFalling,
186 32 => RuleKind::UndershootRising,
187 33 => RuleKind::MaxMinImpedance,
188 34 => RuleKind::SignalTopValue,
189 35 => RuleKind::SignalBaseValue,
190 36 => RuleKind::FlightTimeRising,
191 37 => RuleKind::FlightTimeFalling,
192 38 => RuleKind::LayerStack,
193 39 => RuleKind::SlopeRising,
194 40 => RuleKind::SlopeFalling,
195 41 => RuleKind::SupplyNets,
196 42 => RuleKind::HoleSize,
197 43 => RuleKind::TestPointStyle,
198 44 => RuleKind::TestPointUsage,
199 45 => RuleKind::UnconnectedPin,
200 46 => RuleKind::SMDToPlane,
201 47 => RuleKind::SMDNeckDown,
202 48 => RuleKind::LayerPairs,
203 49 => RuleKind::FanoutControl,
204 50 => RuleKind::Height,
205 51 => RuleKind::DiffPairsRouting,
206 52 => RuleKind::HoleToHoleClearance,
207 53 => RuleKind::MinimumSolderMaskSliver,
208 54 => RuleKind::SilkToSolderMaskClearance,
209 55 => RuleKind::SilkToSilkClearance,
210 56 => RuleKind::NetAntennae,
211 57 => RuleKind::AssemblyTestpoint,
212 58 => RuleKind::AssemblyTestPointUsage,
213 59 => RuleKind::SilkToBoardRegionClearance,
214 62 => RuleKind::UnpouredPolygon,
215 other => RuleKind::Unknown(other),
216 }
217 }
218
219 pub fn to_id(self) -> u16 {
221 match self {
222 RuleKind::Clearance => 0,
223 RuleKind::ParallelSegment => 1,
224 RuleKind::Width => 2,
225 RuleKind::Length => 3,
226 RuleKind::MatchedLengths => 4,
227 RuleKind::StubLength => 5,
228 RuleKind::PlaneConnect => 6,
229 RuleKind::RoutingTopology => 7,
230 RuleKind::RoutingPriority => 8,
231 RuleKind::RoutingLayers => 9,
232 RuleKind::RoutingCorners => 10,
233 RuleKind::RoutingVias => 11,
234 RuleKind::PlaneClearance => 12,
235 RuleKind::SolderMaskExpansion => 13,
236 RuleKind::PasteMaskExpansion => 14,
237 RuleKind::ShortCircuit => 15,
238 RuleKind::UnRoutedNet => 16,
239 RuleKind::ViasUnderSMD => 17,
240 RuleKind::MaximumViaCount => 18,
241 RuleKind::MinimumAnnularRing => 19,
242 RuleKind::PolygonConnect => 20,
243 RuleKind::AcuteAngle => 21,
244 RuleKind::RoomDefinition => 22,
245 RuleKind::SMDToCorner => 23,
246 RuleKind::ComponentClearance => 24,
247 RuleKind::ComponentOrientations => 25,
248 RuleKind::PermittedLayers => 26,
249 RuleKind::NetsToIgnore => 27,
250 RuleKind::SignalStimulus => 28,
251 RuleKind::OvershootFalling => 29,
252 RuleKind::OvershootRising => 30,
253 RuleKind::UndershootFalling => 31,
254 RuleKind::UndershootRising => 32,
255 RuleKind::MaxMinImpedance => 33,
256 RuleKind::SignalTopValue => 34,
257 RuleKind::SignalBaseValue => 35,
258 RuleKind::FlightTimeRising => 36,
259 RuleKind::FlightTimeFalling => 37,
260 RuleKind::LayerStack => 38,
261 RuleKind::SlopeRising => 39,
262 RuleKind::SlopeFalling => 40,
263 RuleKind::SupplyNets => 41,
264 RuleKind::HoleSize => 42,
265 RuleKind::TestPointStyle => 43,
266 RuleKind::TestPointUsage => 44,
267 RuleKind::UnconnectedPin => 45,
268 RuleKind::SMDToPlane => 46,
269 RuleKind::SMDNeckDown => 47,
270 RuleKind::LayerPairs => 48,
271 RuleKind::FanoutControl => 49,
272 RuleKind::Height => 50,
273 RuleKind::DiffPairsRouting => 51,
274 RuleKind::HoleToHoleClearance => 52,
275 RuleKind::MinimumSolderMaskSliver => 53,
276 RuleKind::SilkToSolderMaskClearance => 54,
277 RuleKind::SilkToSilkClearance => 55,
278 RuleKind::NetAntennae => 56,
279 RuleKind::AssemblyTestpoint => 57,
280 RuleKind::AssemblyTestPointUsage => 58,
281 RuleKind::SilkToBoardRegionClearance => 59,
282 RuleKind::UnpouredPolygon => 62,
283 RuleKind::Unknown(id) => id,
284 }
285 }
286
287 pub fn name(&self) -> &'static str {
289 match self {
290 RuleKind::Clearance => "Clearance",
291 RuleKind::ParallelSegment => "ParallelSegment",
292 RuleKind::Width => "Width",
293 RuleKind::Length => "Length",
294 RuleKind::MatchedLengths => "MatchedLengths",
295 RuleKind::StubLength => "StubLength",
296 RuleKind::PlaneConnect => "PlaneConnect",
297 RuleKind::RoutingTopology => "RoutingTopology",
298 RuleKind::RoutingPriority => "RoutingPriority",
299 RuleKind::RoutingLayers => "RoutingLayers",
300 RuleKind::RoutingCorners => "RoutingCorners",
301 RuleKind::RoutingVias => "RoutingVias",
302 RuleKind::PlaneClearance => "PlaneClearance",
303 RuleKind::SolderMaskExpansion => "SolderMaskExpansion",
304 RuleKind::PasteMaskExpansion => "PasteMaskExpansion",
305 RuleKind::ShortCircuit => "ShortCircuit",
306 RuleKind::UnRoutedNet => "UnRoutedNet",
307 RuleKind::ViasUnderSMD => "ViasUnderSMD",
308 RuleKind::MaximumViaCount => "MaximumViaCount",
309 RuleKind::MinimumAnnularRing => "MinimumAnnularRing",
310 RuleKind::PolygonConnect => "PolygonConnect",
311 RuleKind::AcuteAngle => "AcuteAngle",
312 RuleKind::RoomDefinition => "RoomDefinition",
313 RuleKind::SMDToCorner => "SMDToCorner",
314 RuleKind::ComponentClearance => "ComponentClearance",
315 RuleKind::ComponentOrientations => "ComponentOrientations",
316 RuleKind::PermittedLayers => "PermittedLayers",
317 RuleKind::NetsToIgnore => "NetsToIgnore",
318 RuleKind::SignalStimulus => "SignalStimulus",
319 RuleKind::OvershootFalling => "OvershootFalling",
320 RuleKind::OvershootRising => "OvershootRising",
321 RuleKind::UndershootFalling => "UndershootFalling",
322 RuleKind::UndershootRising => "UndershootRising",
323 RuleKind::MaxMinImpedance => "MaxMinImpedance",
324 RuleKind::SignalTopValue => "SignalTopValue",
325 RuleKind::SignalBaseValue => "SignalBaseValue",
326 RuleKind::FlightTimeRising => "FlightTimeRising",
327 RuleKind::FlightTimeFalling => "FlightTimeFalling",
328 RuleKind::LayerStack => "LayerStack",
329 RuleKind::SlopeRising => "SlopeRising",
330 RuleKind::SlopeFalling => "SlopeFalling",
331 RuleKind::SupplyNets => "SupplyNets",
332 RuleKind::HoleSize => "HoleSize",
333 RuleKind::TestPointStyle => "Testpoint",
334 RuleKind::TestPointUsage => "TestPointUsage",
335 RuleKind::UnconnectedPin => "UnConnectedPin",
336 RuleKind::SMDToPlane => "SMDToPlane",
337 RuleKind::SMDNeckDown => "SMDNeckDown",
338 RuleKind::LayerPairs => "LayerPairs",
339 RuleKind::FanoutControl => "FanoutControl",
340 RuleKind::Height => "Height",
341 RuleKind::DiffPairsRouting => "DiffPairsRouting",
342 RuleKind::HoleToHoleClearance => "HoleToHoleClearance",
343 RuleKind::MinimumSolderMaskSliver => "MinimumSolderMaskSliver",
344 RuleKind::SilkToSolderMaskClearance => "SilkToSolderMaskClearance",
345 RuleKind::SilkToSilkClearance => "SilkToSilkClearance",
346 RuleKind::NetAntennae => "NetAntennae",
347 RuleKind::AssemblyTestpoint => "AssemblyTestpoint",
348 RuleKind::AssemblyTestPointUsage => "AssemblyTestPointUsage",
349 RuleKind::SilkToBoardRegionClearance => "SilkToBoardRegionClearance",
350 RuleKind::UnpouredPolygon => "UnpouredPolygon",
351 RuleKind::Unknown(_) => "Unknown",
352 }
353 }
354
355 pub fn from_name(name: &str) -> Option<Self> {
357 match name {
358 "Clearance" => Some(RuleKind::Clearance),
359 "ParallelSegment" => Some(RuleKind::ParallelSegment),
360 "Width" => Some(RuleKind::Width),
361 "Length" => Some(RuleKind::Length),
362 "MatchedLengths" => Some(RuleKind::MatchedLengths),
363 "StubLength" => Some(RuleKind::StubLength),
364 "PlaneConnect" => Some(RuleKind::PlaneConnect),
365 "RoutingTopology" => Some(RuleKind::RoutingTopology),
366 "RoutingPriority" => Some(RuleKind::RoutingPriority),
367 "RoutingLayers" => Some(RuleKind::RoutingLayers),
368 "RoutingCorners" => Some(RuleKind::RoutingCorners),
369 "RoutingVias" => Some(RuleKind::RoutingVias),
370 "PlaneClearance" => Some(RuleKind::PlaneClearance),
371 "SolderMaskExpansion" => Some(RuleKind::SolderMaskExpansion),
372 "PasteMaskExpansion" => Some(RuleKind::PasteMaskExpansion),
373 "ShortCircuit" => Some(RuleKind::ShortCircuit),
374 "UnRoutedNet" => Some(RuleKind::UnRoutedNet),
375 "ViasUnderSMD" => Some(RuleKind::ViasUnderSMD),
376 "MaximumViaCount" => Some(RuleKind::MaximumViaCount),
377 "MinimumAnnularRing" => Some(RuleKind::MinimumAnnularRing),
378 "PolygonConnect" => Some(RuleKind::PolygonConnect),
379 "AcuteAngle" => Some(RuleKind::AcuteAngle),
380 "RoomDefinition" => Some(RuleKind::RoomDefinition),
381 "SMDToCorner" => Some(RuleKind::SMDToCorner),
382 "ComponentClearance" => Some(RuleKind::ComponentClearance),
383 "ComponentOrientations" => Some(RuleKind::ComponentOrientations),
384 "PermittedLayers" => Some(RuleKind::PermittedLayers),
385 "NetsToIgnore" => Some(RuleKind::NetsToIgnore),
386 "SignalStimulus" => Some(RuleKind::SignalStimulus),
387 "OvershootFalling" => Some(RuleKind::OvershootFalling),
388 "OvershootRising" => Some(RuleKind::OvershootRising),
389 "UndershootFalling" => Some(RuleKind::UndershootFalling),
390 "UndershootRising" => Some(RuleKind::UndershootRising),
391 "MaxMinImpedance" => Some(RuleKind::MaxMinImpedance),
392 "SignalTopValue" => Some(RuleKind::SignalTopValue),
393 "SignalBaseValue" => Some(RuleKind::SignalBaseValue),
394 "FlightTimeRising" => Some(RuleKind::FlightTimeRising),
395 "FlightTimeFalling" => Some(RuleKind::FlightTimeFalling),
396 "LayerStack" => Some(RuleKind::LayerStack),
397 "SlopeRising" => Some(RuleKind::SlopeRising),
398 "SlopeFalling" => Some(RuleKind::SlopeFalling),
399 "SupplyNets" => Some(RuleKind::SupplyNets),
400 "HoleSize" => Some(RuleKind::HoleSize),
401 "Testpoint" | "TestPointStyle" => Some(RuleKind::TestPointStyle),
402 "TestPointUsage" => Some(RuleKind::TestPointUsage),
403 "UnConnectedPin" | "UnconnectedPin" => Some(RuleKind::UnconnectedPin),
404 "SMDToPlane" => Some(RuleKind::SMDToPlane),
405 "SMDNeckDown" => Some(RuleKind::SMDNeckDown),
406 "LayerPairs" => Some(RuleKind::LayerPairs),
407 "FanoutControl" => Some(RuleKind::FanoutControl),
408 "Height" => Some(RuleKind::Height),
409 "DiffPairsRouting" => Some(RuleKind::DiffPairsRouting),
410 "HoleToHoleClearance" => Some(RuleKind::HoleToHoleClearance),
411 "MinimumSolderMaskSliver" => Some(RuleKind::MinimumSolderMaskSliver),
412 "SilkToSolderMaskClearance" => Some(RuleKind::SilkToSolderMaskClearance),
413 "SilkToSilkClearance" => Some(RuleKind::SilkToSilkClearance),
414 "NetAntennae" => Some(RuleKind::NetAntennae),
415 "AssemblyTestpoint" => Some(RuleKind::AssemblyTestpoint),
416 "AssemblyTestPointUsage" => Some(RuleKind::AssemblyTestPointUsage),
417 "SilkToBoardRegionClearance" => Some(RuleKind::SilkToBoardRegionClearance),
418 "UnpouredPolygon" => Some(RuleKind::UnpouredPolygon),
419 _ => None,
420 }
421 }
422}
423
424impl fmt::Display for RuleKind {
425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 match self {
427 RuleKind::Unknown(id) => write!(f, "Unknown({})", id),
428 _ => write!(f, "{}", self.name()),
429 }
430 }
431}
432
433#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
435pub enum NetScope {
436 #[default]
437 AnyNet,
438 InNetClass,
439 Net,
440 FromTo,
441 Board,
442 DifferentialPair,
443}
444
445impl NetScope {
446 pub fn parse(s: &str) -> Self {
447 match s {
448 "AnyNet" => NetScope::AnyNet,
449 "InNetClass" => NetScope::InNetClass,
450 "Net" => NetScope::Net,
451 "FromTo" => NetScope::FromTo,
452 "Board" => NetScope::Board,
453 "DifferentialPair" => NetScope::DifferentialPair,
454 _ => NetScope::AnyNet,
455 }
456 }
457
458 pub fn to_str(&self) -> &'static str {
459 match self {
460 NetScope::AnyNet => "AnyNet",
461 NetScope::InNetClass => "InNetClass",
462 NetScope::Net => "Net",
463 NetScope::FromTo => "FromTo",
464 NetScope::Board => "Board",
465 NetScope::DifferentialPair => "DifferentialPair",
466 }
467 }
468}
469
470#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
472pub enum LayerKind {
473 #[default]
474 SameLayer,
475 AnyLayer,
476 OnLayer,
477 InLayerClass,
478}
479
480impl LayerKind {
481 pub fn parse(s: &str) -> Self {
482 match s {
483 "SameLayer" => LayerKind::SameLayer,
484 "AnyLayer" => LayerKind::AnyLayer,
485 "OnLayer" => LayerKind::OnLayer,
486 "InLayerClass" => LayerKind::InLayerClass,
487 _ => LayerKind::SameLayer,
488 }
489 }
490
491 pub fn to_str(&self) -> &'static str {
492 match self {
493 LayerKind::SameLayer => "SameLayer",
494 LayerKind::AnyLayer => "AnyLayer",
495 LayerKind::OnLayer => "OnLayer",
496 LayerKind::InLayerClass => "InLayerClass",
497 }
498 }
499}
500
501#[derive(Debug, Clone)]
507pub struct PcbRule {
508 pub kind: RuleKind,
510 pub name: String,
512 pub enabled: bool,
514 pub priority: i32,
516 pub comment: String,
518 pub unique_id: String,
520 pub net_scope: NetScope,
522 pub layer_kind: LayerKind,
524 pub scope1_expression: String,
526 pub scope2_expression: String,
528 pub defined_by_logical_document: bool,
530 pub params: ParameterCollection,
533}
534
535impl Default for PcbRule {
536 fn default() -> Self {
537 PcbRule {
538 kind: RuleKind::Clearance,
539 name: String::new(),
540 enabled: true,
541 priority: 1,
542 comment: String::new(),
543 unique_id: String::new(),
544 net_scope: NetScope::AnyNet,
545 layer_kind: LayerKind::SameLayer,
546 scope1_expression: "All".to_string(),
547 scope2_expression: "All".to_string(),
548 defined_by_logical_document: false,
549 params: ParameterCollection::new(),
550 }
551 }
552}
553
554impl PcbRule {
555 pub fn new(kind: RuleKind, name: &str) -> Self {
557 PcbRule {
558 kind,
559 name: name.to_string(),
560 ..Default::default()
561 }
562 }
563
564 pub fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
571 let kind_id = reader.read_u16::<LittleEndian>()?;
573 let kind = RuleKind::from_id(kind_id);
574
575 let data_size = reader.read_u32::<LittleEndian>()? as usize;
577
578 if data_size == 0 {
579 return Ok(PcbRule {
580 kind,
581 ..Default::default()
582 });
583 }
584
585 let mut data = vec![0u8; data_size];
587 reader.read_exact(&mut data)?;
588
589 let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
591 let param_str = decode_windows_1252(&data[..end]);
592
593 let params = ParameterCollection::from_string(¶m_str);
595
596 let name = params
598 .get("NAME")
599 .map(|v| v.as_str().to_string())
600 .unwrap_or_default();
601 let enabled = params
602 .get("ENABLED")
603 .map(|v| v.as_bool_or(true))
604 .unwrap_or(true);
605 let priority = params.get("PRIORITY").map(|v| v.as_int_or(1)).unwrap_or(1);
606 let comment = params
607 .get("COMMENT")
608 .map(|v| v.as_str().to_string())
609 .unwrap_or_default();
610 let unique_id = params
611 .get("UNIQUEID")
612 .map(|v| v.as_str().to_string())
613 .unwrap_or_default();
614 let net_scope = params
615 .get("NETSCOPE")
616 .map(|v| NetScope::parse(v.as_str()))
617 .unwrap_or_default();
618 let layer_kind = params
619 .get("LAYERKIND")
620 .map(|v| LayerKind::parse(v.as_str()))
621 .unwrap_or_default();
622 let scope1_expression = params
623 .get("SCOPE1EXPRESSION")
624 .map(|v| v.as_str().to_string())
625 .unwrap_or_else(|| "All".to_string());
626 let scope2_expression = params
627 .get("SCOPE2EXPRESSION")
628 .map(|v| v.as_str().to_string())
629 .unwrap_or_else(|| "All".to_string());
630 let defined_by_logical_document = params
631 .get("DEFINEDBYLOGICALDOCUMENT")
632 .map(|v| v.as_bool_or(false))
633 .unwrap_or(false);
634
635 Ok(PcbRule {
636 kind,
637 name,
638 enabled,
639 priority,
640 comment,
641 unique_id,
642 net_scope,
643 layer_kind,
644 scope1_expression,
645 scope2_expression,
646 defined_by_logical_document,
647 params,
648 })
649 }
650
651 pub fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
653 let mut params = self.params.clone();
655 params.add("RULEKIND", self.kind.name());
656 params.add("NAME", &self.name);
657 params.add("ENABLED", if self.enabled { "TRUE" } else { "FALSE" });
658 params.add("PRIORITY", &self.priority.to_string());
659 params.add("COMMENT", &self.comment);
660 params.add("UNIQUEID", &self.unique_id);
661 params.add("NETSCOPE", self.net_scope.to_str());
662 params.add("LAYERKIND", self.layer_kind.to_str());
663 params.add("SCOPE1EXPRESSION", &self.scope1_expression);
664 params.add("SCOPE2EXPRESSION", &self.scope2_expression);
665 params.add(
666 "DEFINEDBYLOGICALDOCUMENT",
667 if self.defined_by_logical_document {
668 "TRUE"
669 } else {
670 "FALSE"
671 },
672 );
673
674 let param_str = params.to_string();
676 let mut data = encode_windows_1252(¶m_str);
677 data.push(0); writer.write_u16::<LittleEndian>(self.kind.to_id())?;
681
682 writer.write_u32::<LittleEndian>(data.len() as u32)?;
684
685 writer.write_all(&data)?;
687
688 Ok(())
689 }
690
691 pub fn binary_size(&self) -> usize {
693 let param_str = self.params.to_string();
695 6 + param_str.len() + 1
696 }
697
698 pub fn clearance(&self) -> Option<Coord> {
702 self.params.get("GAP").and_then(|v| v.as_coord().ok())
703 }
704
705 pub fn set_clearance(&mut self, value: Coord) {
707 self.params.add_coord("GAP", value);
708 }
709
710 pub fn min_width(&self) -> Option<Coord> {
712 self.params.get("MINWIDTH").and_then(|v| v.as_coord().ok())
713 }
714
715 pub fn set_min_width(&mut self, value: Coord) {
717 self.params.add_coord("MINWIDTH", value);
718 }
719
720 pub fn max_width(&self) -> Option<Coord> {
722 self.params.get("MAXWIDTH").and_then(|v| v.as_coord().ok())
723 }
724
725 pub fn set_max_width(&mut self, value: Coord) {
727 self.params.add_coord("MAXWIDTH", value);
728 }
729
730 pub fn preferred_width(&self) -> Option<Coord> {
732 self.params.get("PREFWIDTH").and_then(|v| v.as_coord().ok())
733 }
734
735 pub fn set_preferred_width(&mut self, value: Coord) {
737 self.params.add_coord("PREFWIDTH", value);
738 }
739
740 pub fn min_hole_size(&self) -> Option<Coord> {
742 self.params
743 .get("MINHOLESIZE")
744 .and_then(|v| v.as_coord().ok())
745 }
746
747 pub fn set_min_hole_size(&mut self, value: Coord) {
749 self.params.add_coord("MINHOLESIZE", value);
750 }
751
752 pub fn max_hole_size(&self) -> Option<Coord> {
754 self.params
755 .get("MAXHOLESIZE")
756 .and_then(|v| v.as_coord().ok())
757 }
758
759 pub fn set_max_hole_size(&mut self, value: Coord) {
761 self.params.add_coord("MAXHOLESIZE", value);
762 }
763
764 pub fn solder_mask_expansion(&self) -> Option<Coord> {
766 self.params.get("EXPANSION").and_then(|v| v.as_coord().ok())
767 }
768
769 pub fn set_solder_mask_expansion(&mut self, value: Coord) {
771 self.params.add_coord("EXPANSION", value);
772 }
773
774 pub fn paste_mask_expansion(&self) -> Option<Coord> {
776 self.params.get("EXPANSION").and_then(|v| v.as_coord().ok())
777 }
778
779 pub fn set_paste_mask_expansion(&mut self, value: Coord) {
781 self.params.add_coord("EXPANSION", value);
782 }
783
784 pub fn diff_pair_min_gap(&self) -> Option<Coord> {
788 self.params.get("MINLIMIT").and_then(|v| v.as_coord().ok())
789 }
790
791 pub fn set_diff_pair_min_gap(&mut self, value: Coord) {
793 self.params.add_coord("MINLIMIT", value);
794 }
795
796 pub fn diff_pair_max_gap(&self) -> Option<Coord> {
798 self.params.get("MAXLIMIT").and_then(|v| v.as_coord().ok())
799 }
800
801 pub fn set_diff_pair_max_gap(&mut self, value: Coord) {
803 self.params.add_coord("MAXLIMIT", value);
804 }
805
806 pub fn diff_pair_preferred_gap(&self) -> Option<Coord> {
808 self.params
809 .get("MOSTFREQGAP")
810 .and_then(|v| v.as_coord().ok())
811 }
812
813 pub fn set_diff_pair_preferred_gap(&mut self, value: Coord) {
815 self.params.add_coord("MOSTFREQGAP", value);
816 }
817
818 pub fn diff_pair_max_uncoupled_length(&self) -> Option<Coord> {
820 self.params
821 .get("MAXUNCOUPLEDLENGTH")
822 .and_then(|v| v.as_coord().ok())
823 }
824
825 pub fn set_diff_pair_max_uncoupled_length(&mut self, value: Coord) {
827 self.params.add_coord("MAXUNCOUPLEDLENGTH", value);
828 }
829
830 pub fn via_min_diameter(&self) -> Option<Coord> {
834 self.params.get("MINWIDTH").and_then(|v| v.as_coord().ok())
835 }
836
837 pub fn set_via_min_diameter(&mut self, value: Coord) {
839 self.params.add_coord("MINWIDTH", value);
840 }
841
842 pub fn via_max_diameter(&self) -> Option<Coord> {
844 self.params.get("MAXWIDTH").and_then(|v| v.as_coord().ok())
845 }
846
847 pub fn set_via_max_diameter(&mut self, value: Coord) {
849 self.params.add_coord("MAXWIDTH", value);
850 }
851
852 pub fn via_preferred_diameter(&self) -> Option<Coord> {
854 self.params.get("PREFWIDTH").and_then(|v| v.as_coord().ok())
855 }
856
857 pub fn set_via_preferred_diameter(&mut self, value: Coord) {
859 self.params.add_coord("PREFWIDTH", value);
860 }
861
862 pub fn annular_ring(&self) -> Option<Coord> {
866 self.params
867 .get("ANNULARRING")
868 .and_then(|v| v.as_coord().ok())
869 }
870
871 pub fn set_annular_ring(&mut self, value: Coord) {
873 self.params.add_coord("ANNULARRING", value);
874 }
875
876 pub fn hole_to_hole_clearance(&self) -> Option<Coord> {
880 self.params.get("GAP").and_then(|v| v.as_coord().ok())
881 }
882
883 pub fn set_hole_to_hole_clearance(&mut self, value: Coord) {
885 self.params.add_coord("GAP", value);
886 }
887
888 pub fn min_solder_mask_sliver(&self) -> Option<Coord> {
892 self.params.get("MINLIMIT").and_then(|v| v.as_coord().ok())
893 }
894
895 pub fn set_min_solder_mask_sliver(&mut self, value: Coord) {
897 self.params.add_coord("MINLIMIT", value);
898 }
899
900 pub fn plane_connect_style(&self) -> Option<String> {
904 self.params
905 .get("CONNECTSTYLE")
906 .map(|v| v.as_str().to_string())
907 }
908
909 pub fn set_plane_connect_style(&mut self, style: &str) {
911 self.params.add("CONNECTSTYLE", style);
912 }
913
914 pub fn thermal_relief_air_gap(&self) -> Option<Coord> {
916 self.params
917 .get("AIRGAPWIDTH")
918 .and_then(|v| v.as_coord().ok())
919 }
920
921 pub fn set_thermal_relief_air_gap(&mut self, value: Coord) {
923 self.params.add_coord("AIRGAPWIDTH", value);
924 }
925
926 pub fn thermal_relief_conductor_width(&self) -> Option<Coord> {
928 self.params
929 .get("CONDUCTORWIDTH")
930 .and_then(|v| v.as_coord().ok())
931 }
932
933 pub fn set_thermal_relief_conductor_width(&mut self, value: Coord) {
935 self.params.add_coord("CONDUCTORWIDTH", value);
936 }
937
938 pub fn thermal_relief_conductors(&self) -> Option<i32> {
940 self.params.get("CONDUCTORS").map(|v| v.as_int_or(4))
941 }
942
943 pub fn set_thermal_relief_conductors(&mut self, count: i32) {
945 self.params.add("CONDUCTORS", &count.to_string());
946 }
947
948 pub fn routing_corner_style(&self) -> Option<String> {
952 self.params.get("STYLE").map(|v| v.as_str().to_string())
953 }
954
955 pub fn set_routing_corner_style(&mut self, style: &str) {
957 self.params.add("STYLE", style);
958 }
959
960 pub fn routing_topology(&self) -> Option<String> {
964 self.params.get("TOPOLOGY").map(|v| v.as_str().to_string())
965 }
966
967 pub fn set_routing_topology(&mut self, topology: &str) {
969 self.params.add("TOPOLOGY", topology);
970 }
971
972 pub fn component_clearance(&self) -> Option<Coord> {
976 self.params.get("GAP").and_then(|v| v.as_coord().ok())
977 }
978
979 pub fn set_component_clearance(&mut self, value: Coord) {
981 self.params.add_coord("GAP", value);
982 }
983
984 pub fn max_height(&self) -> Option<Coord> {
986 self.params.get("MAXLIMIT").and_then(|v| v.as_coord().ok())
987 }
988
989 pub fn set_max_height(&mut self, value: Coord) {
991 self.params.add_coord("MAXLIMIT", value);
992 }
993
994 pub fn min_length(&self) -> Option<Coord> {
998 self.params.get("MINLIMIT").and_then(|v| v.as_coord().ok())
999 }
1000
1001 pub fn set_min_length(&mut self, value: Coord) {
1003 self.params.add_coord("MINLIMIT", value);
1004 }
1005
1006 pub fn max_length(&self) -> Option<Coord> {
1008 self.params.get("MAXLIMIT").and_then(|v| v.as_coord().ok())
1009 }
1010
1011 pub fn set_max_length(&mut self, value: Coord) {
1013 self.params.add_coord("MAXLIMIT", value);
1014 }
1015
1016 pub fn matched_length_tolerance(&self) -> Option<Coord> {
1018 self.params.get("TOLERANCE").and_then(|v| v.as_coord().ok())
1019 }
1020
1021 pub fn set_matched_length_tolerance(&mut self, value: Coord) {
1023 self.params.add_coord("TOLERANCE", value);
1024 }
1025
1026 pub fn max_stub_length(&self) -> Option<Coord> {
1028 self.params.get("MAXLIMIT").and_then(|v| v.as_coord().ok())
1029 }
1030
1031 pub fn set_max_stub_length(&mut self, value: Coord) {
1033 self.params.add_coord("MAXLIMIT", value);
1034 }
1035
1036 pub fn min_impedance(&self) -> Option<f64> {
1040 self.params.get("MINLIMIT").map(|v| v.as_double_or(0.0))
1041 }
1042
1043 pub fn set_min_impedance(&mut self, value: f64) {
1045 self.params.add("MINLIMIT", &value.to_string());
1046 }
1047
1048 pub fn max_impedance(&self) -> Option<f64> {
1050 self.params.get("MAXLIMIT").map(|v| v.as_double_or(0.0))
1051 }
1052
1053 pub fn set_max_impedance(&mut self, value: f64) {
1055 self.params.add("MAXLIMIT", &value.to_string());
1056 }
1057
1058 pub fn vias_under_smd_allowed(&self) -> bool {
1062 self.params
1063 .get("ALLOWVIAS")
1064 .map(|v| v.as_bool_or(false))
1065 .unwrap_or(false)
1066 }
1067
1068 pub fn set_vias_under_smd_allowed(&mut self, allowed: bool) {
1070 self.params
1071 .add("ALLOWVIAS", if allowed { "TRUE" } else { "FALSE" });
1072 }
1073
1074 pub fn max_via_count(&self) -> Option<i32> {
1078 self.params.get("MAXVIACOUNT").map(|v| v.as_int_or(0))
1079 }
1080
1081 pub fn set_max_via_count(&mut self, count: i32) {
1083 self.params.add("MAXVIACOUNT", &count.to_string());
1084 }
1085
1086 pub fn fanout_direction(&self) -> Option<String> {
1090 self.params.get("DIRECTION").map(|v| v.as_str().to_string())
1091 }
1092
1093 pub fn set_fanout_direction(&mut self, direction: &str) {
1095 self.params.add("DIRECTION", direction);
1096 }
1097
1098 pub fn fanout_style(&self) -> Option<String> {
1100 self.params.get("STYLE").map(|v| v.as_str().to_string())
1101 }
1102
1103 pub fn set_fanout_style(&mut self, style: &str) {
1105 self.params.add("STYLE", style);
1106 }
1107
1108 pub fn generic_min_limit(&self) -> Option<Coord> {
1112 self.params.get("MINLIMIT").and_then(|v| v.as_coord().ok())
1113 }
1114
1115 pub fn generic_max_limit(&self) -> Option<Coord> {
1117 self.params.get("MAXLIMIT").and_then(|v| v.as_coord().ok())
1118 }
1119
1120 pub fn generic_gap(&self) -> Option<Coord> {
1122 self.params.get("GAP").and_then(|v| v.as_coord().ok())
1123 }
1124}
1125
1126pub fn read_rules<R: Read>(reader: &mut R, data_len: usize) -> Result<Vec<PcbRule>> {
1128 let mut rules = Vec::new();
1129 let mut bytes_read = 0;
1130
1131 while bytes_read < data_len {
1132 let start_pos = bytes_read;
1133
1134 if bytes_read + 6 > data_len {
1136 break;
1137 }
1138
1139 match PcbRule::read_from(reader) {
1140 Ok(rule) => {
1141 let rule_size = rule.binary_size();
1143 bytes_read += rule_size;
1144 rules.push(rule);
1145 }
1146 Err(e) => {
1147 if start_pos == bytes_read {
1149 return Err(e);
1150 }
1151 break;
1152 }
1153 }
1154 }
1155
1156 Ok(rules)
1157}
1158
1159pub fn write_rules<W: Write>(writer: &mut W, rules: &[PcbRule]) -> Result<()> {
1161 for rule in rules {
1162 rule.write_to(writer)?;
1163 }
1164 Ok(())
1165}
1166
1167#[cfg(test)]
1168mod tests {
1169 use super::*;
1170 use std::io::Cursor;
1171
1172 #[test]
1173 fn test_rule_kind_roundtrip() {
1174 let kinds = [
1175 RuleKind::Clearance,
1176 RuleKind::Width,
1177 RuleKind::RoutingLayers,
1178 RuleKind::DiffPairsRouting,
1179 ];
1180
1181 for kind in &kinds {
1182 let id = kind.to_id();
1183 let parsed = RuleKind::from_id(id);
1184 assert_eq!(*kind, parsed);
1185 }
1186 }
1187
1188 #[test]
1189 fn test_rule_kind_name() {
1190 assert_eq!(RuleKind::Clearance.name(), "Clearance");
1191 assert_eq!(RuleKind::Width.name(), "Width");
1192 assert_eq!(RuleKind::DiffPairsRouting.name(), "DiffPairsRouting");
1193 }
1194
1195 #[test]
1196 fn test_rule_serialization() {
1197 let mut rule = PcbRule::new(RuleKind::Clearance, "TestClearance");
1198 rule.enabled = true;
1199 rule.priority = 1;
1200 rule.scope1_expression = "All".to_string();
1201 rule.scope2_expression = "All".to_string();
1202 rule.params.add("GAP", "10mil");
1203
1204 let mut buffer = Vec::new();
1206 rule.write_to(&mut buffer).unwrap();
1207
1208 let mut cursor = Cursor::new(&buffer);
1210 let parsed = PcbRule::read_from(&mut cursor).unwrap();
1211
1212 assert_eq!(parsed.kind, RuleKind::Clearance);
1213 assert_eq!(parsed.name, "TestClearance");
1214 assert!(parsed.enabled);
1215 assert_eq!(parsed.priority, 1);
1216 }
1217}