Skip to content

Commit 47e3aaa

Browse files
committed
refactor(*): split formatting code into seperate modules
1 parent 84f8062 commit 47e3aaa

File tree

11 files changed

+433
-236
lines changed

11 files changed

+433
-236
lines changed

src/integrations/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
mod rust_postgres;
1+
mod rust_postgres;

src/integrations/rust_postgres.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
use std::error::Error;
2-
use postgres::types::{FromSql, IsNull, ToSql, Type, INTERVAL};
3-
use ::Interval;
41
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
5-
2+
use postgres::types::{FromSql, IsNull, ToSql, Type, INTERVAL};
3+
use std::error::Error;
4+
use Interval;
65

76
impl FromSql for Interval {
8-
fn from_sql(_: &Type, mut raw: &[u8])
9-
-> Result<Interval, Box<Error + Sync + Send>> {
10-
7+
fn from_sql(_: &Type, mut raw: &[u8]) -> Result<Interval, Box<Error + Sync + Send>> {
118
let microseconds: i64 = raw.read_i64::<BigEndian>()?;
129
let days: i32 = raw.read_i32::<BigEndian>()?;
1310
let months: i32 = raw.read_i32::<BigEndian>()?;
1411

15-
Ok(Interval{months, days, microseconds})
12+
Ok(Interval {
13+
months,
14+
days,
15+
microseconds,
16+
})
1617
}
1718

1819
fn accepts(ty: &Type) -> bool {
@@ -24,9 +25,7 @@ impl FromSql for Interval {
2425
}
2526

2627
impl ToSql for Interval {
27-
fn to_sql(&self, _: &Type, out: &mut Vec<u8>)
28-
-> Result<IsNull, Box<Error + Sync + Send>> {
29-
28+
fn to_sql(&self, _: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
3029
out.write_i64::<BigEndian>(self.microseconds)?;
3130
out.write_i32::<BigEndian>(self.days)?;
3231
out.write_i32::<BigEndian>(self.months)?;

src/interval_fmt/iso_8601.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use interval_norm::IntervalNorm;
2+
3+
impl IntervalNorm {
4+
/// Produces a iso 8601 compliant interval string.
5+
pub fn into_iso_8601(self) -> String {
6+
if self.is_zeroed() {
7+
return "PT0S".to_owned();
8+
}
9+
let mut year_interval = "P".to_owned();
10+
let mut day_interval = "".to_owned();
11+
let mut time_interval;
12+
if self.is_time_present() {
13+
time_interval = "T".to_owned();
14+
if self.hours != 0 {
15+
time_interval.push_str(&format!("{}H", self.hours));
16+
}
17+
if self.minutes != 0 {
18+
time_interval.push_str(&format!("{}M", self.minutes));
19+
}
20+
if self.seconds != 0 {
21+
time_interval.push_str(&format!("{}S", self.seconds));
22+
}
23+
if self.microseconds != 0 {
24+
let ms = super::safe_abs_u64(self.microseconds);
25+
time_interval.push_str(&format!(".{:06}", ms));
26+
}
27+
} else {
28+
time_interval = "".to_owned();
29+
}
30+
if self.years != 0 {
31+
year_interval.push_str(&format!("{}Y", self.years));
32+
}
33+
if self.months != 0 {
34+
year_interval.push_str(&format!("{}M", self.months));
35+
}
36+
if self.days != 0 {
37+
day_interval.push_str(&format!("{}D", self.days));
38+
}
39+
year_interval.push_str(&*day_interval);
40+
year_interval.push_str(&*time_interval);
41+
year_interval
42+
}
43+
}

src/interval_fmt/mod.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
mod iso_8601;
2+
mod postgres;
3+
mod sql;
4+
5+
use std::ops::Neg;
6+
7+
/// Safely maps a i64 value to a unsigned number
8+
/// without any overflow issues.
9+
fn safe_abs_u64(mut num: i64) -> u64 {
10+
let max = i64::max_value();
11+
let max_min = max.neg();
12+
if num <= max_min {
13+
let result = max as u64;
14+
num += max;
15+
num *= -1;
16+
result + num as u64
17+
} else {
18+
num.abs() as u64
19+
}
20+
}
21+
22+
/// Safely maps a i32 value to a unsigned number
23+
/// without any overflow issues.
24+
fn safe_abs_u32(mut num: i32) -> u32 {
25+
let max = i32::max_value();
26+
let max_min = max.neg();
27+
if num <= max_min {
28+
let result = max as u32;
29+
num += max;
30+
num *= -1;
31+
result + num as u32
32+
} else {
33+
num.abs() as u32
34+
}
35+
}
36+
37+
/// Pads a i64 value with a width of 2.
38+
fn pad_i64(val: i64) -> String {
39+
let num = if val < 0 {
40+
safe_abs_u64(val)
41+
} else {
42+
val as u64
43+
};
44+
return format!("{:02}", num);
45+
}

src/interval_fmt/postgres.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use interval_norm::IntervalNorm;
2+
3+
impl IntervalNorm {
4+
/// Produces a postgres compliant interval string.
5+
pub fn into_postgres(self) -> String {
6+
if self.is_zeroed() {
7+
return "00:00:00".to_owned();
8+
}
9+
let mut year_interval = "".to_owned();
10+
let mut day_interval = "".to_owned();
11+
let time_interval = self.get_postgres_time_interval();
12+
if self.is_day_present() {
13+
day_interval = format!("{:#?} days ", self.days)
14+
}
15+
if self.is_year_month_present() {
16+
if self.years != 0 {
17+
year_interval.push_str(&*format!("{:#?} year ", self.years))
18+
}
19+
if self.months != 0 {
20+
year_interval.push_str(&*format!("{:#?} mons ", self.months));
21+
}
22+
}
23+
year_interval.push_str(&*day_interval);
24+
year_interval.push_str(&*time_interval);
25+
year_interval.trim().to_owned()
26+
}
27+
28+
fn get_postgres_time_interval(&self) -> String {
29+
let mut time_interval = "".to_owned();
30+
if self.is_time_present() {
31+
let sign = if !self.is_time_interval_pos() {
32+
"-".to_owned()
33+
} else {
34+
"".to_owned()
35+
};
36+
let hours = super::pad_i64(self.hours);
37+
time_interval.push_str(
38+
&*(sign
39+
+ &hours
40+
+ ":"
41+
+ &super::pad_i64(self.minutes)
42+
+ ":"
43+
+ &super::pad_i64(self.seconds)),
44+
);
45+
if self.microseconds != 0 {
46+
time_interval.push_str(&*format!(".{:06}", super::safe_abs_u64(self.microseconds)))
47+
}
48+
}
49+
time_interval
50+
}
51+
}

src/interval_fmt/sql.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use interval_norm::IntervalNorm;
2+
3+
impl IntervalNorm {
4+
pub fn into_sql(self) -> String {
5+
if self.is_zeroed() {
6+
return "0".to_owned();
7+
} else if !self.is_time_present() && !self.is_day_present() {
8+
get_year_month(self.months, self.years, true)
9+
} else if !self.is_time_present() && !self.is_year_month_present() {
10+
format!("{} 0:00:00", self.days)
11+
} else if !self.is_year_month_present() && !self.is_day_present() {
12+
get_time_interval(
13+
self.hours,
14+
self.minutes,
15+
self.seconds,
16+
self.microseconds,
17+
self.is_time_interval_pos(),
18+
true,
19+
)
20+
} else {
21+
let year_month = get_year_month(self.months, self.years, false);
22+
let time_interval = get_time_interval(
23+
self.hours,
24+
self.minutes,
25+
self.seconds,
26+
self.microseconds,
27+
self.is_time_interval_pos(),
28+
false,
29+
);
30+
format!("{} {:+} {}", year_month, self.days, time_interval)
31+
}
32+
}
33+
}
34+
35+
fn get_year_month(mons: i32, years: i32, is_only_year_month: bool) -> String {
36+
let months = super::safe_abs_u32(mons);
37+
if years == 0 || is_only_year_month {
38+
format!("{}-{}", years, months)
39+
} else {
40+
format!("{:+}-{}", years, months)
41+
}
42+
}
43+
44+
fn get_time_interval(
45+
hours: i64,
46+
mins: i64,
47+
secs: i64,
48+
micros: i64,
49+
is_time_interval_pos: bool,
50+
is_only_time: bool,
51+
) -> String {
52+
let mut interval = "".to_owned();
53+
if is_time_interval_pos && is_only_time {
54+
interval.push_str(&format!("{}:{:02}:{:02}", hours, mins, secs));
55+
} else {
56+
let minutes = super::safe_abs_u64(mins);
57+
let seconds = super::safe_abs_u64(secs);
58+
interval.push_str(&format!("{:+}:{:02}:{:02}", hours, minutes, seconds));
59+
}
60+
if micros != 0 {
61+
let microseconds = format!(".{:06}", super::safe_abs_u64(micros));
62+
interval.push_str(&microseconds);
63+
}
64+
interval
65+
}

src/interval_norm.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 % 3_600_000_000)) / 3_600_000_000;
25+
let microseconds = microseconds - hours * 3_600_000_000;
26+
// calc the minutes from remaining microseconds and
27+
// update the remaining microseconds.
28+
let minutes = (microseconds - (microseconds % 60_000_000)) / 60_000_000;
29+
let microseconds = microseconds - minutes * 60_000_000;
30+
// calc the seconds and update the remaining microseconds.
31+
let seconds = (microseconds - (microseconds % 1_000_000)) / 1_000_000;
32+
let microseconds = microseconds - seconds * 1_000_000;
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+
pub fn is_zeroed(&self) -> bool {
48+
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+
pub fn is_year_month_present(&self) -> bool {
58+
self.years != 0 || self.months != 0
59+
}
60+
61+
/// Is the day value set?
62+
pub fn is_day_present(&self) -> bool {
63+
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+
pub 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+
pub fn is_time_present(&self) -> bool {
75+
self.hours != 0 || self.minutes != 0 || self.seconds != 0 || self.microseconds != 0
76+
}
77+
}

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ extern crate byteorder;
77
#[cfg(feature = "postgres")]
88
mod integrations;
99

10-
mod pg_formatter;
10+
mod interval_fmt;
11+
mod interval_norm;
1112
mod pg_interval;
1213
mod pg_interval_add;
1314
mod pg_interval_sub;
14-
1515
pub use pg_interval::Interval;

0 commit comments

Comments
 (0)