Skip to content

Commit 447cb78

Browse files
committed
refactor(*): split interval functionality into smaller modules and redo formatting logic
1 parent 8038871 commit 447cb78

File tree

4 files changed

+540
-483
lines changed

4 files changed

+540
-483
lines changed

src/pg_formatter.rs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use pg_interval::Interval;
2+
3+
pub struct IntervalNorm {
4+
pub years: i32,
5+
pub months: i32,
6+
pub days: i32,
7+
pub hours: i64,
8+
pub minutes: i64,
9+
pub seconds: i64,
10+
pub microseconds: i64,
11+
}
12+
13+
impl<'a> From<&'a Interval> for IntervalNorm {
14+
fn from(val: &Interval) -> IntervalNorm {
15+
// grab the base values from the interval
16+
let months = val.months;
17+
let days = val.days;
18+
let microseconds = val.microseconds;
19+
// calc the year and get the remaining months
20+
let years = (months - (months % 12)) / 12;
21+
let months = months - years * 12;
22+
// calc the hours from the microseconds and update
23+
// the remaining microseconds.
24+
let hours = (microseconds - (microseconds % 3600000000)) / 3600000000;
25+
let microseconds = microseconds - hours * 3600000000;
26+
// calc the minutes from remaining microseconds and
27+
// update the remaining microseconds.
28+
let minutes = (microseconds - (microseconds % 60000000)) / 60000000;
29+
let microseconds = microseconds - minutes * 60000000;
30+
// calc the seconds and update the remaining microseconds.
31+
let seconds = (microseconds - (microseconds % 1000000)) / 1000000;
32+
let microseconds = microseconds - seconds * 1000000;
33+
IntervalNorm {
34+
years,
35+
months,
36+
days,
37+
hours,
38+
minutes,
39+
seconds,
40+
microseconds,
41+
}
42+
}
43+
}
44+
45+
impl IntervalNorm {
46+
/// Is all the values in the interval set to 0?
47+
fn is_zeroed(&self) -> bool {
48+
return self.years == 0
49+
&& self.months == 0
50+
&& self.hours == 0
51+
&& self.minutes == 0
52+
&& self.seconds == 0
53+
&& self.microseconds == 0;
54+
}
55+
56+
/// Is the years or month value set?
57+
fn is_year_month_present(&self) -> bool {
58+
return self.years != 0 || self.months != 0;
59+
}
60+
61+
/// Is the day value set?
62+
fn is_day_present(&self) -> bool {
63+
return self.days != 0;
64+
}
65+
66+
/// Is at least one of hours,minutes,seconds,microseconds values
67+
/// postive. There are no mixed intervals so we can assume that
68+
/// if one value is postive the rest are at least >= 0
69+
fn is_time_interval_pos(&self) -> bool {
70+
self.hours > 0 || self.minutes > 0 || self.seconds > 0 || self.microseconds > 0
71+
}
72+
73+
/// Is the hours,minutes, seconds, microseconds values set?
74+
fn is_time_present(&self) -> bool {
75+
self.hours != 0 || self.minutes != 0 || self.seconds != 0 || self.microseconds != 0
76+
}
77+
78+
/// The sql and postgres standard for the time intervals format
79+
/// are very similar. This gets the base string that is
80+
/// shared between them.
81+
fn get_sql_postgres_time_interval(&self) -> String {
82+
let mut time_interval = "".to_owned();
83+
if self.is_time_present() {
84+
let mut sign = "".to_owned();
85+
if !self.is_time_interval_pos() {
86+
sign = "-".to_owned();
87+
}
88+
time_interval.push_str(
89+
&*(sign
90+
+ &pad_i64(self.hours)
91+
+ ":"
92+
+ &pad_i64(self.minutes)
93+
+ ":"
94+
+ &pad_i64(self.seconds)),
95+
);
96+
if self.microseconds != 0 {
97+
time_interval.push_str(&*format!(".{:06}", self.microseconds))
98+
}
99+
}
100+
time_interval
101+
}
102+
103+
/// Produces a iso 8601 compliant interval string.
104+
pub fn to_iso_8601(self) -> String {
105+
if self.is_zeroed() {
106+
return "PT0S".to_owned();
107+
}
108+
let mut year_interval = "P".to_owned();
109+
let mut day_interval = "".to_owned();
110+
let mut time_interval;
111+
if self.is_time_present() {
112+
time_interval = "T".to_owned();
113+
if self.hours != 0 {
114+
time_interval.push_str(&format!("{}H", self.hours));
115+
}
116+
if self.minutes != 0 {
117+
time_interval.push_str(&format!("{}M", self.minutes));
118+
}
119+
if self.seconds != 0 {
120+
time_interval.push_str(&format!("{}S", self.seconds));
121+
}
122+
if self.microseconds != 0 {
123+
let ms = safe_abs_u64(self.microseconds);
124+
time_interval.push_str(&format!(".{:06}", ms));
125+
}
126+
} else {
127+
time_interval = "".to_owned();
128+
}
129+
if self.years != 0 {
130+
year_interval.push_str(&format!("{}Y", self.years));
131+
}
132+
if self.months != 0 {
133+
year_interval.push_str(&format!("{}M", self.months));
134+
}
135+
if self.days != 0 {
136+
day_interval.push_str(&format!("{}D", self.days));
137+
}
138+
year_interval.push_str(&*day_interval);
139+
year_interval.push_str(&*time_interval);
140+
return year_interval;
141+
}
142+
143+
/// Produces a postgres compliant interval string.
144+
pub fn to_postgres(self) -> String {
145+
if self.is_zeroed() {
146+
return "00:00:00".to_owned();
147+
}
148+
let mut year_interval = "".to_owned();
149+
let mut day_interval = "".to_owned();
150+
let time_interval = self.get_sql_postgres_time_interval();
151+
if self.is_day_present() {
152+
day_interval = format!("{:#?} days ", self.days)
153+
}
154+
if self.is_year_month_present() {
155+
if self.years != 0 {
156+
year_interval.push_str(&*format!("{:#?} year ", self.years))
157+
}
158+
if self.months != 0 {
159+
year_interval.push_str(&*format!("{:#?} mons ", self.months));
160+
}
161+
}
162+
year_interval.push_str(&*day_interval);
163+
year_interval.push_str(&*time_interval);
164+
year_interval.trim().to_owned()
165+
}
166+
167+
/// Produces a compliant sql interval string.
168+
pub fn to_sql(self) -> String {
169+
if self.is_zeroed() {
170+
return "0".to_owned();
171+
}
172+
let mut year_interval = "".to_owned();
173+
let mut day_interval = "".to_owned();
174+
let mut time_interval = self.get_sql_postgres_time_interval();
175+
if self.is_time_interval_pos() {
176+
time_interval = "+".to_owned() + &time_interval
177+
}
178+
if self.is_day_present() {
179+
day_interval = format!("{:#?} ", self.days);
180+
}
181+
if self.is_year_month_present() {
182+
let mut sign = "+".to_owned();
183+
if self.years < 0 || self.months < 0 {
184+
sign = "-".to_owned();
185+
}
186+
year_interval = format!("{}{}-{} ", sign, self.years, self.months);
187+
}
188+
year_interval.push_str(&day_interval);
189+
year_interval.push_str(&time_interval);
190+
return year_interval.trim().to_owned();
191+
}
192+
}
193+
194+
/// Safely maps a i64 value to a unsigned number
195+
/// without any overflow issues.
196+
fn safe_abs_u64(mut num: i64) -> u64 {
197+
let max = i64::max_value();
198+
let max_min = max * -1;
199+
if num <= max_min {
200+
let result = max as u64;
201+
num += max;
202+
num = num * -1;
203+
let result = result + num as u64;
204+
result
205+
} else {
206+
num.abs() as u64
207+
}
208+
}
209+
210+
/// Pads a i64 value with a width of 2.
211+
fn pad_i64(val: i64) -> String {
212+
let num;
213+
if val < 0 {
214+
num = safe_abs_u64(val);
215+
} else {
216+
num = val as u64;
217+
}
218+
return format!("{:02}", num);
219+
}

0 commit comments

Comments
 (0)