jay-compositor 1.13.0

The Jay compositor
use {
    crate::{
        sqlite::{
            SqliteError, SqliteUserId,
            sqlite_sys::{
                self, SQLITE_BLOB, SQLITE_DONE, SQLITE_INTEGER, SQLITE_PREPARE_PERSISTENT,
                SQLITE_ROW, SQLITE_STATIC, SqliteResult, sqlite3, sqlite3_stmt,
            },
        },
        utils::errorfmt::ErrorFmt,
    },
    opera::PhantomNotSync,
    sqlite_sys::Sqlite,
    std::{ptr, rc::Rc, slice},
    uapi::{IntoUstr, c::c_int},
};

pub struct SqliteDb {
    pub api: &'static Sqlite,
    pub s: *mut sqlite3,
    pub _no_sync: PhantomNotSync,
}

enum DbRef<'a> {
    Rc(#[expect(dead_code)] Rc<SqliteDb>),
    Ref(#[expect(dead_code)] &'a SqliteDb),
}

pub struct SqliteStmt<'a> {
    api: &'static Sqlite,
    _db: DbRef<'a>,
    stmt: *mut sqlite3_stmt,
}

pub struct SqliteActiveStmt<'bind, 'stmt> {
    api: &'static Sqlite,
    s: &'bind SqliteStmt<'stmt>,
    state: StatementState,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum StatementState {
    Init,
    Row,
    Done,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SqliteStep {
    Row,
    Done,
}

unsafe impl Send for SqliteDb {}

impl Drop for SqliteDb {
    fn drop(&mut self) {
        let res = unsafe { (self.api.sqlite3_close)(self.s).result() };
        if let Err(e) = res {
            log::error!("Could not close database: {}", ErrorFmt(e));
        }
    }
}

impl Drop for SqliteStmt<'_> {
    fn drop(&mut self) {
        let res = unsafe { (self.api.sqlite3_finalize)(self.stmt).result() };
        if let Err(e) = res {
            log::error!("Could not finalize statement: {}", ErrorFmt(e));
        }
    }
}

impl<'bind, 'stmt> Drop for SqliteActiveStmt<'bind, 'stmt> {
    fn drop(&mut self) {
        if let Err(e) = self.exec_() {
            log::error!("Could not step statement: {}", ErrorFmt(e));
        }
        let res = unsafe { (self.api.sqlite3_reset)(self.s.stmt).result() };
        if let Err(e) = res {
            log::error!("Could not reset statement: {}", ErrorFmt(e));
        }
        let res = unsafe { (self.api.sqlite3_clear_bindings)(self.s.stmt).result() };
        if let Err(e) = res {
            log::error!("Could not clear bindings: {}", ErrorFmt(e));
        }
    }
}

impl SqliteDb {
    pub fn exec(&self, sql: &str) -> Result<(), SqliteError> {
        self.exec_(sql).map_err(SqliteError::Exec)
    }

    pub(super) fn exec_(&self, sql: &str) -> Result<(), SqliteResult> {
        let sql = sql.into_ustr();
        unsafe {
            (self.api.sqlite3_exec)(self.s, sql.as_ptr(), None, ptr::null_mut(), ptr::null_mut())
                .result()
        }
    }

    pub fn prepare_tmp(&self, sql: &str) -> Result<SqliteStmt<'_>, SqliteError> {
        let stmt = self.prepare_raw(sql)?;
        Ok(SqliteStmt {
            api: self.api,
            _db: DbRef::Ref(self),
            stmt,
        })
    }

    pub fn prepare(self: &Rc<Self>, sql: &str) -> Result<SqliteStmt<'static>, SqliteError> {
        let stmt = self.prepare_raw(sql)?;
        Ok(SqliteStmt {
            api: self.api,
            _db: DbRef::Rc(self.clone()),
            stmt,
        })
    }

    fn prepare_raw(&self, sql: &str) -> Result<*mut sqlite3_stmt, SqliteError> {
        let n_bytes = sql
            .len()
            .try_into()
            .map_err(|_| SqliteError::StatementTooLong)?;
        let mut stmt = ptr::null_mut();
        unsafe {
            (self.api.sqlite3_prepare_v3)(
                self.s,
                sql.as_ptr().cast(),
                n_bytes,
                SQLITE_PREPARE_PERSISTENT,
                &mut stmt,
                ptr::null_mut(),
            )
            .result()
            .map_err(SqliteError::PrepareStatement)?;
        }
        if stmt.is_null() {
            return Err(SqliteError::NullStatement);
        }
        Ok(stmt)
    }
}

impl<'stmt> SqliteStmt<'stmt> {
    pub fn activate(&mut self) -> SqliteActiveStmt<'_, 'stmt> {
        SqliteActiveStmt {
            s: self,
            api: self.api,
            state: StatementState::Init,
        }
    }

    pub fn exec(&mut self) -> Result<(), SqliteError> {
        self.activate().exec()
    }
}

impl<'bind, 'stmt> SqliteActiveStmt<'bind, 'stmt> {
    pub fn bind_user_id(&self, idx: c_int, v: SqliteUserId) -> Result<(), SqliteError> {
        self.bind_i64(idx, v.0)
    }

    pub fn bind_i64(&self, idx: c_int, v: i64) -> Result<(), SqliteError> {
        if self.state != StatementState::Init {
            return Err(SqliteError::NotInit);
        }
        unsafe {
            (self.api.sqlite3_bind_int64)(self.s.stmt, idx, v)
                .result()
                .map_err(SqliteError::BindI64)?;
        }
        Ok(())
    }

    pub fn bind_blob<'data>(&self, idx: c_int, data: &'data [u8]) -> Result<(), SqliteError>
    where
        'data: 'bind,
    {
        if self.state != StatementState::Init {
            return Err(SqliteError::NotInit);
        }
        let n = data
            .len()
            .try_into()
            .map_err(|_| SqliteError::BlobTooLong)?;
        unsafe {
            (self.api.sqlite3_bind_blob)(self.s.stmt, idx, data.as_ptr().cast(), n, SQLITE_STATIC)
                .result()
                .map_err(SqliteError::BindBlob)?;
        }
        Ok(())
    }

    pub fn exec(mut self) -> Result<(), SqliteError> {
        self.exec_()
    }

    fn exec_(&mut self) -> Result<(), SqliteError> {
        while self.state != StatementState::Done {
            self.step()?;
        }
        Ok(())
    }

    pub fn step(&mut self) -> Result<SqliteStep, SqliteError> {
        if self.state == StatementState::Done {
            return Err(SqliteError::Done);
        }
        let res = unsafe { (self.api.sqlite3_step)(self.s.stmt) };
        if res.0 == SQLITE_ROW {
            self.state = StatementState::Row;
            return Ok(SqliteStep::Row);
        }
        self.state = StatementState::Done;
        if res.0 == SQLITE_DONE {
            return Ok(SqliteStep::Done);
        }
        Err(SqliteError::Step(res))
    }

    pub fn get_i64(&self, idx: c_int) -> Result<i64, SqliteError> {
        if self.state != StatementState::Row {
            return Err(SqliteError::NotRow);
        }
        let ty = unsafe { (self.api.sqlite3_column_type)(self.s.stmt, idx) };
        if ty != SQLITE_INTEGER {
            return Err(SqliteError::NotI64);
        }
        let res = unsafe { (self.api.sqlite3_column_int64)(self.s.stmt, idx) };
        Ok(res)
    }

    pub fn get_blob(&self, idx: c_int) -> Result<&[u8], SqliteError> {
        if self.state != StatementState::Row {
            return Err(SqliteError::NotRow);
        }
        let ty = unsafe { (self.api.sqlite3_column_type)(self.s.stmt, idx) };
        if ty != SQLITE_BLOB {
            return Err(SqliteError::NotBlob);
        }
        let len = unsafe { (self.api.sqlite3_column_bytes)(self.s.stmt, idx) };
        let Ok(len) = len.try_into() else {
            return Err(SqliteError::NegativeBlobSize);
        };
        let bytes = unsafe { (self.api.sqlite3_column_blob)(self.s.stmt, idx) };
        if bytes.is_null() {
            return Ok(&[]);
        }
        let slice = unsafe { slice::from_raw_parts(bytes.cast(), len) };
        Ok(slice)
    }
}