odbc_api/handles/
sql_char.rs1use super::buffer::{buf_ptr, mut_buf_ptr};
17use std::{
18 borrow::Cow,
19 mem::{size_of, size_of_val},
20};
21
22#[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
23use std::{ffi::CStr, string::FromUtf8Error};
24
25#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
26use std::{
27 char::{decode_utf16, DecodeUtf16Error},
28 marker::PhantomData,
29};
30
31#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
32use widestring::{U16CStr, U16String};
33
34#[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
35pub type SqlChar = u8;
36#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
37pub type SqlChar = u16;
38
39#[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
40pub type DecodingError = FromUtf8Error;
41#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
42pub type DecodingError = DecodeUtf16Error;
43
44#[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
45pub fn slice_to_utf8(text: &[u8]) -> Result<String, FromUtf8Error> {
46 String::from_utf8(text.to_owned())
47}
48#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
49pub fn slice_to_utf8(text: &[u16]) -> Result<String, DecodeUtf16Error> {
50 decode_utf16(text.iter().copied()).collect()
51}
52
53#[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
54pub fn slice_to_cow_utf8(text: &[u8]) -> Cow<str> {
55 String::from_utf8_lossy(text)
56}
57#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
58pub fn slice_to_cow_utf8(text: &[u16]) -> Cow<str> {
59 let text: Result<String, _> = decode_utf16(text.iter().copied()).collect();
60 text.unwrap().into()
61}
62
63#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
64fn sz_to_utf8(buffer: &[u16]) -> String {
65 let c_str = U16CStr::from_slice_truncate(buffer).unwrap();
66 c_str.to_string_lossy()
67}
68#[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
69fn sz_to_utf8(buffer: &[u8]) -> String {
70 let end = buffer
72 .iter()
73 .enumerate()
74 .find(|(_index, &character)| character == b'\0')
75 .expect("Buffer must contain terminating zero.")
76 .0;
77 let c_str = unsafe { CStr::from_bytes_with_nul_unchecked(&buffer[..=end]) };
78 c_str.to_string_lossy().into_owned()
79}
80
81pub fn binary_length(buffer: &[SqlChar]) -> usize {
83 size_of_val(buffer)
84}
85
86pub fn is_truncated_bin(buffer: &[SqlChar], actual_length_bin: usize) -> bool {
92 size_of_val(buffer) <= actual_length_bin
93}
94
95pub fn resize_to_fit_with_tz(buffer: &mut Vec<SqlChar>, required_binary_length: usize) {
99 buffer.resize((required_binary_length / size_of::<SqlChar>()) + 2, 0);
104}
105
106pub fn resize_to_fit_without_tz(buffer: &mut Vec<SqlChar>, required_binary_length: usize) {
110 buffer.resize(required_binary_length / size_of::<SqlChar>(), 0);
111}
112
113pub struct SqlText<'a> {
118 #[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
121 text: U16String,
122 #[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
125 _ref: PhantomData<&'a str>,
126 #[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
128 text: &'a str,
129}
130
131impl<'a> SqlText<'a> {
132 #[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
133 pub fn new(text: &'a str) -> Self {
135 Self {
136 text: U16String::from_str(text),
137 _ref: PhantomData,
138 }
139 }
140 #[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
141 pub fn new(text: &'a str) -> Self {
143 Self { text }
144 }
145
146 #[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
147 pub fn ptr(&self) -> *const u16 {
148 buf_ptr(self.text.as_slice())
149 }
150 #[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
151 pub fn ptr(&self) -> *const u8 {
152 buf_ptr(self.text.as_bytes())
153 }
154
155 pub fn len_char(&self) -> usize {
157 self.text.len()
158 }
159}
160
161pub struct SzBuffer {
164 buffer: Vec<SqlChar>,
165}
166
167impl SzBuffer {
168 pub fn with_capacity(capacity: usize) -> Self {
172 Self {
173 buffer: vec![0; capacity + 1],
175 }
176 }
177
178 pub fn mut_buf(&mut self) -> &mut [SqlChar] {
179 self.buffer.resize(self.buffer.capacity(), 0);
181 &mut self.buffer
182 }
183
184 pub fn to_utf8(&self) -> String {
186 sz_to_utf8(&self.buffer)
187 }
188}
189
190pub struct OutputStringBuffer {
192 buffer: Vec<SqlChar>,
194 actual_length: i16,
197}
198
199impl OutputStringBuffer {
200 pub fn empty() -> Self {
203 Self::with_buffer_size(0)
204 }
205
206 pub fn with_buffer_size(max_str_len: usize) -> Self {
210 Self {
211 buffer: vec![0; max_str_len],
212 actual_length: 0,
213 }
214 }
215
216 pub fn mut_buf_ptr(&mut self) -> *mut SqlChar {
218 mut_buf_ptr(&mut self.buffer)
219 }
220
221 pub fn buf_len(&self) -> i16 {
223 self.buffer.len().try_into().unwrap()
226 }
227
228 pub fn mut_actual_len_ptr(&mut self) -> *mut i16 {
230 &mut self.actual_length as *mut i16
231 }
232
233 pub fn to_utf8(&self) -> String {
235 if self.buffer.is_empty() {
236 return String::new();
237 }
238
239 if self.is_truncated() {
240 slice_to_utf8(&self.buffer[0..(self.buffer.len() - 1)]).unwrap()
243 } else {
244 let actual_length: usize = self.actual_length.try_into().unwrap();
247 slice_to_utf8(&self.buffer[0..actual_length]).unwrap()
248 }
249 }
250
251 pub fn is_truncated(&self) -> bool {
253 self.actual_length >= self.buffer.len().try_into().unwrap()
254 }
255}