partslist-vendor: Generate Mouser format parts list file on request
[hw/altusmetrum] / bin / partslist-vendor
1 #!/usr/bin/env nickle
2 #
3 # Convert CSV parts list into vendor import format
4 #
5 # Copyright © 2015 Keith Packard <keithp@keithp.com>, GPL v3+
6 #
7
8 bool    mfg_part = false;
9 string[*]       vendors;
10 string          vendor_list;
11 string[*]       not_vendors;
12 string          not_vendor_list;
13 string  input_name;
14 string  output_name;
15 string  program;
16 int     argi;
17 int     lineno = 0;
18
19 void fatal(string format, poly args ...)
20 {
21         File::fprintf(stderr, format, args...);
22         exit(1);
23 }
24
25 string[*] read_line(file f) {
26         lineno++;
27         string  line = fgets(f);
28
29         return String::parse_csv(line);
30 }
31
32 string[*] header;
33
34 string[*] required_elements = {
35         "quantity",
36         "vendor",
37         "vendor_part_number",
38         "mfg_part_number",
39         "device",
40         "value",
41         "refdes",
42         "loadstatus"
43 };
44
45 bool has_header_member(string member) {
46         for (int i = 0; i < dim(header); i++)
47                 if (header[i] == member)
48                         return true;
49         return false;
50 }
51
52 bool has_vendor(string[*] vendors, string vendor) {
53         for (int i = 0; i < dim(vendors); i++)
54                 if (vendors[i] == vendor)
55                         return true;
56         return false;
57 }
58
59 void read_header(file f) {
60         header = read_line(f);
61
62         for (int i = 0; i < dim(required_elements); i++)
63                 if (!has_header_member(required_elements[i]))
64                         fatal("Missing header element \"%s\"\n", required_elements[i]);
65 }
66
67 string[string] read_entry(file f) {
68         string[*]       elements = read_line(f);
69         string[string]  entry = {};
70
71         if (dim(header) != dim(elements))
72                 fatal("line %d: has %d instead of %d elements (%V)\n",
73                       lineno, dim(elements), dim(header), elements);
74
75         for (int i = 0; i < dim(header); i++) {
76                 if (elements[i] == "")
77                         elements[i] = "unknown";
78                 entry[header[i]] = elements[i];
79         }
80         return entry;
81 }
82
83 string part_number(string[string] entry)
84 {
85         if (mfg_part)
86                 return entry["mfg_part_number"];
87         else
88                 return entry["vendor_part_number"];
89 }
90
91 void process_seeed(string[string] entry)
92 {
93         if (entry["loadstatus"] != "smt")
94                 return;
95
96         static bool start = true;
97         if (start) {
98                 printf("Part/Designator,Manufacturer Part Number/Seeed SKU, Quantity\n");
99                 start = false;
100         }
101
102         string[*] refdes = String::wordsplit(entry["refdes"], " \t");
103         if (dim(refdes) > 1)
104                 printf ("\"");
105         for (int i = 0; i < dim(refdes); i++) {
106                 printf("%s", refdes[i]);
107                 if (i < dim(refdes) - 1)
108                         printf (",");
109         }
110         if (dim(refdes) > 1)
111                 printf ("\"");
112         printf(",%s,%s\n", entry["mfg_part_number"], entry["quantity"]);
113 }
114
115 void process_digikey(string[string] entry)
116 {
117         if (entry["loadstatus"] == "noload")
118                 return;
119         printf("%s,%s,%s %s\n",
120                entry["quantity"],
121                part_number(entry),
122                entry["device"],
123                entry["value"]);
124 }
125
126 void process_mouser(string[string] entry)
127 {
128         if (entry["loadstatus"] == "noload")
129                 return;
130         printf("%s|%s\n", part_number(entry), entry["quantity"]);
131 }
132
133 void process_other(string[string] entry) {
134         if (entry["loadstatus"] == "noload")
135                 return;
136         printf("%s,%s,%s,%s %s\n",
137                entry["vendor"],
138                entry["quantity"],
139                part_number(entry),
140                entry["device"],
141                entry["value"]);
142 }
143
144 void process_file(file f) {
145         read_header(f);
146         while (!File::end(f)) {
147                 string[string] entry = read_entry(f);
148                 string vendor = entry["vendor"];
149                 if (!is_uninit(&vendors) && has_vendor(vendors, "seeed")) {
150                         process_seeed(entry);
151                 } else if ((!is_uninit(&vendors) && has_vendor(vendors, vendor)) ||
152                            (!is_uninit(&not_vendors) && !has_vendor(not_vendors, vendor))) {
153                         switch (entry["vendor"]) {
154                         case "digikey":
155                                 process_digikey(entry);
156                                 break;
157                         case "mouser":
158                                 process_mouser(entry);
159                                 break;
160                         default:
161                                 process_other(entry);
162                                 break;
163                         }
164                 }
165         }
166 }
167
168 ParseArgs::argdesc argd = {
169         .args = {
170                 { .var = { .arg_flag = &mfg_part },
171                   .abbr = 'm',
172                   .name = "mfg",
173                   .desc = "Display manufacturer part number"},
174                 { .var = { .arg_string = &vendor_list },
175                   .abbr = 'v',
176                   .name = "vendor",
177                   .expr_name = "vendors",
178                   .desc = "Vendors to match"},
179                 { .var = { .arg_string = &not_vendor_list },
180                   .abbr = 'n',
181                   .name = "not-vendor",
182                   .expr_name = "not-vendor",
183                   .desc = "Vendors to exclude"},
184         },
185         .unknown = &argi,
186 };
187
188 void main() {
189         ParseArgs::parseargs(&argd, &argv);
190         if (!is_uninit(&vendor_list))
191                 vendors = String::parse_csv(vendor_list);
192
193         if (!is_uninit(&not_vendor_list))
194                 not_vendors = String::parse_csv(not_vendor_list);
195
196         if (!is_uninit(&argi)) {
197                 for (int i = argi; i < dim(argv); i++)
198                         twixt(file f = File::open(argv[i], "r"); File::close(f))
199                                 process_file(f);
200         } else
201                 process_file(stdin);
202 }
203
204 main();