1#[macro_use]
2extern crate diesel;
3#[macro_use]
4extern crate diesel_migrations;
5
6#[macro_use]
7extern crate prettytable;
8
9pub mod config;
10pub mod models;
11pub mod prompt;
12pub mod schema;
13pub mod schematic;
14pub mod tables;
15
16use diesel::prelude::*;
17
18use models::*;
19
20use std::io::{StdinLock, Stdout};
21
22pub struct Application<'a> {
23 pub config: config::Config,
25
26 pub config_path: std::path::PathBuf,
28
29 pub prompt: prompt::Prompt<StdinLock<'a>, Stdout>,
31
32 pub conn: SqliteConnection,
34}
35
36embed_migrations!();
38
39pub fn establish_connection(db_name: &str) -> SqliteConnection {
40 let mut database_url =
42 config::get_default_config_path().unwrap_or_else(|_| panic!("Unable to get config path."));
43
44 database_url.push(db_name);
46
47 let conn = SqliteConnection::establish(&database_url.to_string_lossy())
49 .unwrap_or_else(|_| panic!("Error connecting to {}", &database_url.to_string_lossy()));
50
51 embedded_migrations::run(&conn).expect("Unable to run migration.");
53
54 conn
55}
56
57pub fn create_part(
59 conn: &SqliteConnection,
60 part: &NewUpdatePart,
61) -> std::result::Result<usize, diesel::result::Error> {
62 use schema::parts;
63
64 diesel::insert_into(parts::table).values(part).execute(conn)
65}
66
67pub fn update_part(
68 conn: &SqliteConnection,
69 id: &i32,
70 part: &NewUpdatePart,
71) -> std::result::Result<usize, diesel::result::Error> {
72 use schema::parts;
73
74 diesel::update(parts::dsl::parts.filter(parts::dsl::id.eq(id)))
75 .set(part)
76 .execute(conn)
77}
78
79pub fn rename_part(
80 conn: &SqliteConnection,
81 oldpn: &str,
82 newpn: &str,
83) -> std::result::Result<usize, diesel::result::Error> {
84 use schema::parts::dsl::*;
85
86 let part = find_part_by_pn(&conn, &oldpn).expect("Old part not found");
87
88 diesel::update(parts.find(part.id))
89 .set(pn.eq(newpn))
90 .execute(conn)
91}
92
93pub fn delete_part(
94 conn: &SqliteConnection,
95 id: &i32,
96) -> std::result::Result<usize, diesel::result::Error> {
97 use schema::parts;
98
99 diesel::delete(parts::dsl::parts.filter(parts::dsl::id.eq(id))).execute(conn)
100}
101
102pub fn find_part_by_pn(
103 conn: &SqliteConnection,
104 pn: &str,
105) -> std::result::Result<Part, diesel::result::Error> {
106 use schema::parts;
107
108 parts::dsl::parts.filter(parts::dsl::pn.eq(pn)).first(conn)
109}
110
111pub fn find_part_by_mpn(
112 conn: &SqliteConnection,
113 mpn: &str,
114) -> std::result::Result<Part, diesel::result::Error> {
115 use schema::parts;
116
117 println!("mpn: \"{}\"", mpn);
118
119 parts::dsl::parts
120 .filter(parts::dsl::mpn.eq(mpn))
121 .first(conn)
122}
123
124pub fn find_part_by_pn_and_ver(
125 conn: &SqliteConnection,
126 pn: &str,
127 ver: &i32,
128) -> std::result::Result<Part, diesel::result::Error> {
129 use schema::parts;
130
131 parts::dsl::parts
132 .filter(parts::dsl::pn.eq(pn))
133 .filter(parts::dsl::ver.eq(ver))
134 .first(conn)
135}
136
137pub fn find_part_by_id(
138 conn: &SqliteConnection,
139 id: &i32,
140) -> std::result::Result<Part, diesel::result::Error> {
141 use schema::parts;
142
143 parts::dsl::parts.filter(parts::dsl::id.eq(id)).first(conn)
144}
145
146pub fn create_bom_line_item(
147 conn: &SqliteConnection,
148 part: &NewPartsParts,
149) -> std::result::Result<usize, diesel::result::Error> {
150 use schema::parts_parts;
151
152 diesel::insert_into(parts_parts::table)
153 .values(part)
154 .execute(conn)
155}
156
157pub fn delete_bom_list_by_id_and_ver(
158 conn: &SqliteConnection,
159 bom_id: &i32,
160 ver: &i32,
161) -> std::result::Result<usize, diesel::result::Error> {
162 use schema::parts_parts::dsl::*;
163
164 let query = parts_parts
166 .select(id)
167 .filter(bom_part_id.eq(bom_id))
168 .load::<i32>(conn)?;
169
170 let target = parts_parts.filter(bom_ver.eq(ver)).filter(id.eq_any(query));
172
173 diesel::delete(target).execute(conn)
175}
176
177pub fn create_build(
180 conn: &SqliteConnection,
181 build: &NewUpdateBuild,
182) -> std::result::Result<usize, diesel::result::Error> {
183 use schema::builds;
184
185 diesel::insert_into(builds::table)
186 .values(build)
187 .execute(conn)
188}
189
190pub fn update_build_by_id(
191 conn: &SqliteConnection,
192 id: &i32,
193 entry: &NewUpdateBuild,
194) -> std::result::Result<usize, diesel::result::Error> {
195 use schema::builds;
196
197 diesel::update(builds::dsl::builds.filter(builds::dsl::id.eq(id)))
198 .set(entry)
199 .execute(conn)
200}
201
202pub fn find_builds_by_pn(
203 conn: &SqliteConnection,
204 pn: &str,
205) -> std::result::Result<Vec<Build>, diesel::result::Error> {
206 use schema::builds;
207
208 let part = find_part_by_pn(&conn, &pn).expect("Unable to run parts query.");
209
210 builds::dsl::builds
211 .filter(builds::dsl::part_id.eq(part.id))
212 .load::<Build>(conn)
213}
214
215pub fn find_build_by_id(
216 conn: &SqliteConnection,
217 id: &i32,
218) -> std::result::Result<Build, diesel::result::Error> {
219 use schema::builds;
220
221 builds::dsl::builds
222 .filter(builds::dsl::id.eq(id))
223 .first(conn)
224}
225
226pub fn delete_build(
227 conn: &SqliteConnection,
228 id: &i32,
229) -> std::result::Result<usize, diesel::result::Error> {
230 use schema::builds;
231
232 diesel::delete(builds::dsl::builds.filter(builds::dsl::id.eq(id))).execute(conn)
233}
234
235pub fn create_inventory(
238 conn: &SqliteConnection,
239 entry: &NewUpdateInventoryEntry,
240) -> std::result::Result<usize, diesel::result::Error> {
241 use schema::inventories;
242
243 diesel::insert_into(inventories::table)
244 .values(entry)
245 .execute(conn)
246}
247
248pub fn update_inventory_by_id(
249 conn: &SqliteConnection,
250 id: &i32,
251 entry: &NewUpdateInventoryEntry,
252) -> std::result::Result<usize, diesel::result::Error> {
253 use schema::inventories;
254
255 diesel::update(inventories::dsl::inventories.filter(inventories::dsl::id.eq(id)))
256 .set(entry)
257 .execute(conn)
258}
259
260pub fn find_inventories_by_part_id(
261 conn: &SqliteConnection,
262 id: &i32,
263) -> std::result::Result<Vec<Inventory>, diesel::result::Error> {
264 use schema::inventories;
265
266 inventories::dsl::inventories
267 .filter(inventories::dsl::part_id.eq(id))
268 .load::<Inventory>(conn)
269}
270
271pub fn test_connection() -> SqliteConnection {
272 let conn = SqliteConnection::establish(":memory:").expect("Unable to establish db in memory!");
274
275 embedded_migrations::run(&conn).expect("Unable to run test migration.");
277
278 conn
280}
281
282mod part_tests {
284
285 #[test]
286 fn create_part_check_if_created() {
287 use super::*;
288 use models::Part;
289 use schema::parts::dsl::*;
290
291 let conn = test_connection();
292
293 let part = NewUpdatePart {
295 pn: "CAP-0.1U-10V-0402",
296 mpn: "ABCD",
297 descr: "CAP 0.1U 10V 0402",
298 ver: &1,
299 mqty: &1,
300 };
301
302 create_part(&conn, &part).expect("Error creating part!");
304
305 let found: Part = parts.find(1).first(&conn).unwrap();
307
308 assert_eq!(part.pn, found.pn);
310 assert_eq!(part.mpn, found.mpn);
311 assert_eq!(part.descr, found.descr);
312 assert_eq!(*part.ver, found.ver);
313 }
314
315 #[test]
316 #[should_panic]
317 fn create_duplicate_pn_should_panic() {
320 use super::*;
321 let conn = test_connection();
322
323 let part = NewUpdatePart {
325 pn: "CAP-0.1U-10V-0402",
326 mpn: "ABCD",
327 descr: "CAP 0.1U 10V 0402",
328 ver: &1,
329 mqty: &1,
330 };
331
332 create_part(&conn, &part).expect("Error creating part!");
334
335 let part = NewUpdatePart {
337 pn: "CAP-0.1U-10V-0402",
338 mpn: "ABCD-ND",
339 descr: "CAP 0.1U 10V 0402",
340 ver: &1,
341 mqty: &1,
342 };
343
344 create_part(&conn, &part).expect("Error creating part!");
346 }
347
348 #[test]
349 #[should_panic]
350 fn create_duplicate_mpn_should_panic() {
353 use super::*;
354 let conn = test_connection();
355
356 let part = NewUpdatePart {
358 pn: "CAP-0.1U-10V-0402",
359 mpn: "ABCD",
360 descr: "CAP 0.1U 10V 0402",
361 ver: &1,
362 mqty: &1,
363 };
364
365 create_part(&conn, &part).expect("Error creating part!");
367
368 let part = NewUpdatePart {
370 pn: "CAP-0.1U-10V-0402-01",
371 mpn: "ABCD",
372 descr: "CAP 0.1U 10V 0402",
373 ver: &1,
374 mqty: &1,
375 };
376
377 create_part(&conn, &part).expect("Error creating part!");
379 }
380
381 #[test]
382 fn create_and_update_part() {
383 use super::*;
384 use models::Part;
385 use schema::parts::dsl::*;
386
387 let conn = test_connection();
388
389 let part = NewUpdatePart {
391 pn: "CAP-0.1U-10V-0402",
392 mpn: "ABCD",
393 descr: "CAP 0.1U 10V 0402",
394 ver: &1,
395 mqty: &1,
396 };
397
398 create_part(&conn, &part).expect("Error creating part!");
400
401 let found = find_part_by_pn(&conn, &part.pn).expect("Error getting part back.");
403
404 let part = NewUpdatePart {
406 pn: "CAP-0.1U-10V-0402",
407 mpn: "ABCD",
408 descr: "CAP 0.1 10V 0402 GOOD", ver: &1,
410 mqty: &1,
411 };
412
413 update_part(&conn, &found.id, &part).expect("Error creating part!");
415
416 let found: Part = parts.find(1).first(&conn).unwrap();
418
419 assert_eq!(part.descr, found.descr);
421 }
422}
423
424mod inventory_tests {}
426
427mod build_tests {}