Add ability to generate dk, mouser and other partslists
[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 };
42
43 bool has_header_member(string member) {
44         for (int i = 0; i < dim(header); i++)
45                 if (header[i] == member)
46                         return true;
47         return false;
48 }
49
50 bool has_vendor(string[*] vendors, string vendor) {
51         for (int i = 0; i < dim(vendors); i++)
52                 if (vendors[i] == vendor)
53                         return true;
54         return false;
55 }
56
57 void read_header(file f) {
58         header = read_line(f);
59
60         for (int i = 0; i < dim(required_elements); i++)
61                 if (!has_header_member(required_elements[i]))
62                         fatal("Missing header element \"%s\"\n", required_elements[i]);
63 }
64
65 string[string] read_entry(file f) {
66         string[*]       elements = read_line(f);
67         string[string]  entry = {};
68
69         if (dim(header) != dim(elements))
70                 fatal("line %d: has %d instead of %d elements (%V)\n",
71                       lineno, dim(elements), dim(header), elements);
72
73         for (int i = 0; i < dim(header); i++) {
74                 if (elements[i] == "")
75                         elements[i] = "unknown";
76                 entry[header[i]] = elements[i];
77         }
78         return entry;
79 }
80
81 string part_number(string[string] entry)
82 {
83         if (mfg_part)
84                 return entry["mfg_part_number"];
85         else
86                 return entry["vendor_part_number"];
87 }
88
89 void process_digikey(string[string] entry)
90 {
91         printf("%s,%s,%s %s\n",
92                entry["quantity"],
93                part_number(entry),
94                entry["device"],
95                entry["value"]);
96 }
97
98 void process_other(string[string] entry) {
99         printf("%s,%s,%s,%s %s\n",
100                entry["vendor"],
101                entry["quantity"],
102                part_number(entry),
103                entry["device"],
104                entry["value"]);
105 }
106
107 void process_file(file f) {
108         read_header(f);
109         while (!File::end(f)) {
110                 string[string] entry = read_entry(f);
111                 string vendor = entry["vendor"];
112                 if ((!is_uninit(&vendors) && has_vendor(vendors, vendor)) ||
113                     (!is_uninit(&not_vendors) && !has_vendor(not_vendors, vendor))) {
114                         switch (entry["vendor"]) {
115                         case "digikey":
116                                 process_digikey(entry);
117                                 break;
118                         default:
119                                 process_other(entry);
120                                 break;
121                         }
122                 }
123         }
124 }
125
126 ParseArgs::argdesc argd = {
127         .args = {
128                 { .var = { .arg_flag = &mfg_part },
129                   .abbr = 'm',
130                   .name = "mfg",
131                   .desc = "Display manufacturer part number"},
132                 { .var = { .arg_string = &vendor_list },
133                   .abbr = 'v',
134                   .name = "vendor",
135                   .expr_name = "vendors",
136                   .desc = "Vendors to match"},
137                 { .var = { .arg_string = &not_vendor_list },
138                   .abbr = 'n',
139                   .name = "not-vendor",
140                   .expr_name = "not-vendor",
141                   .desc = "Vendors to exclude"},
142         },
143         .unknown = &argi,
144 };
145
146 void main() {
147         ParseArgs::parseargs(&argd, &argv);
148         if (!is_uninit(&vendor_list))
149                 vendors = String::parse_csv(vendor_list);
150
151         if (!is_uninit(&not_vendor_list))
152                 not_vendors = String::parse_csv(not_vendor_list);
153
154         if (!is_uninit(&argi)) {
155                 for (int i = argi; i < dim(argv); i++)
156                         twixt(file f = File::open(argv[i], "r"); File::close(f))
157                                 process_file(f);
158         } else
159                 process_file(stdin);
160 }
161
162 main();