Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use crate::models::NewUserProgram;
use crate::schema::user_programs::columns;
use crate::schema::user_programs::columns::discord_user_id;
use crate::schema::user_programs::dsl::user_programs;
use bigdecimal::{BigDecimal, FromPrimitive};
use diesel::{BoolExpressionMethods, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use serenity::model::id::UserId;
use std::collections::HashMap;
pub(crate) struct Program {
pub(crate) code: HashMap<u32, String>,
}
impl Program {
pub fn new() -> Self {
Self {
code: HashMap::new(),
}
}
pub fn stringify(&self) -> String {
let mut code: Vec<(u32, String)> =
self.code.iter().map(|(a, b)| (*a, b.to_owned())).collect();
code.sort_by(|a, b| a.0.cmp(&b.0));
code.into_iter()
.map(|a| a.1)
.collect::<Vec<String>>()
.join("\n")
}
pub fn stringy_line_nums(&self) -> String {
let mut code: Vec<(u32, String)> =
self.code.iter().map(|(a, b)| (*a, b.to_owned())).collect();
code.sort_by(|a, b| a.0.cmp(&b.0));
code.into_iter()
.map(|a| format!("{}\t{}", a.0, a.1))
.collect::<Vec<String>>()
.join("\n")
}
pub fn save_program(&self, conn: &PgConnection, user_id: UserId, name: &str) -> Option<()> {
let code = self.stringy_line_nums();
let new_program = NewUserProgram {
discord_user_id: BigDecimal::from_u64(*user_id.as_u64()).unwrap(),
name,
code: code.as_str(),
};
diesel::insert_into(user_programs)
.values(&new_program)
.on_conflict((discord_user_id, columns::name))
.do_update()
.set(columns::code.eq(&code))
.execute(conn)
.ok()?;
Some(())
}
pub fn load_program(&mut self, conn: &PgConnection, user_id: UserId, name: &str) -> Option<()> {
let code: Vec<String> = user_programs
.filter(
columns::discord_user_id
.eq(BigDecimal::from_u64(*user_id.as_u64()).unwrap())
.and(columns::name.eq(name)),
)
.limit(1)
.select(columns::code)
.get_results(conn)
.ok()?;
if code.is_empty() {
return None;
}
let code = &code[0];
self.parse_string(code)?;
Some(())
}
fn parse_string(&mut self, code: &str) -> Option<()> {
let mut valid = true;
let code: HashMap<u32, String> = code
.split('\n')
.map(|line| {
let mut iter = line.splitn(2, &[' ', '\t'][..]);
// This unwrap_or_else thing is pretty ugly
// Is there a better way?
let line_num: u32 = iter
.next()
.unwrap_or_else(|| {
valid = false;
"0"
})
.parse()
.unwrap_or_else(|_| {
valid = false;
0
});
let line_code = iter
.next()
.unwrap_or_else(|| {
valid = false;
""
})
.to_owned();
(line_num, line_code)
})
.collect();
if valid {
self.code = code;
Some(())
} else {
None
}
}
}