jay-compositor 1.15.0

The Jay compositor
use crate::client::Client;
use crate::criteria::CritLiteralOrRegex;
use crate::criteria::CritMgrExt;
use crate::criteria::CritTarget;
use crate::criteria::CritUpstreamNode;
use crate::leaks::Tracker;
use crate::object::Version;
use crate::wire::JayGenericMatchBuilderId;
use crate::wire::jay_generic_match_builder::*;
use jay_proc::Object;
use regex::Regex;
use std::cell::RefCell;
use std::rc::Rc;
use thiserror::Error;

pub struct MatchBuilder<T>
where
    T: CritTarget,
{
    pub mgr: Rc<T::Mgr>,
    stack: RefCell<Vec<Vec<Rc<dyn CritUpstreamNode<T>>>>>,
}

pub trait MatchBuilderDyn: 'static {
    fn not(&self) -> Result<(), JayGenericMatchBuilderError>;
    fn nest(&self);
    fn list(&self, all: bool);
    fn exactly(&self, n: usize);
}

impl<T> MatchBuilder<T>
where
    T: CritTarget,
{
    pub fn new(mgr: &Rc<T::Mgr>) -> Self {
        Self {
            mgr: mgr.clone(),
            stack: Default::default(),
        }
    }

    pub fn build(&self) -> Result<Rc<dyn CritUpstreamNode<T>>, JayGenericMatchBuilderError> {
        let stack = &mut *self.stack.borrow_mut();
        let [crits] = &**stack else {
            return Err(JayGenericMatchBuilderError::NotUnique);
        };
        let [crit] = &**crits else {
            return Err(JayGenericMatchBuilderError::NotUnique);
        };
        Ok(crit.clone())
    }

    pub fn push(&self, crit: Rc<dyn CritUpstreamNode<T>>) {
        let stack = &mut *self.stack.borrow_mut();
        let mut last = stack.pop().unwrap_or_default();
        last.push(crit);
        stack.push(last);
    }

    pub fn push_str(
        &self,
        s: &str,
        regex: bool,
        build: impl FnOnce(CritLiteralOrRegex) -> Rc<dyn CritUpstreamNode<T>>,
    ) -> Result<(), JayGenericMatchBuilderError> {
        let v = match regex {
            false => CritLiteralOrRegex::Literal(s.to_string()),
            true => CritLiteralOrRegex::Regex(
                Regex::new(s).map_err(JayGenericMatchBuilderError::InvalidRegex)?,
            ),
        };
        self.push(build(v));
        Ok(())
    }
}

impl<T> MatchBuilderDyn for MatchBuilder<T>
where
    T: CritTarget,
{
    fn not(&self) -> Result<(), JayGenericMatchBuilderError> {
        let stack = &mut *self.stack.borrow_mut();
        if let Some(last) = stack.last_mut()
            && let Some(last) = last.last_mut()
        {
            *last = last.not(&self.mgr);
            Ok(())
        } else {
            Err(JayGenericMatchBuilderError::NoPreviousMatch)
        }
    }

    fn nest(&self) {
        let stack = &mut *self.stack.borrow_mut();
        stack.push(vec![]);
    }

    fn list(&self, all: bool) {
        let stack = &mut *self.stack.borrow_mut();
        let vec = stack.pop().unwrap_or_default();
        let mut last = stack.pop().unwrap_or_default();
        last.push(self.mgr.list(&vec, all));
        stack.push(last);
    }

    fn exactly(&self, n: usize) {
        let stack = &mut *self.stack.borrow_mut();
        let vec = stack.pop().unwrap_or_default();
        let mut last = stack.pop().unwrap_or_default();
        last.push(self.mgr.exactly(&vec, n));
        stack.push(last);
    }
}

#[derive(Object)]
pub struct JayGenericMatchBuilder {
    id: JayGenericMatchBuilderId,
    client: Rc<Client>,
    tracker: Tracker<Self>,
    version: Version,
    builder: Rc<dyn MatchBuilderDyn>,
}

impl JayGenericMatchBuilder {
    pub fn create(
        id: JayGenericMatchBuilderId,
        client: &Rc<Client>,
        version: Version,
        builder: &Rc<impl MatchBuilderDyn>,
    ) {
        let slf = Rc::new(Self {
            id,
            client: client.clone(),
            tracker: Default::default(),
            version,
            builder: builder.clone(),
        });
        track!(client, slf);
        client.add_client_obj(&slf);
    }
}

impl JayGenericMatchBuilderRequestHandler for JayGenericMatchBuilder {
    type Error = JayGenericMatchBuilderError;

    fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.client.remove_obj(self);
        Ok(())
    }

    fn not(&self, _req: Not, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.builder.not()
    }

    fn nest(&self, _req: Nest, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.builder.nest();
        Ok(())
    }

    fn list(&self, req: List, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.builder.list(req.all);
        Ok(())
    }

    fn exactly(&self, req: Exactly, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.builder.exactly(req.n);
        Ok(())
    }
}

#[derive(Debug, Error)]
pub enum JayGenericMatchBuilderError {
    #[error("The stack is empty")]
    NoPreviousMatch,
    #[error("The builder does not contain exactly one element")]
    NotUnique,
    #[error("The regex is invalid")]
    InvalidRegex(#[source] regex::Error),
}