nextjlc/dcode.rs
1/* src/dcode.rs */
2
3/* SPDX-License-Identifier: Apache-2.0 */
4/*
5 * Author HalfSweet <halfsweet@halfsweet.cn>
6 * Author Canmi <t@canmi.icu>
7 */
8
9use once_cell::sync::Lazy;
10use regex::Regex;
11
12// This regex matches any standard D-code (Dxx*).
13static DCODE_REGEX: Lazy<Regex> =
14 Lazy::new(|| Regex::new(r"(D\d{2,4}\*)").expect("Failed to compile D-Code regex"));
15
16/// Gerber flavor for D-code processing
17pub enum GerberFlavor {
18 KiCad,
19 Altium,
20}
21
22/// Process Gerber data to prepend "G54" to D-codes according to the CAD flavor.
23///
24/// KiCad: process all non-%ADD, non-G54D D-codes.
25/// Altium: skip D-codes on lines starting with G01/G02/G36/G37 with coordinates
26/// or lines that only contain a single Dxx*; other D-codes (including %ADD/%AM/G04) are processed.
27///
28/// # Arguments
29/// * `gerber_data` - Raw Gerber file content
30/// * `flavor` - KiCad or Altium
31///
32/// # Returns
33/// Processed Gerber content with appropriate D-codes prefixed with "G54"
34pub fn process_d_codes(gerber_data: String, flavor: GerberFlavor) -> String {
35 let input_lines: Vec<&str> = gerber_data.split('\n').collect();
36 let mut processed_lines = Vec::with_capacity(input_lines.len());
37
38 for line in input_lines {
39 let mut should_skip = false;
40
41 match flavor {
42 GerberFlavor::KiCad => {
43 if line.contains("%ADD") || line.contains("G54D") {
44 should_skip = true;
45 }
46 }
47 GerberFlavor::Altium => {
48 // Skip if line already contains G54D
49 if line.contains("G54D") {
50 should_skip = true;
51 }
52
53 // Skip lines starting with G01/G02/G36/G37 with coordinates or single Dxx*
54 if line.starts_with("G01")
55 || line.starts_with("G02")
56 || line.starts_with("G36")
57 || line.starts_with("G37")
58 {
59 let trimmed = line.trim();
60 // Skip lines that are only Dxx* or contain coordinates
61 if trimmed == "D01*"
62 || trimmed == "D02*"
63 || trimmed == "D03*"
64 || trimmed.contains("X")
65 || trimmed.contains("Y")
66 {
67 should_skip = true;
68 }
69 }
70 }
71 }
72
73 if should_skip {
74 processed_lines.push(line.to_string());
75 } else {
76 let modified_line = DCODE_REGEX.replace_all(line, "G54$1");
77 processed_lines.push(modified_line.to_string());
78 }
79 }
80
81 processed_lines.join("\n")
82}