use crate::{AsValue, ColumnDef, DynQuery, TableRef, Value};
use proc_macro2::TokenStream;
use quote::{ToTokens, TokenStreamExt, quote};
use rust_decimal::prelude::ToPrimitive;
use serde_json::{Map, Number, Value as JsonValue};
use std::{
borrow::Cow,
cmp::min,
collections::BTreeMap,
ffi::{CStr, CString},
ptr,
};
use syn::Path;
#[derive(Clone)]
pub enum EitherIterator<A, B>
where
A: Iterator,
B: Iterator<Item = A::Item>,
{
Left(A),
Right(B),
}
impl<A, B> Iterator for EitherIterator<A, B>
where
A: Iterator,
B: Iterator<Item = A::Item>,
{
type Item = A::Item;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Left(a) => a.next(),
Self::Right(b) => b.next(),
}
}
}
pub fn value_to_json(v: &Value) -> Option<JsonValue> {
Some(match v {
_ if v.is_null() => JsonValue::Null,
Value::Boolean(Some(v), ..) => JsonValue::Bool(*v),
Value::Int8(Some(v), ..) => JsonValue::Number(Number::from_i128(*v as _)?),
Value::Int16(Some(v), ..) => JsonValue::Number(Number::from_i128(*v as _)?),
Value::Int32(Some(v), ..) => JsonValue::Number(Number::from_i128(*v as _)?),
Value::Int64(Some(v), ..) => JsonValue::Number(Number::from_i128(*v as _)?),
Value::Int128(Some(v), ..) => JsonValue::Number(Number::from_i128(*v as _)?),
Value::UInt8(Some(v), ..) => JsonValue::Number(Number::from_u128(*v as _)?),
Value::UInt16(Some(v), ..) => JsonValue::Number(Number::from_u128(*v as _)?),
Value::UInt32(Some(v), ..) => JsonValue::Number(Number::from_u128(*v as _)?),
Value::UInt64(Some(v), ..) => JsonValue::Number(Number::from_u128(*v as _)?),
Value::UInt128(Some(v), ..) => JsonValue::Number(Number::from_u128(*v as _)?),
Value::Float32(Some(v), ..) => JsonValue::Number(Number::from_f64(*v as _)?),
Value::Float64(Some(v), ..) => JsonValue::Number(Number::from_f64(*v as _)?),
Value::Decimal(Some(v), ..) => JsonValue::Number(Number::from_f64(v.to_f64()?)?),
Value::Char(Some(v), ..) => JsonValue::String(v.to_string()),
Value::Varchar(Some(v), ..) => JsonValue::String(v.to_string()),
Value::Blob(Some(v), ..) => JsonValue::Array(
v.iter()
.map(|v| Number::from_u128(*v as _).map(JsonValue::Number))
.collect::<Option<_>>()?,
),
v @ (Value::Date(Some(..), ..)
| Value::Time(Some(..), ..)
| Value::Timestamp(Some(..), ..)
| Value::TimestampWithTimezone(Some(..), ..)) => JsonValue::String(v.to_string()),
Value::Interval(Some(_v), ..) => {
return None;
}
Value::Uuid(Some(v), ..) => JsonValue::String(v.to_string()),
Value::Array(Some(v), ..) => {
JsonValue::Array(v.iter().map(value_to_json).collect::<Option<_>>()?)
}
Value::List(Some(v), ..) => {
JsonValue::Array(v.iter().map(value_to_json).collect::<Option<_>>()?)
}
Value::Map(Some(v), ..) => {
let mut map = Map::new();
for (k, v) in v.iter() {
let Ok(k) = String::try_from_value(k.clone()) else {
return None;
};
let Some(v) = value_to_json(v) else {
return None;
};
map.insert(k, v)?;
}
JsonValue::Object(map)
}
Value::Json(Some(v), ..) => v.clone(),
Value::Struct(Some(v), ..) => {
let mut map = Map::new();
for (k, v) in v.iter() {
let Some(v) = value_to_json(v) else {
return None;
};
map.insert(k.clone(), v)?;
}
JsonValue::Object(map)
}
Value::Unknown(Some(v), ..) => JsonValue::String(v.clone()),
_ => {
return None;
}
})
}
pub fn quote_btree_map<K: ToTokens, V: ToTokens>(value: &BTreeMap<K, V>) -> TokenStream {
let mut tokens = TokenStream::new();
for (k, v) in value {
let ks = k.to_token_stream();
let vs = v.to_token_stream();
tokens.append_all(quote! {
(#ks, #vs),
});
}
quote! {
::std::collections::BTreeMap::from([
#tokens
])
}
}
pub fn quote_cow<T: ToOwned + ToTokens + ?Sized>(value: &Cow<T>) -> TokenStream
where
<T as ToOwned>::Owned: ToTokens,
{
match value {
Cow::Borrowed(v) => quote! { ::std::borrow::Cow::Borrowed(#v) },
Cow::Owned(v) => quote! { ::std::borrow::Cow::Owned(#v) },
}
}
pub fn quote_option<T: ToTokens>(value: &Option<T>) -> TokenStream {
match value {
None => quote! { None },
Some(v) => quote! { Some(#v) },
}
}
pub fn matches_path(path: &Path, expect: &[&str]) -> bool {
let len = min(path.segments.len(), expect.len());
path.segments
.iter()
.rev()
.take(len)
.map(|v| &v.ident)
.eq(expect.iter().rev().take(len))
}
pub fn separated_by<T, F>(
out: &mut DynQuery,
values: impl IntoIterator<Item = T>,
mut f: F,
separator: &str,
) where
F: FnMut(&mut DynQuery, T),
{
let mut len = out.len();
for v in values {
if out.len() > len {
out.push_str(separator);
}
len = out.len();
f(out, v);
}
}
pub fn write_escaped(out: &mut DynQuery, value: &str, search: char, replace: &str) {
let mut position = 0;
for (i, c) in value.char_indices() {
if c == search {
out.push_str(&value[position..i]);
out.push_str(replace);
position = i + c.len_utf8();
}
}
out.push_str(&value[position..]);
}
pub fn as_c_string(str: impl Into<Vec<u8>>) -> CString {
CString::new(
str.into()
.into_iter()
.map(|b| if b == 0 { b'?' } else { b })
.collect::<Vec<u8>>(),
)
.unwrap_or_default()
}
pub fn error_message_from_ptr<'a>(ptr: &'a *const i8) -> Cow<'a, str> {
unsafe {
if *ptr != ptr::null() {
CStr::from_ptr(*ptr).to_string_lossy()
} else {
Cow::Borrowed("Unknown error: could not extract the error message")
}
}
}
pub fn consume_while<'s>(input: &mut &'s str, predicate: impl FnMut(&char) -> bool) -> &'s str {
let len = input
.chars()
.take_while(predicate)
.map(char::len_utf8)
.sum();
if len == 0 {
return "";
}
let result = &input[..len];
*input = &input[len..];
result
}
pub fn extract_number<'s, const SIGNED: bool>(input: &mut &'s str) -> &'s str {
let mut end = 0;
let mut chars = input.chars().peekable();
if SIGNED && matches!(chars.peek(), Some('+') | Some('-')) {
chars.next();
end += 1;
}
for _ in chars.take_while(char::is_ascii_digit) {
end += 1;
}
let result = &input[..end];
*input = &input[end..];
result
}
pub fn column_def(name: &str, table: &TableRef) -> Option<&'static ColumnDef> {
table.columns.iter().find(|v| v.name() == name)
}
#[macro_export]
macro_rules! number_to_month {
($month:expr, $throw:expr $(,)?) => {
match $month {
1 => Month::January,
2 => Month::February,
3 => Month::March,
4 => Month::April,
5 => Month::May,
6 => Month::June,
7 => Month::July,
8 => Month::August,
9 => Month::September,
10 => Month::October,
11 => Month::November,
12 => Month::December,
_ => $throw,
}
};
}
#[macro_export]
macro_rules! month_to_number {
($month:expr $(,)?) => {
match $month {
Month::January => 1,
Month::February => 2,
Month::March => 3,
Month::April => 4,
Month::May => 5,
Month::June => 6,
Month::July => 7,
Month::August => 8,
Month::September => 9,
Month::October => 10,
Month::November => 11,
Month::December => 12,
}
};
}
#[macro_export]
macro_rules! possibly_parenthesized {
($out:ident, $cond:expr, $v:expr) => {
if $cond {
$out.push('(');
$v;
$out.push(')');
} else {
$v;
}
};
}
pub const TRUNCATE_LONG_LIMIT: usize = {
#[cfg(debug_assertions)]
{
2000
}
#[cfg(not(debug_assertions))]
{
497
}
};
#[macro_export]
macro_rules! truncate_long {
($query:expr) => {
format_args!(
"{}{}",
&$query[..::std::cmp::min($query.len(), $crate::TRUNCATE_LONG_LIMIT)].trim(),
if $query.len() > $crate::TRUNCATE_LONG_LIMIT {
"...\n"
} else {
""
},
)
};
($query:expr,true) => {{
let query = $query;
format!(
"{}{}",
&query[..::std::cmp::min(query.len(), $crate::TRUNCATE_LONG_LIMIT)].trim(),
if query.len() > $crate::TRUNCATE_LONG_LIMIT {
"...\n"
} else {
""
},
)
}};
}
#[macro_export]
macro_rules! send_value {
($tx:ident, $value:expr) => {{
if let Err(e) = $tx.send($value) {
log::error!("{e:#}");
}
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! take_until {
($original:expr, $($parser:expr),+ $(,)?) => {{
let macro_local_input = $original.fork();
let mut macro_local_result = (
TokenStream::new(),
($({
let _ = $parser;
None
}),+),
);
loop {
if macro_local_input.is_empty() {
break;
}
let mut parsed = false;
let produced = ($({
let attempt = macro_local_input.fork();
if let Ok(content) = ($parser)(&attempt) {
macro_local_input.advance_to(&attempt);
parsed = true;
Some(content)
} else {
None
}
}),+);
if parsed {
macro_local_result.1 = produced;
break;
}
macro_local_result.0.append(macro_local_input.parse::<TokenTree>()?);
}
$original.advance_to(¯o_local_input);
macro_local_result
}};
}
#[macro_export]
macro_rules! impl_executor_transaction {
($driver:ty, $transaction:ident $(< $lt:lifetime >)?, $connection:ident) => {
impl $(<$lt>)? ::tank_core::Executor for $transaction $(<$lt>)? {
type Driver = $driver;
fn accepts_multiple_statements(&self) -> bool {
self.$connection.accepts_multiple_statements()
}
fn do_prepare(
&mut self,
sql: String,
) -> impl Future<Output = ::tank_core::Result<::tank_core::Query<Self::Driver>>> + Send
{
self.$connection.do_prepare(sql)
}
fn run<'s>(
&'s mut self,
query: impl ::tank_core::AsQuery<Self::Driver> + 's,
) -> impl ::tank_core::stream::Stream<
Item = ::tank_core::Result<::tank_core::QueryResult>,
> + Send {
self.$connection.run(query)
}
fn fetch<'s>(
&'s mut self,
query: impl ::tank_core::AsQuery<Self::Driver> + 's,
) -> impl ::tank_core::stream::Stream<
Item = ::tank_core::Result<::tank_core::Row>,
> + Send
+ 's {
self.$connection.fetch(query)
}
fn execute<'s>(
&'s mut self,
query: impl ::tank_core::AsQuery<Self::Driver> + 's,
) -> impl Future<Output = ::tank_core::Result<::tank_core::RowsAffected>> + Send {
self.$connection.execute(query)
}
fn append<'a, E, It>(
&mut self,
entities: It,
) -> impl Future<Output = ::tank_core::Result<::tank_core::RowsAffected>> + Send
where
E: ::tank_core::Entity + 'a,
It: IntoIterator<Item = &'a E> + Send,
<It as IntoIterator>::IntoIter: Send,
{
self.$connection.append(entities)
}
}
}
}
#[macro_export]
macro_rules! current_timestamp_ms {
() => {{}};
}