jay-compositor 1.13.0

The Jay compositor
use {
    crate::{
        it::{
            test_error::TestError, test_ifs::test_toplevel_session::TestToplevelSession,
            test_object::TestObject, test_transport::TestTransport,
            test_utils::test_window::TestWindow, testrun::ParseFull,
        },
        utils::buffd::MsgParser,
        wire::{XdgSessionV1Id, XdgToplevelId, xdg_session_v1::*},
    },
    std::{
        cell::Cell,
        future::poll_fn,
        rc::Rc,
        task::{Poll, Waker},
    },
};

pub struct TestSession {
    pub id: XdgSessionV1Id,
    pub tran: Rc<TestTransport>,
    pub destroyed: Cell<bool>,
    pub result: Cell<Option<TestSessionResult>>,
    pub result_waiter: Cell<Option<Waker>>,
    pub replaced: Cell<bool>,
}

pub enum TestSessionResult {
    Created(String),
    Restored,
}

impl TestSession {
    pub fn destroy(&self) -> Result<(), TestError> {
        if !self.destroyed.replace(true) {
            self.tran.send(Destroy { self_id: self.id })?;
        }
        Ok(())
    }

    #[expect(dead_code)]
    pub fn remove(&self) -> Result<(), TestError> {
        if !self.destroyed.replace(true) {
            self.tran.send(Remove { self_id: self.id })?;
        }
        Ok(())
    }

    pub fn add_toplevel(
        &self,
        win: &TestWindow,
        name: &str,
    ) -> Result<Rc<TestToplevelSession>, TestError> {
        self.add_toplevel2(win.tl.server.id, name)
    }

    pub fn add_toplevel2(
        &self,
        toplevel: XdgToplevelId,
        name: &str,
    ) -> Result<Rc<TestToplevelSession>, TestError> {
        let id = self.tran.id();
        self.tran.send(AddToplevel {
            self_id: self.id,
            id,
            toplevel,
            name,
        })?;
        let ts = Rc::new(TestToplevelSession {
            id,
            tran: self.tran.clone(),
            destroyed: Cell::new(false),
            restored: Cell::new(false),
        });
        self.tran.add_obj(ts.clone())?;
        Ok(ts)
    }

    pub fn restore_toplevel(
        &self,
        win: &TestWindow,
        name: &str,
    ) -> Result<Rc<TestToplevelSession>, TestError> {
        self.restore_toplevel2(win.tl.server.id, name)
    }

    pub fn restore_toplevel2(
        &self,
        toplevel: XdgToplevelId,
        name: &str,
    ) -> Result<Rc<TestToplevelSession>, TestError> {
        let id = self.tran.id();
        self.tran.send(RestoreToplevel {
            self_id: self.id,
            id,
            toplevel,
            name,
        })?;
        let ts = Rc::new(TestToplevelSession {
            id,
            tran: self.tran.clone(),
            destroyed: Cell::new(false),
            restored: Cell::new(false),
        });
        self.tran.add_obj(ts.clone())?;
        Ok(ts)
    }

    #[expect(dead_code)]
    pub fn remove_toplevel(&self, name: &str) -> Result<(), TestError> {
        self.tran.send(RemoveToplevel {
            self_id: self.id,
            name,
        })?;
        Ok(())
    }

    pub async fn result_created(&self) -> Result<String, TestError> {
        let res = self.result().await;
        match res {
            TestSessionResult::Created(id) => Ok(id),
            TestSessionResult::Restored => bail!("Session was restored instead of created"),
        }
    }

    #[expect(dead_code)]
    pub async fn result_restored(&self) -> Result<(), TestError> {
        let res = self.result().await;
        match res {
            TestSessionResult::Created(id) => {
                bail!("Session was created ({id}) instead of restored")
            }
            TestSessionResult::Restored => Ok(()),
        }
    }

    pub async fn result(&self) -> TestSessionResult {
        poll_fn(|ctx| {
            if let Some(res) = self.result.take() {
                return Poll::Ready(res);
            }
            self.result_waiter.set(Some(ctx.waker().clone()));
            Poll::Pending
        })
        .await
    }

    fn set_result(&self, result: TestSessionResult) {
        self.result.set(Some(result));
        if let Some(waker) = self.result_waiter.take() {
            waker.wake();
        }
    }

    fn handle_created(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
        let ev = Created::parse_full(parser)?;
        self.set_result(TestSessionResult::Created(ev.session_id.to_string()));
        Ok(())
    }

    fn handle_restored(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
        let _ev = Restored::parse_full(parser)?;
        self.set_result(TestSessionResult::Restored);
        Ok(())
    }

    fn handle_replaced(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
        let _ev = Replaced::parse_full(parser)?;
        self.replaced.set(true);
        Ok(())
    }
}

test_object! {
    TestSession, XdgSessionV1;

    CREATED => handle_created,
    RESTORED => handle_restored,
    REPLACED => handle_replaced,
}

impl TestObject for TestSession {}

impl Drop for TestSession {
    fn drop(&mut self) {
        let _ = self.destroy();
    }
}