<commit_before>use crate::util::msb;
use std::arch::x86_64::*;

macro_rules! satd_hbd_avx2 {
  ($(($W:expr, $H:expr)),*) => {
    $(
      paste::item! {
        #[target_feature(enable = "avx2")]
        pub(crate) unsafe extern fn [<rav1e_satd_ $W x $H _hbd_avx2>](
          src: *const u16, src_stride: isize, dst: *const u16, dst_stride: isize,
        ) -> u32 {
          // Size of hadamard transform should be 4x4 or 8x8.
          // 4x* and *x4 use 4x4 and all other use 8x8.
          // Because these are constants via the macro system,
          // the branches here will be optimized out.
          let size: usize = $W.min($H).min(8);
          let sizei = size as isize;

          let mut sum = 0u64;

          // Loop over chunks the size of the chosen transform
          for chunk_y in (0isize..$H).step_by(size) {
            for chunk_x in (0isize..$W).step_by(size) {
              let buf: &mut [i32] = &mut [0; 8 * 8][..size * size];
              let input1: &mut [i32] = &mut [0; 8 * 8][..size * size];
              let input2: &mut [i32] = &mut [0; 8 * 8][..size * size];
              for y in 0..(sizei) {
                let y1 = chunk_y + y;
                for x in 0..(sizei) {
                  let x1 = chunk_x + x;
                  input1.as_mut_ptr().offset(y * sizei + x).write(src.offset(y1 * src_stride + x1).read() as i32);
                  input2.as_mut_ptr().offset(y * sizei + x).write(dst.offset(y1 * dst_stride + x1).read() as i32);
                }
              }

              // Move the difference of the transforms to a buffer
              for y in 0..(sizei) {
                if size == 4 {
                  let row_src = _mm_loadu_si128(input1.as_ptr().offset(y * sizei) as *const __m128i);
                  let row_dst = _mm_loadu_si128(input2.as_ptr().offset(y * sizei) as *const __m128i);
                  let diff = _mm_sub_epi32(row_src, row_dst);
                  _mm_storeu_si128(buf.as_mut_ptr().offset(y * sizei) as *mut __m128i, diff);
                } else {
                  let row_src = _mm256_loadu_si256(input1.as_ptr().offset(y * sizei) as *const __m256i);
                  let row_dst = _mm256_loadu_si256(input2.as_ptr().offset(y * sizei) as *const __m256i);
                  let diff = _mm256_sub_epi32(row_src, row_dst);
                  _mm256_storeu_si256(buf.as_mut_ptr().offset(y * sizei) as *mut __m256i, diff);
                }
              }

              // Perform the hadamard transform on the differences,
              // Then sum the absolute values of the transformed differences
              if size == 4 {
                hadamard4x4(buf.as_mut_ptr());
                let row1 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(0) as *const __m256i));
                let row2 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(8) as *const __m256i));
                let sum_t = _mm256_add_epi32(row1, row2);
                let mut abs = [0u32; 8];
                _mm256_storeu_si256(abs.as_mut_ptr() as *mut __m256i, sum_t);
                sum += abs.iter().copied().map(|a| a as u64).sum::<u64>();
              } else {
                hadamard8x8(buf.as_mut_ptr());
                let row1 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(0) as *const __m256i));
                let row2 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(8) as *const __m256i));
                let row3 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(16) as *const __m256i));
                let row4 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(24) as *const __m256i));
                let row5 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(32) as *const __m256i));
                let row6 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(40) as *const __m256i));
                let row7 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(48) as *const __m256i));
                let row8 = _mm256_abs_epi32(_mm256_loadu_si256(buf.as_ptr().add(56) as *const __m256i));
                let sum1 = _mm256_add_epi32(row1, row2);
                let sum2 = _mm256_add_epi32(row3, row4);
                let sum3 = _mm256_add_epi32(row5, row6);
                let sum4 = _mm256_add_epi32(row7, row8);
                let sum5 = _mm256_add_epi32(sum1, sum2);
                let sum6 = _mm256_add_epi32(sum3, sum4);
                let sum_t = _mm256_add_epi32(sum5, sum6);
                let mut abs_sums = [0u32; 8];
                _mm256_storeu_si256(abs_sums.as_mut_ptr() as *mut __m256i, sum_t);
                sum += abs_sums.iter().copied().map(|a| a as u64).sum::<u64>();
              }
            }
          }

          // Normalize the results
          let ln = msb(size as i32) as u64;
          ((sum + (1 << ln >> 1)) >> ln) as u32
        }
      }
    )*
  }
}

satd_hbd_avx2!(
  (4, 4),
  (4, 8),
  (4, 16),
  (8, 4),
  (8, 8),
  (8, 16),
  (8, 32),
  (16, 4),
  (16, 8),
  (16, 16),
  (16, 32),
  (16, 64),
  (32, 8),
  (32, 16),
  (32, 32),
  (32, 64),
  (64, 16),
  (64, 32),
  (64, 64),
  (64, 128),
  (128, 64),
  (128, 128)
);

#[inline(always)]
const fn butterfly(a: i32, b: i32) -> (i32, i32) {
  ((a + b), (a - b))
}

#[inline(always)]
#[allow(clippy::identity_op, clippy::erasing_op)]
unsafe fn hadamard4_1d(
  data: *mut i32, n: usize, stride0: usize, stride1: usize,
) {
  for i in 0..n {
    let sub = data.add(i * stride0);
    let (a0, a1) =
      butterfly(sub.add(0 * stride1).read(), sub.add(1 * stride1).read());
    let (a2, a3) =
      butterfly(sub.add(2 * stride1).read(), sub.add(3 * stride1).read());
    let (b0, b2) = butterfly(a0, a2);
    let (b1, b3) = butterfly(a1, a3);
    sub.add(0 * stride1).write(b0);
    sub.add(1 * stride1).write(b1);
    sub.add(2 * stride1).write(b2);
    sub.add(3 * stride1).write(b3);
  }
}

#[inline(always)]
#[allow(clippy::identity_op, clippy::erasing_op)]
unsafe fn hadamard8_1d(
  data: *mut i32, n: usize, stride0: usize, stride1: usize,
) {
  for i in 0..n {
    let sub = data.add(i * stride0);

    let (a0, a1) =
      butterfly(sub.add(0 * stride1).read(), sub.add(1 * stride1).read());
    let (a2, a3) =
      butterfly(sub.add(2 * stride1).read(), sub.add(3 * stride1).read());
    let (a4, a5) =
      butterfly(sub.add(4 * stride1).read(), sub.add(5 * stride1).read());
    let (a6, a7) =
      butterfly(sub.add(6 * stride1).read(), sub.add(7 * stride1).read());

    let (b0, b2) = butterfly(a0, a2);
    let (b1, b3) = butterfly(a1, a3);
    let (b4, b6) = butterfly(a4, a6);
    let (b5, b7) = butterfly(a5, a7);

    let (c0, c4) = butterfly(b0, b4);
    let (c1, c5) = butterfly(b1, b5);
    let (c2, c6) = butterfly(b2, b6);
    let (c3, c7) = butterfly(b3, b7);

    sub.add(0 * stride1).write(c0);
    sub.add(1 * stride1).write(c1);
    sub.add(2 * stride1).write(c2);
    sub.add(3 * stride1).write(c3);
    sub.add(4 * stride1).write(c4);
    sub.add(5 * stride1).write(c5);
    sub.add(6 * stride1).write(c6);
    sub.add(7 * stride1).write(c7);
  }
}

#[inline(always)]
fn hadamard2d(data: *mut i32, (w, h): (usize, usize)) {
  /*Vertical transform.*/
  let vert_func = if h == 4 { hadamard4_1d } else { hadamard8_1d };
  unsafe {
    vert_func(data, w, 1, h);
  }
  /*Horizontal transform.*/
  let horz_func = if w == 4 { hadamard4_1d } else { hadamard8_1d };
  unsafe {
    horz_func(data, h, w, 1);
  }
}

#[inline(always)]
fn hadamard4x4(data: *mut i32) {
  hadamard2d(data, (4, 4));
}

#[inline(always)]
fn hadamard8x8(data: *mut i32) {
  hadamard2d(data, (8, 8));
}
<commit_msg>Use aligned stores and loads for HBD get_satd intrinsics<commit_after>use crate::util::{msb, Aligned};
use std::arch::x86_64::*;

macro_rules! satd_hbd_avx2 {
  ($(($W:expr, $H:expr)),*) => {
    $(
      paste::item! {
        #[target_feature(enable = "avx2")]
        pub(crate) unsafe extern fn [<rav1e_satd_ $W x $H _hbd_avx2>](
          src: *const u16, src_stride: isize, dst: *const u16, dst_stride: isize,
        ) -> u32 {
          // Size of hadamard transform should be 4x4 or 8x8.
          // 4x* and *x4 use 4x4 and all other use 8x8.
          // Because these are constants via the macro system,
          // the branches here will be optimized out.
          let size: usize = $W.min($H).min(8);
          let sizei = size as isize;

          let mut sum = 0u64;

          // Loop over chunks the size of the chosen transform
          for chunk_y in (0isize..$H).step_by(size) {
            for chunk_x in (0isize..$W).step_by(size) {
              let buf = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
              let input1 = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
              let input2 = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
              for y in 0..(sizei) {
                let y1 = chunk_y + y;
                for x in 0..(sizei) {
                  let x1 = chunk_x + x;
                  input1.as_mut_ptr().offset(y * sizei + x).write(src.offset(y1 * src_stride + x1).read() as i32);
                  input2.as_mut_ptr().offset(y * sizei + x).write(dst.offset(y1 * dst_stride + x1).read() as i32);
                }
              }

              // Move the difference of the transforms to a buffer
              for y in 0..(sizei) {
                if size == 4 {
                  let row_src = _mm_load_si128(input1.as_ptr().offset(y * sizei) as *const __m128i);
                  let row_dst = _mm_load_si128(input2.as_ptr().offset(y * sizei) as *const __m128i);
                  let diff = _mm_sub_epi32(row_src, row_dst);
                  _mm_store_si128(buf.as_mut_ptr().offset(y * sizei) as *mut __m128i, diff);
                } else {
                  let row_src = _mm256_load_si256(input1.as_ptr().offset(y * sizei) as *const __m256i);
                  let row_dst = _mm256_load_si256(input2.as_ptr().offset(y * sizei) as *const __m256i);
                  let diff = _mm256_sub_epi32(row_src, row_dst);
                  _mm256_store_si256(buf.as_mut_ptr().offset(y * sizei) as *mut __m256i, diff);
                }
              }

              // Perform the hadamard transform on the differences,
              // Then sum the absolute values of the transformed differences
              if size == 4 {
                hadamard4x4(buf.as_mut_ptr());
                let row1 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(0) as *const __m256i));
                let row2 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(8) as *const __m256i));
                let sum_t = _mm256_add_epi32(row1, row2);
                let mut abs = Aligned::<[u32; 8]>::uninitialized();
                _mm256_store_si256(abs.data.as_mut_ptr() as *mut __m256i, sum_t);
                sum += abs.data.iter().copied().map(|a| a as u64).sum::<u64>();
              } else {
                hadamard8x8(buf.as_mut_ptr());
                let row1 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(0) as *const __m256i));
                let row2 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(8) as *const __m256i));
                let row3 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(16) as *const __m256i));
                let row4 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(24) as *const __m256i));
                let row5 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(32) as *const __m256i));
                let row6 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(40) as *const __m256i));
                let row7 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(48) as *const __m256i));
                let row8 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(56) as *const __m256i));
                let sum1 = _mm256_add_epi32(row1, row2);
                let sum2 = _mm256_add_epi32(row3, row4);
                let sum3 = _mm256_add_epi32(row5, row6);
                let sum4 = _mm256_add_epi32(row7, row8);
                let sum5 = _mm256_add_epi32(sum1, sum2);
                let sum6 = _mm256_add_epi32(sum3, sum4);
                let sum_t = _mm256_add_epi32(sum5, sum6);
                let mut abs_sums = Aligned::<[u32; 8]>::uninitialized();
                _mm256_store_si256(abs_sums.data.as_mut_ptr() as *mut __m256i, sum_t);
                sum += abs_sums.data.iter().copied().map(|a| a as u64).sum::<u64>();
              }
            }
          }

          // Normalize the results
          let ln = msb(size as i32) as u64;
          ((sum + (1 << ln >> 1)) >> ln) as u32
        }
      }
    )*
  }
}

satd_hbd_avx2!(
  (4, 4),
  (4, 8),
  (4, 16),
  (8, 4),
  (8, 8),
  (8, 16),
  (8, 32),
  (16, 4),
  (16, 8),
  (16, 16),
  (16, 32),
  (16, 64),
  (32, 8),
  (32, 16),
  (32, 32),
  (32, 64),
  (64, 16),
  (64, 32),
  (64, 64),
  (64, 128),
  (128, 64),
  (128, 128)
);

#[inline(always)]
const fn butterfly(a: i32, b: i32) -> (i32, i32) {
  ((a + b), (a - b))
}

#[inline(always)]
#[allow(clippy::identity_op, clippy::erasing_op)]
unsafe fn hadamard4_1d(
  data: *mut i32, n: usize, stride0: usize, stride1: usize,
) {
  for i in 0..n {
    let sub = data.add(i * stride0);
    let (a0, a1) =
      butterfly(sub.add(0 * stride1).read(), sub.add(1 * stride1).read());
    let (a2, a3) =
      butterfly(sub.add(2 * stride1).read(), sub.add(3 * stride1).read());
    let (b0, b2) = butterfly(a0, a2);
    let (b1, b3) = butterfly(a1, a3);
    sub.add(0 * stride1).write(b0);
    sub.add(1 * stride1).write(b1);
    sub.add(2 * stride1).write(b2);
    sub.add(3 * stride1).write(b3);
  }
}

#[inline(always)]
#[allow(clippy::identity_op, clippy::erasing_op)]
unsafe fn hadamard8_1d(
  data: *mut i32, n: usize, stride0: usize, stride1: usize,
) {
  for i in 0..n {
    let sub = data.add(i * stride0);

    let (a0, a1) =
      butterfly(sub.add(0 * stride1).read(), sub.add(1 * stride1).read());
    let (a2, a3) =
      butterfly(sub.add(2 * stride1).read(), sub.add(3 * stride1).read());
    let (a4, a5) =
      butterfly(sub.add(4 * stride1).read(), sub.add(5 * stride1).read());
    let (a6, a7) =
      butterfly(sub.add(6 * stride1).read(), sub.add(7 * stride1).read());

    let (b0, b2) = butterfly(a0, a2);
    let (b1, b3) = butterfly(a1, a3);
    let (b4, b6) = butterfly(a4, a6);
    let (b5, b7) = butterfly(a5, a7);

    let (c0, c4) = butterfly(b0, b4);
    let (c1, c5) = butterfly(b1, b5);
    let (c2, c6) = butterfly(b2, b6);
    let (c3, c7) = butterfly(b3, b7);

    sub.add(0 * stride1).write(c0);
    sub.add(1 * stride1).write(c1);
    sub.add(2 * stride1).write(c2);
    sub.add(3 * stride1).write(c3);
    sub.add(4 * stride1).write(c4);
    sub.add(5 * stride1).write(c5);
    sub.add(6 * stride1).write(c6);
    sub.add(7 * stride1).write(c7);
  }
}

#[inline(always)]
fn hadamard2d(data: *mut i32, (w, h): (usize, usize)) {
  /*Vertical transform.*/
  let vert_func = if h == 4 { hadamard4_1d } else { hadamard8_1d };
  unsafe {
    vert_func(data, w, 1, h);
  }
  /*Horizontal transform.*/
  let horz_func = if w == 4 { hadamard4_1d } else { hadamard8_1d };
  unsafe {
    horz_func(data, h, w, 1);
  }
}

#[inline(always)]
fn hadamard4x4(data: *mut i32) {
  hadamard2d(data, (4, 4));
}

#[inline(always)]
fn hadamard8x8(data: *mut i32) {
  hadamard2d(data, (8, 8));
}
<|endoftext|>


<commit_before>use rusqlite as rs;
use slack_api as slack;

use chrono::prelude::{Utc, DateTime};

use tokio::sync::mpsc;

use crate::bot::parser::karma::Karma;
use crate::bot::parser::reacji_to_karma;

use crate::bot::query::santizer;
use crate::bot::query::{KarmaName, ReacjiAction};

use crate::core::cache;

use crate::core::database::DbResult;
use crate::core::database::Query;
use crate::core::database::send_query;

pub async fn add_reacji<R>(
    sql_tx: &mut mpsc::Sender<Query>,
    cache: &cache::Cache<R>,
    input: &str,
    user_id: String,
    channel_id: String,
    ts: String,
    action: ReacjiAction,
)
where
    R: slack::requests::SlackWebRequestSender + std::clone::Clone
{
    match reacji_to_karma(input) {
        Some(karma) => {
            let message_id = query_reacji_message(
                sql_tx,
                channel_id.clone(),
                ts.clone()
            ).await.map_or_else(
                |e| Err(e),
                |v| v.ok_or("NONE".to_string())
            );

            let message_id = match message_id {
                Err(_) => match cache.get_message(&channel_id, &ts).await {
                    Some(cache::ConversationHistoryMessage::Message { text, user_id: Some(user_id) }) => {
                        // We have the message content, insert it into the table and
                        // get its row id
                        let santized_text = santizer(&text, &cache).await;

                        match (
                            cache.get_username(&user_id).await,
                            cache.get_user_real_name(&user_id).await,
                        ) {
                            (Some(ud), Some(rn)) => {
                                add_reacji_message(
                                    sql_tx,
                                    user_id,
                                    KarmaName::new(&ud),
                                    KarmaName::new(&rn),
                                    channel_id.clone(),
                                    ts.clone(),
                                    santized_text
                                ).await
                            },
                            e => Err(format!("ERROR: [User Event] Querying for user/name failed: {:?}", e)),
                        }
                    },

                    // This happens when there is no user_id (is none)
                    // This shouldn't happen but .... slack....
                    Some(cache::ConversationHistoryMessage::Message { user_id: None, .. }) => Ok(None),

                    e => Err(format!("ERROR: [User Event] IDK here: {:?}", e)),
                },
                Ok(mid) => Ok(Some(mid)),
            };

            match message_id {
                Err(e) => {
                    eprintln!("ERROR: [User Event] Wasn't able to get/store an reactji message, vote isn't recorded");
                    eprintln!("ERROR: [User Event] Returned error: {:?}", e);
                },
                Ok(None) => (), // These are expected error, drop
                Ok(Some(mid)) => {
                    match (
                        cache.get_username(&user_id).await,
                        cache.get_user_real_name(&user_id).await,
                    ) {
                        (Some(ud), Some(rn)) => {
                            let e = add_reacji_query(
                                sql_tx,
                                Utc::now(),
                                user_id,
                                KarmaName::new(&ud),
                                KarmaName::new(&rn),
                                action,
                                mid,
                                karma,
                            ).await;

                            if e.is_err() {
                                eprintln!("ERROR: [User Event] Query failed: {:?}", e);
                            }
                        },
                        e => eprintln!("ERROR: [User Event] Querying for user/name failed: {:?}", e),
                    }
                },
            }
        },
        None => (),
    }
}

async fn query_reacji_message(
    sql_tx: &mut mpsc::Sender<Query>,
    channel_id: String,
    message_ts: String,
) -> DbResult<Option<i64>> {
    send_query(
        sql_tx,
        Box::new(move |conn: &mut rs::Connection| {
            let channel_id: Option<i64> = {
                let mut stmt = conn.prepare("SELECT id FROM chan_metadata WHERE channel = ?")?;
                let mut rows = stmt.query(rs::params![channel_id])?;

                if let Ok(Some(row)) = rows.next() {
                    row.get(0).ok()
                } else {
                    None
                }
            };

            if let Some(channel_id) = channel_id {
                // We good let's proceed, otherwise abort since if channel id isn't here its
                // not going to be in reacji_message either
                let mut stmt = conn.prepare("SELECT id FROM reacji_message WHERE ts = ? AND chan_id = ?")?;
                let mut rows = stmt.query(rs::params![message_ts, channel_id])?;

                if let Ok(Some(row)) = rows.next() {
                    let msg_id: Option<i64> = row.get(0).ok();
                    Ok(msg_id)
                } else {
                    Ok(None)
                }
            } else {
                Ok(None)
            }
        })
    ).await
}

async fn add_reacji_message(
    sql_tx: &mut mpsc::Sender<Query>,
    user_id: String,
    username: KarmaName,
    real_name: KarmaName,
    channel_id: String,
    message_ts: String,
    message: String,
) -> DbResult<Option<i64>> {
    send_query(
        sql_tx,
        Box::new(move |conn: &mut rs::Connection| {
            let nick_id: i64 = {
                let mut stmt = conn.prepare("SELECT id FROM nick_metadata WHERE username = ?")?;
                let mut rows = stmt.query(rs::params![user_id])?;

                if let Ok(Some(row)) = rows.next() {
                    row.get(0)?
                } else {
                    let mut stmt = conn.prepare(
                        "INSERT INTO nick_metadata (cleaned_nick, full_name, username, hostmask) VALUES (?, ?, ?, ?)"
                    )?;
                    stmt.insert(rs::params![username, real_name, user_id, "SlackServer"])?
                }
            };

            let channel_id: i64 = {
                let mut stmt = conn.prepare("SELECT id FROM chan_metadata WHERE channel = ?")?;
                let mut rows = stmt.query(rs::params![channel_id])?;

                if let Ok(Some(row)) = rows.next() {
                    row.get(0)?
                } else {
                    let mut stmt = conn.prepare("INSERT INTO chan_metadata (channel) VALUES (?)")?;
                    stmt.insert(rs::params![channel_id])?
                }
            };

            // Insert the reacji_message content now
            let mut stmt = conn.prepare(
                "INSERT INTO reacji_message (ts, chan_id, nick_id, message) VALUES (?, ?, ?, ?)"
            )?;

            Ok(
                Some(stmt.insert(rs::params![message_ts, channel_id, nick_id, message])?)
            )
        })
    ).await
}

async fn add_reacji_query(
    sql_tx: &mut mpsc::Sender<Query>,
    timestamp: DateTime<Utc>,
    user_id: String,
    username: KarmaName,
    real_name: KarmaName,
    action: ReacjiAction,
    message_id: i64,
    amount: Karma,
) -> DbResult<Option<i64>> {
    send_query(
        sql_tx,
        Box::new(move |conn: &mut rs::Connection| {
            let nick_id: i64 = {
                let mut stmt = conn.prepare("SELECT id FROM nick_metadata WHERE username = ?")?;
                let mut rows = stmt.query(rs::params![user_id])?;

                if let Ok(Some(row)) = rows.next() {
                    row.get(0)?
                } else {
                    let mut stmt = conn.prepare(
                        "INSERT INTO nick_metadata (cleaned_nick, full_name, username, hostmask) VALUES (?, ?, ?, ?)"
                    )?;

                    stmt.insert(rs::params![username, real_name, user_id, "SlackServer"])?
                }
            };

            let ts = timestamp.to_rfc3339();
            let ts = ts.trim_end_matches("+00:00");

            // Insert the reacji into the database
            let mut stmt = conn.prepare(
                "INSERT INTO reacji_votes
                    (voted_at, by_whom_name, action, reacji_message_id, amount, nick_id)
                VALUES
                    (?, ?, ?, ?, ?, ?)"
            )?;

            stmt.insert(rs::params![ts, username, action, message_id, amount, nick_id])?;

            Ok(None)
        })
    ).await
}
<commit_msg>Improve reacji messages and add some basic upset<commit_after>use rusqlite as rs;
use slack_api as slack;

use chrono::prelude::{Utc, DateTime};

use tokio::sync::mpsc;

use crate::bot::parser::karma::Karma;
use crate::bot::parser::reacji_to_karma;

use crate::bot::query::santizer;
use crate::bot::query::{KarmaName, ReacjiAction};

use crate::core::cache;

use crate::core::database::DbResult;
use crate::core::database::Query;
use crate::core::database::send_query;

pub async fn add_reacji<R>(
    sql_tx: &mut mpsc::Sender<Query>,
    cache: &cache::Cache<R>,
    input: &str,
    user_id: String,
    channel_id: String,
    ts: String,
    action: ReacjiAction,
)
where
    R: slack::requests::SlackWebRequestSender + std::clone::Clone
{
    match reacji_to_karma(input) {
        Some(karma) => {
            let message_id = query_reacji_message(
                sql_tx,
                channel_id.clone(),
                ts.clone()
            ).await.map_or_else(
                |e| Err(e),
                |v| v.ok_or("NONE".to_string())
            );

            let message_id = match message_id {
                Err(_) => match cache.get_message(&channel_id, &ts).await {
                    Some(cache::ConversationHistoryMessage::Message { text, user_id: Some(user_id) }) => {
                        // We have the message content, insert it into the table and
                        // get its row id
                        let santized_text = santizer(&text, &cache).await;

                        match (
                            cache.get_username(&user_id).await,
                            cache.get_user_real_name(&user_id).await,
                        ) {
                            (Some(ud), Some(rn)) => {
                                add_reacji_message(
                                    sql_tx,
                                    user_id,
                                    KarmaName::new(&ud),
                                    KarmaName::new(&rn),
                                    channel_id.clone(),
                                    ts.clone(),
                                    santized_text
                                ).await
                            },
                            e => Err(format!("ERROR: [User Event] Querying for user/name failed: {:?}", e)),
                        }
                    },

                    // This happens when there is no user_id (is none)
                    // This shouldn't happen but .... slack....
                    Some(cache::ConversationHistoryMessage::Message { user_id: None, .. }) => Ok(None),

                    e => Err(format!("ERROR: [User Event] IDK here: {:?}", e)),
                },
                Ok(mid) => Ok(Some(mid)),
            };

            match message_id {
                Err(e) => {
                    eprintln!("ERROR: [User Event] Wasn't able to get/store an reactji message, vote isn't recorded");
                    eprintln!("ERROR: [User Event] Returned error: {:?}", e);
                },
                Ok(None) => (), // These are expected error, drop
                Ok(Some(mid)) => {
                    match (
                        cache.get_username(&user_id).await,
                        cache.get_user_real_name(&user_id).await,
                    ) {
                        (Some(ud), Some(rn)) => {
                            let e = add_reacji_query(
                                sql_tx,
                                Utc::now(),
                                user_id,
                                KarmaName::new(&ud),
                                KarmaName::new(&rn),
                                action,
                                mid,
                                karma,
                            ).await;

                            if e.is_err() {
                                eprintln!("ERROR: [User Event] Query failed: {:?}", e);
                            }
                        },
                        e => eprintln!("ERROR: [User Event] Querying for user/name failed: {:?}", e),
                    }
                },
            }
        },
        None => (),
    }
}

async fn query_reacji_message(
    sql_tx: &mut mpsc::Sender<Query>,
    channel_id: String,
    message_ts: String,
) -> DbResult<Option<i64>> {
    send_query(
        sql_tx,
        Box::new(move |conn: &mut rs::Connection| {
            let channel_id: Option<i64> = {
                let mut stmt = conn.prepare("SELECT id FROM chan_metadata WHERE channel = ?")?;
                let mut rows = stmt.query(rs::params![channel_id])?;

                if let Ok(Some(row)) = rows.next() {
                    row.get(0).ok()
                } else {
                    None
                }
            };

            if let Some(channel_id) = channel_id {
                // We good let's proceed, otherwise abort since if channel id isn't here its
                // not going to be in reacji_message either
                let mut stmt = conn.prepare("SELECT id FROM reacji_message WHERE ts = ? AND chan_id = ?")?;
                let mut rows = stmt.query(rs::params![message_ts, channel_id])?;

                if let Ok(Some(row)) = rows.next() {
                    let msg_id: Option<i64> = row.get(0).ok();
                    Ok(msg_id)
                } else {
                    Ok(None)
                }
            } else {
                Ok(None)
            }
        })
    ).await
}

async fn add_reacji_message(
    sql_tx: &mut mpsc::Sender<Query>,
    user_id: String,
    username: KarmaName,
    real_name: KarmaName,
    channel_id: String,
    message_ts: String,
    message: String,
) -> DbResult<Option<i64>> {
    send_query(
        sql_tx,
        Box::new(move |conn: &mut rs::Connection| {
            let nick_id: i64 = {
                let mut stmt = conn.prepare("SELECT id FROM nick_metadata WHERE username = ?")?;
                let mut rows = stmt.query(rs::params![user_id])?;

                if let Ok(Some(row)) = rows.next() {
                    row.get(0)?
                } else {
                    let mut stmt = conn.prepare(
                        "INSERT INTO nick_metadata (cleaned_nick, full_name, username, hostmask) VALUES (?, ?, ?, ?)"
                    )?;
                    stmt.insert(rs::params![username, real_name, user_id, "SlackServer"])?
                }
            };

            let channel_id: i64 = {
                let mut stmt = conn.prepare("SELECT id FROM chan_metadata WHERE channel = ?")?;
                let mut rows = stmt.query(rs::params![channel_id])?;

                if let Ok(Some(row)) = rows.next() {
                    row.get(0)?
                } else {
                    let mut stmt = conn.prepare("INSERT INTO chan_metadata (channel) VALUES (?)")?;
                    stmt.insert(rs::params![channel_id])?
                }
            };

            // Insert the reacji_message content now
            let reacji_message_id: i64 = {
                let mut stmt = conn.prepare("SELECT id, nick_id, message FROM reacji_message WHERE ts = ? AND chan_id = ?")?;
                let mut rows = stmt.query(rs::params![message_ts, channel_id])?;

                if let Ok(Some(row)) = rows.next() {
                    // Compare the 2 and if its not the same, warn in log, otherwise return
                    let id = row.get(0)?;
                    let sql_nick: i64 = row.get(1)?;
                    let sql_message: String = row.get(2)?;

                    // Compare
                    if sql_nick != nick_id || sql_message != message {
                        eprintln!("ERROR: [Reacji Message] duplicate channel + ts");
                        eprintln!("ERROR: [Reacji Message] \tSlack Nick: {}", nick_id);
                        eprintln!("ERROR: [Reacji Message] \tSql Nick:   {}", sql_nick);
                        eprintln!("ERROR: [Reacji Message] \tSlack Msg:  {}", message);
                        eprintln!("ERROR: [Reacji Message] \tSql Msg:    {}", sql_message);
                    }

                    // Return one anyway for now
                    id
                } else {
                    let mut stmt = conn.prepare(
                        "INSERT INTO reacji_message (ts, chan_id, nick_id, message) VALUES (?, ?, ?, ?)"
                    )?;
                    stmt.insert(rs::params![message_ts, channel_id, nick_id, message])?
                }
            };

            Ok(Some(reacji_message_id))
        })
    ).await
}

async fn add_reacji_query(
    sql_tx: &mut mpsc::Sender<Query>,
    timestamp: DateTime<Utc>,
    user_id: String,
    username: KarmaName,
    real_name: KarmaName,
    action: ReacjiAction,
    message_id: i64,
    amount: Karma,
) -> DbResult<Option<i64>> {
    send_query(
        sql_tx,
        Box::new(move |conn: &mut rs::Connection| {
            let nick_id: i64 = {
                let mut stmt = conn.prepare("SELECT id FROM nick_metadata WHERE username = ?")?;
                let mut rows = stmt.query(rs::params![user_id])?;

                if let Ok(Some(row)) = rows.next() {
                    row.get(0)?
                } else {
                    let mut stmt = conn.prepare(
                        "INSERT INTO nick_metadata (cleaned_nick, full_name, username, hostmask) VALUES (?, ?, ?, ?)"
                    )?;

                    stmt.insert(rs::params![username, real_name, user_id, "SlackServer"])?
                }
            };

            let ts = timestamp.to_rfc3339();
            let ts = ts.trim_end_matches("+00:00");

            // Insert the reacji into the database
            let mut stmt = conn.prepare(
                "INSERT INTO reacji_votes
                    (voted_at, by_whom_name, action, reacji_message_id, amount, nick_id)
                VALUES
                    (?, ?, ?, ?, ?, ?)"
            )?;

            stmt.insert(rs::params![ts, username, action, message_id, amount, nick_id])?;

            Ok(None)
        })
    ).await
}
<|endoftext|>


<commit_before>use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::{Display, Path, PathBuf};

use crate::util::errors::CargoResult;
use crate::util::Config;
use anyhow::Context as _;
use cargo_util::paths;
use sys::*;
use termcolor::Color::Cyan;

#[derive(Debug)]
pub struct FileLock {
    f: Option<File>,
    path: PathBuf,
    state: State,
}

#[derive(PartialEq, Debug)]
enum State {
    Unlocked,
    Shared,
    Exclusive,
}

impl FileLock {
    /// Returns the underlying file handle of this lock.
    pub fn file(&self) -> &File {
        self.f.as_ref().unwrap()
    }

    /// Returns the underlying path that this lock points to.
    ///
    /// Note that special care must be taken to ensure that the path is not
    /// referenced outside the lifetime of this lock.
    pub fn path(&self) -> &Path {
        assert_ne!(self.state, State::Unlocked);
        &self.path
    }

    /// Returns the parent path containing this file
    pub fn parent(&self) -> &Path {
        assert_ne!(self.state, State::Unlocked);
        self.path.parent().unwrap()
    }

    /// Removes all sibling files to this locked file.
    ///
    /// This can be useful if a directory is locked with a sentinel file but it
    /// needs to be cleared out as it may be corrupt.
    pub fn remove_siblings(&self) -> CargoResult<()> {
        let path = self.path();
        for entry in path.parent().unwrap().read_dir()? {
            let entry = entry?;
            if Some(&entry.file_name()[..]) == path.file_name() {
                continue;
            }
            let kind = entry.file_type()?;
            if kind.is_dir() {
                paths::remove_dir_all(entry.path())?;
            } else {
                paths::remove_file(entry.path())?;
            }
        }
        Ok(())
    }
}

impl Read for FileLock {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.file().read(buf)
    }
}

impl Seek for FileLock {
    fn seek(&mut self, to: SeekFrom) -> io::Result<u64> {
        self.file().seek(to)
    }
}

impl Write for FileLock {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.file().write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.file().flush()
    }
}

impl Drop for FileLock {
    fn drop(&mut self) {
        if self.state != State::Unlocked {
            if let Some(f) = self.f.take() {
                let _ = unlock(&f);
            }
        }
    }
}

/// A "filesystem" is intended to be a globally shared, hence locked, resource
/// in Cargo.
///
/// The `Path` of a filesystem cannot be learned unless it's done in a locked
/// fashion, and otherwise functions on this structure are prepared to handle
/// concurrent invocations across multiple instances of Cargo.
#[derive(Clone, Debug)]
pub struct Filesystem {
    root: PathBuf,
}

impl Filesystem {
    /// Creates a new filesystem to be rooted at the given path.
    pub fn new(path: PathBuf) -> Filesystem {
        Filesystem { root: path }
    }

    /// Like `Path::join`, creates a new filesystem rooted at this filesystem
    /// joined with the given path.
    pub fn join<T: AsRef<Path>>(&self, other: T) -> Filesystem {
        Filesystem::new(self.root.join(other))
    }

    /// Like `Path::push`, pushes a new path component onto this filesystem.
    pub fn push<T: AsRef<Path>>(&mut self, other: T) {
        self.root.push(other);
    }

    /// Consumes this filesystem and returns the underlying `PathBuf`.
    ///
    /// Note that this is a relatively dangerous operation and should be used
    /// with great caution!.
    pub fn into_path_unlocked(self) -> PathBuf {
        self.root
    }

    /// Returns the underlying `Path`.
    ///
    /// Note that this is a relatively dangerous operation and should be used
    /// with great caution!.
    pub fn as_path_unlocked(&self) -> &Path {
        &self.root
    }

    /// Creates the directory pointed to by this filesystem.
    ///
    /// Handles errors where other Cargo processes are also attempting to
    /// concurrently create this directory.
    pub fn create_dir(&self) -> CargoResult<()> {
        paths::create_dir_all(&self.root)
    }

    /// Returns an adaptor that can be used to print the path of this
    /// filesystem.
    pub fn display(&self) -> Display<'_> {
        self.root.display()
    }

    /// Opens exclusive access to a file, returning the locked version of a
    /// file.
    ///
    /// This function will create a file at `path` if it doesn't already exist
    /// (including intermediate directories), and then it will acquire an
    /// exclusive lock on `path`. If the process must block waiting for the
    /// lock, the `msg` is printed to `config`.
    ///
    /// The returned file can be accessed to look at the path and also has
    /// read/write access to the underlying file.
    pub fn open_rw<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
    where
        P: AsRef<Path>,
    {
        self.open(
            path.as_ref(),
            OpenOptions::new().read(true).write(true).create(true),
            State::Exclusive,
            config,
            msg,
        )
    }

    /// Opens shared access to a file, returning the locked version of a file.
    ///
    /// This function will fail if `path` doesn't already exist, but if it does
    /// then it will acquire a shared lock on `path`. If the process must block
    /// waiting for the lock, the `msg` is printed to `config`.
    ///
    /// The returned file can be accessed to look at the path and also has read
    /// access to the underlying file. Any writes to the file will return an
    /// error.
    pub fn open_ro<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
    where
        P: AsRef<Path>,
    {
        self.open(
            path.as_ref(),
            OpenOptions::new().read(true),
            State::Shared,
            config,
            msg,
        )
    }

    fn open(
        &self,
        path: &Path,
        opts: &OpenOptions,
        state: State,
        config: &Config,
        msg: &str,
    ) -> CargoResult<FileLock> {
        let path = self.root.join(path);

        // If we want an exclusive lock then if we fail because of NotFound it's
        // likely because an intermediate directory didn't exist, so try to
        // create the directory and then continue.
        let f = opts
            .open(&path)
            .or_else(|e| {
                if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive {
                    paths::create_dir_all(path.parent().unwrap())?;
                    Ok(opts.open(&path)?)
                } else {
                    Err(anyhow::Error::from(e))
                }
            })
            .with_context(|| format!("failed to open: {}", path.display()))?;
        match state {
            State::Exclusive => {
                acquire(config, msg, &path, &|| try_lock_exclusive(&f), &|| {
                    lock_exclusive(&f)
                })?;
            }
            State::Shared => {
                acquire(config, msg, &path, &|| try_lock_shared(&f), &|| {
                    lock_shared(&f)
                })?;
            }
            State::Unlocked => {}
        }
        Ok(FileLock {
            f: Some(f),
            path,
            state,
        })
    }
}

impl PartialEq<Path> for Filesystem {
    fn eq(&self, other: &Path) -> bool {
        self.root == other
    }
}

impl PartialEq<Filesystem> for Path {
    fn eq(&self, other: &Filesystem) -> bool {
        self == other.root
    }
}

/// Acquires a lock on a file in a "nice" manner.
///
/// Almost all long-running blocking actions in Cargo have a status message
/// associated with them as we're not sure how long they'll take. Whenever a
/// conflicted file lock happens, this is the case (we're not sure when the lock
/// will be released).
///
/// This function will acquire the lock on a `path`, printing out a nice message
/// to the console if we have to wait for it. It will first attempt to use `try`
/// to acquire a lock on the crate, and in the case of contention it will emit a
/// status message based on `msg` to `config`'s shell, and then use `block` to
/// block waiting to acquire a lock.
///
/// Returns an error if the lock could not be acquired or if any error other
/// than a contention error happens.
fn acquire(
    config: &Config,
    msg: &str,
    path: &Path,
    lock_try: &dyn Fn() -> io::Result<()>,
    lock_block: &dyn Fn() -> io::Result<()>,
) -> CargoResult<()> {
    // File locking on Unix is currently implemented via `flock`, which is known
    // to be broken on NFS. We could in theory just ignore errors that happen on
    // NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
    // forever**, even if the "non-blocking" flag is passed!
    //
    // As a result, we just skip all file locks entirely on NFS mounts. That
    // should avoid calling any `flock` functions at all, and it wouldn't work
    // there anyway.
    //
    // [1]: https://github.com/rust-lang/cargo/issues/2615
    if is_on_nfs_mount(path) {
        return Ok(());
    }

    match lock_try() {
        Ok(()) => return Ok(()),

        // In addition to ignoring NFS which is commonly not working we also
        // just ignore locking on filesystems that look like they don't
        // implement file locking.
        Err(e) if error_unsupported(&e) => return Ok(()),

        Err(e) => {
            if !error_contended(&e) {
                let e = anyhow::Error::from(e);
                let cx = format!("failed to lock file: {}", path.display());
                return Err(e.context(cx));
            }
        }
    }
    let msg = format!("waiting for file lock on {}", msg);
    config.shell().status_with_color("Blocking", &msg, Cyan)?;

    lock_block().with_context(|| format!("failed to lock file: {}", path.display()))?;
    return Ok(());

    #[cfg(all(target_os = "linux", not(target_env = "musl")))]
    fn is_on_nfs_mount(path: &Path) -> bool {
        use std::ffi::CString;
        use std::mem;
        use std::os::unix::prelude::*;

        let path = match CString::new(path.as_os_str().as_bytes()) {
            Ok(path) => path,
            Err(_) => return false,
        };

        unsafe {
            let mut buf: libc::statfs = mem::zeroed();
            let r = libc::statfs(path.as_ptr(), &mut buf);

            r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
        }
    }

    #[cfg(any(not(target_os = "linux"), target_env = "musl"))]
    fn is_on_nfs_mount(_path: &Path) -> bool {
        false
    }
}

#[cfg(unix)]
mod sys {
    use std::fs::File;
    use std::io::{Error, Result};
    use std::os::unix::io::AsRawFd;

    pub(super) fn lock_shared(file: &File) -> Result<()> {
        flock(file, libc::LOCK_SH)
    }

    pub(super) fn lock_exclusive(file: &File) -> Result<()> {
        flock(file, libc::LOCK_EX)
    }

    pub(super) fn try_lock_shared(file: &File) -> Result<()> {
        flock(file, libc::LOCK_SH | libc::LOCK_NB)
    }

    pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
        flock(file, libc::LOCK_EX | libc::LOCK_NB)
    }

    pub(super) fn unlock(file: &File) -> Result<()> {
        flock(file, libc::LOCK_UN)
    }

    pub(super) fn error_contended(err: &Error) -> bool {
        err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK)
    }

    pub(super) fn error_unsupported(err: &Error) -> bool {
        match err.raw_os_error() {
            Some(libc::ENOTSUP) => true,
            #[cfg(target_os = "linux")]
            Some(libc::ENOSYS) => true,
            _ => false,
        }
    }

    #[cfg(not(target_os = "solaris"))]
    fn flock(file: &File, flag: libc::c_int) -> Result<()> {
        let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
        if ret < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(())
        }
    }

    #[cfg(target_os = "solaris")]
    fn flock(file: &File, flag: libc::c_int) -> Result<()> {
        // Solaris lacks flock(), so simply succeed with a no-op
        Ok(())
    }
}

#[cfg(windows)]
mod sys {
    use std::fs::File;
    use std::io::{Error, Result};
    use std::mem;
    use std::os::windows::io::AsRawHandle;

    use winapi::shared::minwindef::DWORD;
    use winapi::shared::winerror::{ERROR_INVALID_FUNCTION, ERROR_LOCK_VIOLATION};
    use winapi::um::fileapi::{LockFileEx, UnlockFile};
    use winapi::um::minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY};

    pub(super) fn lock_shared(file: &File) -> Result<()> {
        lock_file(file, 0)
    }

    pub(super) fn lock_exclusive(file: &File) -> Result<()> {
        lock_file(file, LOCKFILE_EXCLUSIVE_LOCK)
    }

    pub(super) fn try_lock_shared(file: &File) -> Result<()> {
        lock_file(file, LOCKFILE_FAIL_IMMEDIATELY)
    }

    pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
        lock_file(file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY)
    }

    pub(super) fn error_contended(err: &Error) -> bool {
        err.raw_os_error()
            .map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32)
    }

    pub(super) fn error_unsupported(err: &Error) -> bool {
        err.raw_os_error()
            .map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32)
    }

    pub(super) fn unlock(file: &File) -> Result<()> {
        unsafe {
            let ret = UnlockFile(file.as_raw_handle(), 0, 0, !0, !0);
            if ret == 0 {
                Err(Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }

    fn lock_file(file: &File, flags: DWORD) -> Result<()> {
        unsafe {
            let mut overlapped = mem::zeroed();
            let ret = LockFileEx(file.as_raw_handle(), flags, 0, !0, !0, &mut overlapped);
            if ret == 0 {
                Err(Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }
}
<commit_msg>Auto merge of #10157 - luqmana:opnotsupp, r=ehuss<commit_after>use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::{Display, Path, PathBuf};

use crate::util::errors::CargoResult;
use crate::util::Config;
use anyhow::Context as _;
use cargo_util::paths;
use sys::*;
use termcolor::Color::Cyan;

#[derive(Debug)]
pub struct FileLock {
    f: Option<File>,
    path: PathBuf,
    state: State,
}

#[derive(PartialEq, Debug)]
enum State {
    Unlocked,
    Shared,
    Exclusive,
}

impl FileLock {
    /// Returns the underlying file handle of this lock.
    pub fn file(&self) -> &File {
        self.f.as_ref().unwrap()
    }

    /// Returns the underlying path that this lock points to.
    ///
    /// Note that special care must be taken to ensure that the path is not
    /// referenced outside the lifetime of this lock.
    pub fn path(&self) -> &Path {
        assert_ne!(self.state, State::Unlocked);
        &self.path
    }

    /// Returns the parent path containing this file
    pub fn parent(&self) -> &Path {
        assert_ne!(self.state, State::Unlocked);
        self.path.parent().unwrap()
    }

    /// Removes all sibling files to this locked file.
    ///
    /// This can be useful if a directory is locked with a sentinel file but it
    /// needs to be cleared out as it may be corrupt.
    pub fn remove_siblings(&self) -> CargoResult<()> {
        let path = self.path();
        for entry in path.parent().unwrap().read_dir()? {
            let entry = entry?;
            if Some(&entry.file_name()[..]) == path.file_name() {
                continue;
            }
            let kind = entry.file_type()?;
            if kind.is_dir() {
                paths::remove_dir_all(entry.path())?;
            } else {
                paths::remove_file(entry.path())?;
            }
        }
        Ok(())
    }
}

impl Read for FileLock {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.file().read(buf)
    }
}

impl Seek for FileLock {
    fn seek(&mut self, to: SeekFrom) -> io::Result<u64> {
        self.file().seek(to)
    }
}

impl Write for FileLock {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.file().write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.file().flush()
    }
}

impl Drop for FileLock {
    fn drop(&mut self) {
        if self.state != State::Unlocked {
            if let Some(f) = self.f.take() {
                let _ = unlock(&f);
            }
        }
    }
}

/// A "filesystem" is intended to be a globally shared, hence locked, resource
/// in Cargo.
///
/// The `Path` of a filesystem cannot be learned unless it's done in a locked
/// fashion, and otherwise functions on this structure are prepared to handle
/// concurrent invocations across multiple instances of Cargo.
#[derive(Clone, Debug)]
pub struct Filesystem {
    root: PathBuf,
}

impl Filesystem {
    /// Creates a new filesystem to be rooted at the given path.
    pub fn new(path: PathBuf) -> Filesystem {
        Filesystem { root: path }
    }

    /// Like `Path::join`, creates a new filesystem rooted at this filesystem
    /// joined with the given path.
    pub fn join<T: AsRef<Path>>(&self, other: T) -> Filesystem {
        Filesystem::new(self.root.join(other))
    }

    /// Like `Path::push`, pushes a new path component onto this filesystem.
    pub fn push<T: AsRef<Path>>(&mut self, other: T) {
        self.root.push(other);
    }

    /// Consumes this filesystem and returns the underlying `PathBuf`.
    ///
    /// Note that this is a relatively dangerous operation and should be used
    /// with great caution!.
    pub fn into_path_unlocked(self) -> PathBuf {
        self.root
    }

    /// Returns the underlying `Path`.
    ///
    /// Note that this is a relatively dangerous operation and should be used
    /// with great caution!.
    pub fn as_path_unlocked(&self) -> &Path {
        &self.root
    }

    /// Creates the directory pointed to by this filesystem.
    ///
    /// Handles errors where other Cargo processes are also attempting to
    /// concurrently create this directory.
    pub fn create_dir(&self) -> CargoResult<()> {
        paths::create_dir_all(&self.root)
    }

    /// Returns an adaptor that can be used to print the path of this
    /// filesystem.
    pub fn display(&self) -> Display<'_> {
        self.root.display()
    }

    /// Opens exclusive access to a file, returning the locked version of a
    /// file.
    ///
    /// This function will create a file at `path` if it doesn't already exist
    /// (including intermediate directories), and then it will acquire an
    /// exclusive lock on `path`. If the process must block waiting for the
    /// lock, the `msg` is printed to `config`.
    ///
    /// The returned file can be accessed to look at the path and also has
    /// read/write access to the underlying file.
    pub fn open_rw<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
    where
        P: AsRef<Path>,
    {
        self.open(
            path.as_ref(),
            OpenOptions::new().read(true).write(true).create(true),
            State::Exclusive,
            config,
            msg,
        )
    }

    /// Opens shared access to a file, returning the locked version of a file.
    ///
    /// This function will fail if `path` doesn't already exist, but if it does
    /// then it will acquire a shared lock on `path`. If the process must block
    /// waiting for the lock, the `msg` is printed to `config`.
    ///
    /// The returned file can be accessed to look at the path and also has read
    /// access to the underlying file. Any writes to the file will return an
    /// error.
    pub fn open_ro<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
    where
        P: AsRef<Path>,
    {
        self.open(
            path.as_ref(),
            OpenOptions::new().read(true),
            State::Shared,
            config,
            msg,
        )
    }

    fn open(
        &self,
        path: &Path,
        opts: &OpenOptions,
        state: State,
        config: &Config,
        msg: &str,
    ) -> CargoResult<FileLock> {
        let path = self.root.join(path);

        // If we want an exclusive lock then if we fail because of NotFound it's
        // likely because an intermediate directory didn't exist, so try to
        // create the directory and then continue.
        let f = opts
            .open(&path)
            .or_else(|e| {
                if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive {
                    paths::create_dir_all(path.parent().unwrap())?;
                    Ok(opts.open(&path)?)
                } else {
                    Err(anyhow::Error::from(e))
                }
            })
            .with_context(|| format!("failed to open: {}", path.display()))?;
        match state {
            State::Exclusive => {
                acquire(config, msg, &path, &|| try_lock_exclusive(&f), &|| {
                    lock_exclusive(&f)
                })?;
            }
            State::Shared => {
                acquire(config, msg, &path, &|| try_lock_shared(&f), &|| {
                    lock_shared(&f)
                })?;
            }
            State::Unlocked => {}
        }
        Ok(FileLock {
            f: Some(f),
            path,
            state,
        })
    }
}

impl PartialEq<Path> for Filesystem {
    fn eq(&self, other: &Path) -> bool {
        self.root == other
    }
}

impl PartialEq<Filesystem> for Path {
    fn eq(&self, other: &Filesystem) -> bool {
        self == other.root
    }
}

/// Acquires a lock on a file in a "nice" manner.
///
/// Almost all long-running blocking actions in Cargo have a status message
/// associated with them as we're not sure how long they'll take. Whenever a
/// conflicted file lock happens, this is the case (we're not sure when the lock
/// will be released).
///
/// This function will acquire the lock on a `path`, printing out a nice message
/// to the console if we have to wait for it. It will first attempt to use `try`
/// to acquire a lock on the crate, and in the case of contention it will emit a
/// status message based on `msg` to `config`'s shell, and then use `block` to
/// block waiting to acquire a lock.
///
/// Returns an error if the lock could not be acquired or if any error other
/// than a contention error happens.
fn acquire(
    config: &Config,
    msg: &str,
    path: &Path,
    lock_try: &dyn Fn() -> io::Result<()>,
    lock_block: &dyn Fn() -> io::Result<()>,
) -> CargoResult<()> {
    // File locking on Unix is currently implemented via `flock`, which is known
    // to be broken on NFS. We could in theory just ignore errors that happen on
    // NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
    // forever**, even if the "non-blocking" flag is passed!
    //
    // As a result, we just skip all file locks entirely on NFS mounts. That
    // should avoid calling any `flock` functions at all, and it wouldn't work
    // there anyway.
    //
    // [1]: https://github.com/rust-lang/cargo/issues/2615
    if is_on_nfs_mount(path) {
        return Ok(());
    }

    match lock_try() {
        Ok(()) => return Ok(()),

        // In addition to ignoring NFS which is commonly not working we also
        // just ignore locking on filesystems that look like they don't
        // implement file locking.
        Err(e) if error_unsupported(&e) => return Ok(()),

        Err(e) => {
            if !error_contended(&e) {
                let e = anyhow::Error::from(e);
                let cx = format!("failed to lock file: {}", path.display());
                return Err(e.context(cx));
            }
        }
    }
    let msg = format!("waiting for file lock on {}", msg);
    config.shell().status_with_color("Blocking", &msg, Cyan)?;

    lock_block().with_context(|| format!("failed to lock file: {}", path.display()))?;
    return Ok(());

    #[cfg(all(target_os = "linux", not(target_env = "musl")))]
    fn is_on_nfs_mount(path: &Path) -> bool {
        use std::ffi::CString;
        use std::mem;
        use std::os::unix::prelude::*;

        let path = match CString::new(path.as_os_str().as_bytes()) {
            Ok(path) => path,
            Err(_) => return false,
        };

        unsafe {
            let mut buf: libc::statfs = mem::zeroed();
            let r = libc::statfs(path.as_ptr(), &mut buf);

            r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
        }
    }

    #[cfg(any(not(target_os = "linux"), target_env = "musl"))]
    fn is_on_nfs_mount(_path: &Path) -> bool {
        false
    }
}

#[cfg(unix)]
mod sys {
    use std::fs::File;
    use std::io::{Error, Result};
    use std::os::unix::io::AsRawFd;

    pub(super) fn lock_shared(file: &File) -> Result<()> {
        flock(file, libc::LOCK_SH)
    }

    pub(super) fn lock_exclusive(file: &File) -> Result<()> {
        flock(file, libc::LOCK_EX)
    }

    pub(super) fn try_lock_shared(file: &File) -> Result<()> {
        flock(file, libc::LOCK_SH | libc::LOCK_NB)
    }

    pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
        flock(file, libc::LOCK_EX | libc::LOCK_NB)
    }

    pub(super) fn unlock(file: &File) -> Result<()> {
        flock(file, libc::LOCK_UN)
    }

    pub(super) fn error_contended(err: &Error) -> bool {
        err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK)
    }

    pub(super) fn error_unsupported(err: &Error) -> bool {
        match err.raw_os_error() {
            // Unfortunately, depending on the target, these may or may not be the same.
            // For targets in which they are the same, the duplicate pattern causes a warning.
            #[allow(unreachable_patterns)]
            Some(libc::ENOTSUP | libc::EOPNOTSUPP) => true,
            #[cfg(target_os = "linux")]
            Some(libc::ENOSYS) => true,
            _ => false,
        }
    }

    #[cfg(not(target_os = "solaris"))]
    fn flock(file: &File, flag: libc::c_int) -> Result<()> {
        let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
        if ret < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(())
        }
    }

    #[cfg(target_os = "solaris")]
    fn flock(file: &File, flag: libc::c_int) -> Result<()> {
        // Solaris lacks flock(), so simply succeed with a no-op
        Ok(())
    }
}

#[cfg(windows)]
mod sys {
    use std::fs::File;
    use std::io::{Error, Result};
    use std::mem;
    use std::os::windows::io::AsRawHandle;

    use winapi::shared::minwindef::DWORD;
    use winapi::shared::winerror::{ERROR_INVALID_FUNCTION, ERROR_LOCK_VIOLATION};
    use winapi::um::fileapi::{LockFileEx, UnlockFile};
    use winapi::um::minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY};

    pub(super) fn lock_shared(file: &File) -> Result<()> {
        lock_file(file, 0)
    }

    pub(super) fn lock_exclusive(file: &File) -> Result<()> {
        lock_file(file, LOCKFILE_EXCLUSIVE_LOCK)
    }

    pub(super) fn try_lock_shared(file: &File) -> Result<()> {
        lock_file(file, LOCKFILE_FAIL_IMMEDIATELY)
    }

    pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
        lock_file(file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY)
    }

    pub(super) fn error_contended(err: &Error) -> bool {
        err.raw_os_error()
            .map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32)
    }

    pub(super) fn error_unsupported(err: &Error) -> bool {
        err.raw_os_error()
            .map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32)
    }

    pub(super) fn unlock(file: &File) -> Result<()> {
        unsafe {
            let ret = UnlockFile(file.as_raw_handle(), 0, 0, !0, !0);
            if ret == 0 {
                Err(Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }

    fn lock_file(file: &File, flags: DWORD) -> Result<()> {
        unsafe {
            let mut overlapped = mem::zeroed();
            let ret = LockFileEx(file.as_raw_handle(), flags, 0, !0, !0, &mut overlapped);
            if ret == 0 {
                Err(Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::{HashMap, HashSet};

use crate::{
    conversion::{
        analysis::{depth_first::depth_first, pod::PodAnalysis, type_converter::find_types},
        api::{Api, CppVisibility, FuncToConvert, SpecialMemberKind},
    },
    known_types::known_types,
    types::QualifiedName,
};

use super::{
    implicit_constructor_rules::{
        determine_implicit_constructors, ExplicitItemsFound, ImplicitConstructorsNeeded,
    },
    FnAnalysis, FnKind, FnPrePhase, MethodKind, ReceiverMutability, TraitMethodKind,
};

#[derive(Hash, Eq, PartialEq)]
enum ExplicitKind {
    MoveConstructor,
    ConstCopyConstructor,
    NonConstCopyConstructor,
    OtherConstructor,
    Destructor,
    CopyAssignmentOperator,
    MoveAssignmentOperator,
    DeletedOrInaccessibleCopyConstructor,
    DeletedOrInaccessibleDestructor,
}

#[derive(Hash, Eq, PartialEq)]
struct ExplicitFound {
    ty: QualifiedName,
    kind: ExplicitKind,
}

/// If a type has explicit constructors, bindgen will generate corresponding
/// constructor functions, which we'll have already converted to make_unique methods.
/// For types with implicit constructors, we synthesize them here.
/// It is tempting to make this a separate analysis phase, to be run later than
/// the function analysis; but that would make the code much more complex as it
/// would need to output a `FnAnalysisBody`. By running it as part of this phase
/// we can simply generate the sort of thing bindgen generates, then ask
/// the existing code in this phase to figure out what to do with it.
pub(super) fn find_missing_constructors(
    apis: &[Api<FnPrePhase>],
) -> HashMap<QualifiedName, ImplicitConstructorsNeeded> {
    let mut all_known_types = find_types(apis);
    all_known_types.extend(known_types().all_names().cloned());
    let explicits = find_explicit_items(apis);
    let mut implicit_constructors_needed = HashMap::new();
    // Important only to ask for a depth-first analysis of structs, because
    // when all APIs are considered there may be reference loops and that would
    // panic.
    for api in depth_first(apis.iter().filter(|api| matches!(api, Api::Struct { .. }))) {
        if let Api::Struct {
            name,
            analysis: PodAnalysis {
                bases, field_types, ..
            },
            details,
            ..
        } = api
        {
            let name = &name.name;
            let find = |kind: ExplicitKind| -> bool {
                explicits.contains(&ExplicitFound {
                    ty: name.clone(),
                    kind,
                })
            };
            let any_bases_or_fields_lack_const_copy_constructors =
                bases.iter().chain(field_types.iter()).any(|qn| {
                    let has_explicit = explicits.contains(&ExplicitFound {
                        ty: qn.clone(),
                        kind: ExplicitKind::ConstCopyConstructor,
                    });
                    let has_implicit = implicit_constructors_needed
                        .get(qn)
                        .map(|imp: &ImplicitConstructorsNeeded| imp.copy_constructor_taking_const_t)
                        .unwrap_or_default();
                    !has_explicit && !has_implicit
                });
            let any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors =
                bases.iter().chain(field_types.iter()).any(|qn| {
                    explicits.contains(&ExplicitFound {
                        ty: qn.clone(),
                        kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor,
                    })
                });
            let any_bases_have_deleted_or_inaccessible_destructors = bases.iter().any(|qn| {
                explicits.contains(&ExplicitFound {
                    ty: qn.clone(),
                    kind: ExplicitKind::DeletedOrInaccessibleDestructor,
                })
            });
            // Conservatively, we will not generate implicit constructors for any struct/class
            // where we don't fully understand all field types. We need to extend our knowledge
            // to understand the constructor behavior of things in known_types.rs, then we'll
            // be able to cope with types which contain strings, unique_ptrs etc.
            let any_field_or_base_not_understood = bases
                .iter()
                .chain(field_types.iter())
                .any(|qn| !all_known_types.contains(qn));
            let explicit_items_found = ExplicitItemsFound {
                move_constructor: find(ExplicitKind::MoveConstructor),
                copy_constructor: find(ExplicitKind::ConstCopyConstructor)
                    || find(ExplicitKind::NonConstCopyConstructor)
                    || find(ExplicitKind::DeletedOrInaccessibleCopyConstructor),
                any_other_constructor: find(ExplicitKind::OtherConstructor),
                any_bases_or_fields_lack_const_copy_constructors,
                any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors,
                any_bases_have_deleted_or_inaccessible_destructors,
                destructor: find(ExplicitKind::Destructor)
                    || find(ExplicitKind::DeletedOrInaccessibleDestructor),
                copy_assignment_operator: find(ExplicitKind::CopyAssignmentOperator),
                move_assignment_operator: find(ExplicitKind::MoveAssignmentOperator),
                has_rvalue_reference_fields: details.has_rvalue_reference_fields,
                any_field_or_base_not_understood,
            };
            log::info!(
                "Explicit items found for {:?}: {:?}",
                name,
                explicit_items_found
            );
            let implicits = determine_implicit_constructors(explicit_items_found);
            implicit_constructors_needed.insert(name.clone(), implicits);
        }
    }
    log::info!(
        "Implicit constructors needed: {:?}",
        implicit_constructors_needed
    );
    implicit_constructors_needed
}

fn find_explicit_items(apis: &[Api<FnPrePhase>]) -> HashSet<ExplicitFound> {
    apis.iter()
        .filter_map(|api| match api {
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind: FnKind::Method(self_ty, MethodKind::Constructor),
                        ..
                    },
                ..
            } => Some(ExplicitFound {
                ty: self_ty.clone(),
                kind: ExplicitKind::OtherConstructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::MoveConstructor,
                                impl_for,
                                ..
                            },
                        ..
                    },
                ..
            } => Some(ExplicitFound {
                ty: impl_for.clone(),
                kind: ExplicitKind::MoveConstructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::Destructor,
                                impl_for,
                                ..
                            },
                        ..
                    },
                fun,
                ..
            } if is_deleted_or_inaccessible(fun) => Some(ExplicitFound {
                ty: impl_for.clone(),
                kind: ExplicitKind::DeletedOrInaccessibleDestructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::Destructor,
                                impl_for,
                                ..
                            },
                        ..
                    },
                ..
            } => Some(ExplicitFound {
                ty: impl_for.clone(),
                kind: ExplicitKind::Destructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::CopyConstructor,
                                impl_for,
                                ..
                            },
                        ..
                    },
                fun,
                ..
            } if is_deleted_or_inaccessible(fun) => Some(ExplicitFound {
                ty: impl_for.clone(),
                kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::CopyConstructor,
                                impl_for,
                                ..
                            },
                        param_details,
                        ..
                    },
                ..
            } => {
                let receiver_mutability = &param_details
                    .iter()
                    .next()
                    .unwrap()
                    .self_type
                    .as_ref()
                    .unwrap()
                    .1;
                let kind = match receiver_mutability {
                    ReceiverMutability::Const => ExplicitKind::ConstCopyConstructor,
                    ReceiverMutability::Mutable => ExplicitKind::NonConstCopyConstructor,
                };
                Some(ExplicitFound {
                    ty: impl_for.clone(),
                    kind,
                })
            }
            Api::Function {
                fun,
                analysis:
                    FnAnalysis {
                        kind: FnKind::Method(self_ty, ..),
                        ..
                    },
                ..
            } if matches!(
                fun.special_member,
                Some(SpecialMemberKind::AssignmentOperator)
            ) =>
            {
                let is_move_assignment_operator = !fun.references.rvalue_ref_params.is_empty();
                Some(ExplicitFound {
                    ty: self_ty.clone(),
                    kind: if is_move_assignment_operator {
                        ExplicitKind::MoveAssignmentOperator
                    } else {
                        ExplicitKind::CopyAssignmentOperator
                    },
                })
            }
            _ => None,
        })
        .chain(known_type_constructors())
        .collect()
}

fn known_type_constructors() -> impl Iterator<Item = ExplicitFound> {
    known_types()
        .all_types_with_move_constructors()
        .map(|ty| ExplicitFound {
            ty,
            kind: ExplicitKind::MoveConstructor,
        })
        .chain(
            known_types()
                .all_types_with_const_copy_constructors()
                .map(|ty| ExplicitFound {
                    ty,
                    kind: ExplicitKind::ConstCopyConstructor,
                }),
        )
        .chain(
            known_types()
                .all_types_without_copy_constructors()
                .map(|ty| ExplicitFound {
                    ty,
                    kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor,
                }),
        )
}

fn is_deleted_or_inaccessible(fun: &FuncToConvert) -> bool {
    fun.cpp_vis == CppVisibility::Private || fun.is_deleted
}
<commit_msg>Do not generate nested type constructors for now.<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::{HashMap, HashSet};

use crate::{
    conversion::{
        analysis::{depth_first::depth_first, pod::PodAnalysis, type_converter::find_types},
        api::{Api, CppVisibility, FuncToConvert, SpecialMemberKind},
    },
    known_types::known_types,
    types::QualifiedName,
};

use super::{
    implicit_constructor_rules::{
        determine_implicit_constructors, ExplicitItemsFound, ImplicitConstructorsNeeded,
    },
    FnAnalysis, FnKind, FnPrePhase, MethodKind, ReceiverMutability, TraitMethodKind,
};

#[derive(Hash, Eq, PartialEq)]
enum ExplicitKind {
    MoveConstructor,
    ConstCopyConstructor,
    NonConstCopyConstructor,
    OtherConstructor,
    Destructor,
    CopyAssignmentOperator,
    MoveAssignmentOperator,
    DeletedOrInaccessibleCopyConstructor,
    DeletedOrInaccessibleDestructor,
}

#[derive(Hash, Eq, PartialEq)]
struct ExplicitFound {
    ty: QualifiedName,
    kind: ExplicitKind,
}

/// If a type has explicit constructors, bindgen will generate corresponding
/// constructor functions, which we'll have already converted to make_unique methods.
/// For types with implicit constructors, we synthesize them here.
/// It is tempting to make this a separate analysis phase, to be run later than
/// the function analysis; but that would make the code much more complex as it
/// would need to output a `FnAnalysisBody`. By running it as part of this phase
/// we can simply generate the sort of thing bindgen generates, then ask
/// the existing code in this phase to figure out what to do with it.
pub(super) fn find_missing_constructors(
    apis: &[Api<FnPrePhase>],
) -> HashMap<QualifiedName, ImplicitConstructorsNeeded> {
    let mut all_known_types = find_types(apis);
    all_known_types.extend(known_types().all_names().cloned());
    let explicits = find_explicit_items(apis);
    let mut implicit_constructors_needed = HashMap::new();
    // Important only to ask for a depth-first analysis of structs, because
    // when all APIs are considered there may be reference loops and that would
    // panic.
    for api in depth_first(apis.iter().filter(|api| matches!(api, Api::Struct { .. }))) {
        if let Api::Struct {
            name,
            analysis: PodAnalysis {
                bases, field_types, ..
            },
            details,
            ..
        } = api
        {
            if name.cpp_name_if_present().is_some() {
                // For now we do not generate implicit constructors for nested structs - see
                // https://github.com/google/autocxx/issues/884
                continue;
            }
            let name = &name.name;
            let find = |kind: ExplicitKind| -> bool {
                explicits.contains(&ExplicitFound {
                    ty: name.clone(),
                    kind,
                })
            };
            let any_bases_or_fields_lack_const_copy_constructors =
                bases.iter().chain(field_types.iter()).any(|qn| {
                    let has_explicit = explicits.contains(&ExplicitFound {
                        ty: qn.clone(),
                        kind: ExplicitKind::ConstCopyConstructor,
                    });
                    let has_implicit = implicit_constructors_needed
                        .get(qn)
                        .map(|imp: &ImplicitConstructorsNeeded| imp.copy_constructor_taking_const_t)
                        .unwrap_or_default();
                    !has_explicit && !has_implicit
                });
            let any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors =
                bases.iter().chain(field_types.iter()).any(|qn| {
                    explicits.contains(&ExplicitFound {
                        ty: qn.clone(),
                        kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor,
                    })
                });
            let any_bases_have_deleted_or_inaccessible_destructors = bases.iter().any(|qn| {
                explicits.contains(&ExplicitFound {
                    ty: qn.clone(),
                    kind: ExplicitKind::DeletedOrInaccessibleDestructor,
                })
            });
            // Conservatively, we will not generate implicit constructors for any struct/class
            // where we don't fully understand all field types. We need to extend our knowledge
            // to understand the constructor behavior of things in known_types.rs, then we'll
            // be able to cope with types which contain strings, unique_ptrs etc.
            let any_field_or_base_not_understood = bases
                .iter()
                .chain(field_types.iter())
                .any(|qn| !all_known_types.contains(qn));
            let explicit_items_found = ExplicitItemsFound {
                move_constructor: find(ExplicitKind::MoveConstructor),
                copy_constructor: find(ExplicitKind::ConstCopyConstructor)
                    || find(ExplicitKind::NonConstCopyConstructor)
                    || find(ExplicitKind::DeletedOrInaccessibleCopyConstructor),
                any_other_constructor: find(ExplicitKind::OtherConstructor),
                any_bases_or_fields_lack_const_copy_constructors,
                any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors,
                any_bases_have_deleted_or_inaccessible_destructors,
                destructor: find(ExplicitKind::Destructor)
                    || find(ExplicitKind::DeletedOrInaccessibleDestructor),
                copy_assignment_operator: find(ExplicitKind::CopyAssignmentOperator),
                move_assignment_operator: find(ExplicitKind::MoveAssignmentOperator),
                has_rvalue_reference_fields: details.has_rvalue_reference_fields,
                any_field_or_base_not_understood,
            };
            log::info!(
                "Explicit items found for {:?}: {:?}",
                name,
                explicit_items_found
            );
            let implicits = determine_implicit_constructors(explicit_items_found);
            implicit_constructors_needed.insert(name.clone(), implicits);
        }
    }
    log::info!(
        "Implicit constructors needed: {:?}",
        implicit_constructors_needed
    );
    implicit_constructors_needed
}

fn find_explicit_items(apis: &[Api<FnPrePhase>]) -> HashSet<ExplicitFound> {
    apis.iter()
        .filter_map(|api| match api {
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind: FnKind::Method(self_ty, MethodKind::Constructor),
                        ..
                    },
                ..
            } => Some(ExplicitFound {
                ty: self_ty.clone(),
                kind: ExplicitKind::OtherConstructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::MoveConstructor,
                                impl_for,
                                ..
                            },
                        ..
                    },
                ..
            } => Some(ExplicitFound {
                ty: impl_for.clone(),
                kind: ExplicitKind::MoveConstructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::Destructor,
                                impl_for,
                                ..
                            },
                        ..
                    },
                fun,
                ..
            } if is_deleted_or_inaccessible(fun) => Some(ExplicitFound {
                ty: impl_for.clone(),
                kind: ExplicitKind::DeletedOrInaccessibleDestructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::Destructor,
                                impl_for,
                                ..
                            },
                        ..
                    },
                ..
            } => Some(ExplicitFound {
                ty: impl_for.clone(),
                kind: ExplicitKind::Destructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::CopyConstructor,
                                impl_for,
                                ..
                            },
                        ..
                    },
                fun,
                ..
            } if is_deleted_or_inaccessible(fun) => Some(ExplicitFound {
                ty: impl_for.clone(),
                kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor,
            }),
            Api::Function {
                analysis:
                    FnAnalysis {
                        kind:
                            FnKind::TraitMethod {
                                kind: TraitMethodKind::CopyConstructor,
                                impl_for,
                                ..
                            },
                        param_details,
                        ..
                    },
                ..
            } => {
                let receiver_mutability = &param_details
                    .iter()
                    .next()
                    .unwrap()
                    .self_type
                    .as_ref()
                    .unwrap()
                    .1;
                let kind = match receiver_mutability {
                    ReceiverMutability::Const => ExplicitKind::ConstCopyConstructor,
                    ReceiverMutability::Mutable => ExplicitKind::NonConstCopyConstructor,
                };
                Some(ExplicitFound {
                    ty: impl_for.clone(),
                    kind,
                })
            }
            Api::Function {
                fun,
                analysis:
                    FnAnalysis {
                        kind: FnKind::Method(self_ty, ..),
                        ..
                    },
                ..
            } if matches!(
                fun.special_member,
                Some(SpecialMemberKind::AssignmentOperator)
            ) =>
            {
                let is_move_assignment_operator = !fun.references.rvalue_ref_params.is_empty();
                Some(ExplicitFound {
                    ty: self_ty.clone(),
                    kind: if is_move_assignment_operator {
                        ExplicitKind::MoveAssignmentOperator
                    } else {
                        ExplicitKind::CopyAssignmentOperator
                    },
                })
            }
            _ => None,
        })
        .chain(known_type_constructors())
        .collect()
}

fn known_type_constructors() -> impl Iterator<Item = ExplicitFound> {
    known_types()
        .all_types_with_move_constructors()
        .map(|ty| ExplicitFound {
            ty,
            kind: ExplicitKind::MoveConstructor,
        })
        .chain(
            known_types()
                .all_types_with_const_copy_constructors()
                .map(|ty| ExplicitFound {
                    ty,
                    kind: ExplicitKind::ConstCopyConstructor,
                }),
        )
        .chain(
            known_types()
                .all_types_without_copy_constructors()
                .map(|ty| ExplicitFound {
                    ty,
                    kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor,
                }),
        )
}

fn is_deleted_or_inaccessible(fun: &FuncToConvert) -> bool {
    fun.cpp_vis == CppVisibility::Private || fun.is_deleted
}
<|endoftext|>


<commit_before>extern crate server;
extern crate bincode;
extern crate log;

use server::storage::*;
use bincode::rustc_serialize::{encode_into};
use bincode::SizeLimit;
use server::logger;
use server::parse::ast::DataSrc;

fn main() {

    logger::with_loglevel(::log::LogLevelFilter::Trace)
        .with_logfile(std::path::Path::new("log.txt"))
        .enable().unwrap();

    let ty = SqlType::Int;
    let mut v = Vec::new();
    let _ = encode_into(&ty, &mut v, SizeLimit::Infinite);

    //let db = Database::create("storage_team").unwrap();
    let db = Database::load("storage_team").unwrap();

    let mut cols = Vec::new();
    cols.push(Column {
        name: "Heiner".into(),
        sql_type: SqlType::Int,
        allow_null: false,
        description: "Heiner".to_string()
    });
    cols.push(Column {
        name: "Mathias".into(),
        sql_type: SqlType::Bool,
        allow_null: false,
        description: "Mathias".to_string()
    });
    cols.push(Column {
        name: "Dennis".into(),
        sql_type: SqlType::Char(6),
        allow_null: false,
        description: "Dennis".to_string()
    });
    cols.push(Column {
        name: "Jana".into(),
        sql_type: SqlType::VarChar(178),
        allow_null: false,
        description: "Jana".to_string()
    });

    let mut my_data: Vec<Option<DataSrc>> = Vec::new();
    my_data.push(Some(DataSrc::Int(10)));
    my_data.push(Some(DataSrc::Bool(1)));
    my_data.push(Some(DataSrc::String("fünf".to_string())));
    my_data.push(Some(DataSrc::String("i am a very long string, at least i think i am".to_string())));

    let _storage_team = db.create_table("storage_team", cols, 1).unwrap();

    let t = db.load_table("storage_team").unwrap();

    let mut engine = t.create_engine();

    engine.insert_row(&my_data);

    //let _e  = engine.create_table();
    //let t = engine.table();

    //t.delete().unwrap();
    //db.delete().unwrap();

}
<commit_msg>correct style<commit_after>extern crate server;
extern crate bincode;
extern crate log;

use server::storage::*;
use bincode::rustc_serialize::{encode_into};
use bincode::SizeLimit;
use server::logger;
use server::parse::ast::DataSrc;

fn main() {

    logger::with_loglevel(::log::LogLevelFilter::Trace)
        .with_logfile(std::path::Path::new("log.txt"))
        .enable().unwrap();

    let ty = SqlType::Int;
    let mut v = Vec::new();
    let _ = encode_into(&ty, &mut v, SizeLimit::Infinite);

    //let db = Database::create("storage_team").unwrap();
    let db = Database::load("storage_team").unwrap();

    let mut cols = Vec::new();
    cols.push(Column {
        name: "Heiner".into(),
        sql_type: SqlType::Int,
        allow_null: false,
        description: "Heiner".to_string()
    });
    cols.push(Column {
        name: "Mathias".into(),
        sql_type: SqlType::Bool,
        allow_null: false,
        description: "Mathias".to_string()
    });
    cols.push(Column {
        name: "Dennis".into(),
        sql_type: SqlType::Char(6),
        allow_null: false,
        description: "Dennis".to_string()
    });
    cols.push(Column {
        name: "Jana".into(),
        sql_type: SqlType::VarChar(178),
        allow_null: false,
        description: "Jana".to_string()
    });

    let mut my_data: Vec<Option<DataSrc>> = Vec::new();
    my_data.push(Some(DataSrc::Int(10)));
    my_data.push(Some(DataSrc::Bool(1)));
    my_data.push(Some(DataSrc::String("fünf".to_string())));
    my_data.push(
        Some(DataSrc::String("i am a very long string, at least i think i am".to_string()))
    );

    let _storage_team = db.create_table("storage_team", cols, 1).unwrap();

    let t = db.load_table("storage_team").unwrap();

    let mut engine = t.create_engine();

    engine.insert_row(&my_data);

    //let _e  = engine.create_table();
    //let t = engine.table();

    //t.delete().unwrap();
    //db.delete().unwrap();

}
<|endoftext|>


<commit_before>/// Regression test to ensure that type aliases do not cause compilation failures.
use clap::Parser;
use std::str::FromStr;

// Result type alias
#[allow(dead_code)]
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

// Wrapper to use for Option type alias
struct Wrapper<T>(T);

impl<T: FromStr> FromStr for Wrapper<T> {
    type Err = <T as FromStr>::Err;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        T::from_str(s).map(Wrapper)
    }
}

type Option<T> = std::option::Option<Wrapper<T>>;

#[derive(Parser)]
pub struct Opts {
    another_string: Option<String>,
    strings: Vec<String>,
}

#[test]
fn type_alias_regressions() {
    Opts::parse();
}
<commit_msg>test(derive): Don't parse the actual command line<commit_after>/// Regression test to ensure that type aliases do not cause compilation failures.
use clap::Parser;
use std::str::FromStr;

// Result type alias
#[allow(dead_code)]
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

// Wrapper to use for Option type alias
struct Wrapper<T>(T);

impl<T: FromStr> FromStr for Wrapper<T> {
    type Err = <T as FromStr>::Err;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        T::from_str(s).map(Wrapper)
    }
}

type Option<T> = std::option::Option<Wrapper<T>>;

#[derive(Parser)]
pub struct Opts {
    another_string: Option<String>,
    strings: Vec<String>,
}

#[test]
fn type_alias_regressions() {
    Opts::parse_from(["test"]);
}
<|endoftext|>


<commit_before><commit_msg>Remove a collision between the stack pointer register 'wsp' and the register family of w registers including sp 'ws'<commit_after><|endoftext|>