1use crate::chkerr;
17use crate::Context;
18use crate::Result;
19use odpic_sys::*;
20use std::fmt;
21use std::mem::MaybeUninit;
22use std::num::ParseIntError;
23use std::result;
24use std::str::FromStr;
25
26#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
47pub struct Version {
48 major: i32,
49 minor: i32,
50 update: i32,
51 patch: i32,
52 port_update: i32,
53}
54
55impl Version {
56 pub const fn new(major: i32, minor: i32, update: i32, patch: i32, port_update: i32) -> Version {
58 Version {
59 major,
60 minor,
61 update,
62 patch,
63 port_update,
64 }
65 }
66
67 pub fn client() -> Result<Version> {
78 let ctx = Context::new0()?;
79 let mut ver = MaybeUninit::uninit();
80 chkerr!(
81 &ctx,
82 dpiContext_getClientVersion(ctx.context, ver.as_mut_ptr())
83 );
84 Ok(Version::new_from_dpi_ver(unsafe { ver.assume_init() }))
85 }
86
87 pub(crate) fn new_from_dpi_ver(ver: dpiVersionInfo) -> Version {
88 Version::new(
89 ver.versionNum,
90 ver.releaseNum,
91 ver.updateNum,
92 ver.portReleaseNum,
93 ver.portUpdateNum,
94 )
95 }
96
97 pub fn major(&self) -> i32 {
99 self.major
100 }
101
102 pub fn minor(&self) -> i32 {
104 self.minor
105 }
106
107 pub fn update(&self) -> i32 {
109 self.update
110 }
111
112 pub fn patch(&self) -> i32 {
114 self.patch
115 }
116
117 pub fn port_update(&self) -> i32 {
119 self.port_update
120 }
121}
122
123impl fmt::Display for Version {
124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125 write!(
126 f,
127 "{}.{}.{}.{}.{}",
128 self.major, self.minor, self.update, self.patch, self.port_update
129 )
130 }
131}
132
133impl FromStr for Version {
134 type Err = ParseIntError;
135
136 fn from_str(s: &str) -> result::Result<Self, Self::Err> {
137 let mut iter = s.split('.').fuse();
138 let major = iter.next().map_or(Ok(0), |s| s.parse::<i32>())?;
139 let minor = iter.next().map_or(Ok(0), |s| s.parse::<i32>())?;
140 let update = iter.next().map_or(Ok(0), |s| s.parse::<i32>())?;
141 let patch = iter.next().map_or(Ok(0), |s| s.parse::<i32>())?;
142 let port_update = iter.next().map_or(Ok(0), |s| s.parse::<i32>())?;
143 Ok(Version::new(major, minor, update, patch, port_update))
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use crate::test_util;
151
152 #[test]
153 fn to_string() {
154 assert_eq!(Version::new(12, 1, 2, 3, 4).to_string(), "12.1.2.3.4");
155 }
156
157 #[test]
158 fn from_str() {
159 assert_eq!(
160 "12".parse::<Version>().unwrap(),
161 Version::new(12, 0, 0, 0, 0)
162 );
163 assert_eq!(
164 "12.1".parse::<Version>().unwrap(),
165 Version::new(12, 1, 0, 0, 0)
166 );
167 assert_eq!(
168 "12.1.2".parse::<Version>().unwrap(),
169 Version::new(12, 1, 2, 0, 0)
170 );
171 assert_eq!(
172 "12.1.2.3".parse::<Version>().unwrap(),
173 Version::new(12, 1, 2, 3, 0)
174 );
175 assert_eq!(
176 "12.1.2.3.4".parse::<Version>().unwrap(),
177 Version::new(12, 1, 2, 3, 4)
178 );
179 }
180
181 #[test]
182 fn client_version() {
183 let ver = Version::client().unwrap();
184 let conn = test_util::connect().unwrap();
185 let mut ver_from_query = conn.query_row_as::<String>("SELECT client_version FROM v$session_connect_info WHERE sid = SYS_CONTEXT('USERENV', 'SID')", &[]).unwrap();
186 if let Some(pos) = ver_from_query.len().checked_sub(2) {
189 if ver_from_query.as_bytes()[pos] == b'0' {
190 ver_from_query.remove(pos);
191 }
192 }
193 assert_eq!(ver.to_string(), ver_from_query);
194 }
195}