1
0
Fork 0
forked from wry/wry

config: add client-rule infrastructure

This commit is contained in:
Julian Orth 2025-05-04 18:02:17 +02:00
parent 17e715cde4
commit fd2163d658
32 changed files with 1804 additions and 27 deletions

258
toml-config/src/rules.rs Normal file
View file

@ -0,0 +1,258 @@
use {
crate::{
State,
config::{ClientMatch, ClientRule, GenericMatch},
},
ahash::{AHashMap, AHashSet},
jay_config::client::{ClientCriterion, ClientMatcher},
std::{mem::ManuallyDrop, rc::Rc},
};
impl State {
pub fn create_rules<R>(self: &Rc<Self>, rules: &[R]) -> (Vec<MatcherTemp<R>>, RuleMapper<R>)
where
R: Rule,
{
let mut names = AHashMap::new();
for (idx, rule) in rules.iter().enumerate() {
if let Some(name) = rule.name() {
names.insert(name.to_string(), idx);
}
}
let mut mapper = RuleMapper {
state: self.clone(),
names,
pending: Default::default(),
mapped: Default::default(),
};
let mut matchers = vec![];
for idx in 0..rules.len() {
if let Some(matcher) = mapper.map_rule(rules, idx) {
matchers.push(MatcherTemp(matcher));
}
}
(matchers, mapper)
}
}
pub trait Rule: Sized + 'static {
type Match;
type Matcher: Copy + 'static;
type Criterion<'a>;
const NAME_UPPER: &str;
const NAME_LOWER: &str;
fn name(&self) -> Option<&str>;
fn match_(&self) -> &Self::Match;
fn generic(m: &Self::Match) -> &GenericMatch<Self::Match>;
fn map_custom(
state: &Rc<State>,
all: &mut Vec<MatcherTemp<Self>>,
match_: &Self::Match,
) -> Option<()>;
fn create(c: Self::Criterion<'_>) -> Self::Matcher;
fn destroy(m: Self::Matcher);
fn bind(&self, state: &Rc<State>, matcher: Self::Matcher);
fn gen_matcher(m: Self::Matcher) -> Self::Criterion<'static>;
fn gen_not<'a, 'b: 'a>(m: &'a Self::Criterion<'b>) -> Self::Criterion<'a>;
fn gen_all<'a, 'b: 'a>(m: &'a [Self::Criterion<'b>]) -> Self::Criterion<'a>;
fn gen_any<'a, 'b: 'a>(m: &'a [Self::Criterion<'b>]) -> Self::Criterion<'a>;
fn gen_exactly<'a, 'b: 'a>(n: usize, m: &'a [Self::Criterion<'b>]) -> Self::Criterion<'a>;
}
impl Rule for ClientRule {
type Match = ClientMatch;
type Matcher = ClientMatcher;
type Criterion<'a> = ClientCriterion<'a>;
const NAME_UPPER: &str = "Client";
const NAME_LOWER: &str = "client";
fn name(&self) -> Option<&str> {
self.name.as_deref()
}
fn match_(&self) -> &Self::Match {
&self.match_
}
fn generic(m: &Self::Match) -> &GenericMatch<Self::Match> {
&m.generic
}
fn map_custom(
_state: &Rc<State>,
_all: &mut Vec<MatcherTemp<Self>>,
_match_: &Self::Match,
) -> Option<()> {
Some(())
}
fn create(c: Self::Criterion<'_>) -> Self::Matcher {
c.to_matcher()
}
fn destroy(m: Self::Matcher) {
m.destroy();
}
fn bind(&self, state: &Rc<State>, matcher: Self::Matcher) {
let state = state.clone();
macro_rules! latch {
($g:ident, $client:ident) => {
let g = $g.clone();
let state = state.clone();
$client.latch(move || {
state.with_client($client.client(), true, || g());
});
};
}
if let Some(action) = &self.action {
let f = action.clone().into_fn(&state);
if let Some(action) = &self.latch {
let g = action.clone().into_rc_fn(&state);
let state = state.clone();
matcher.bind(move |client| {
state.with_client(client.client(), false, &f);
latch!(g, client);
});
} else {
matcher.bind(move |client| {
state.with_client(client.client(), false, &f);
});
}
} else {
if let Some(action) = &self.latch {
let g = action.clone().into_rc_fn(&state);
matcher.bind(move |client| {
latch!(g, client);
});
}
}
}
fn gen_matcher(m: Self::Matcher) -> Self::Criterion<'static> {
ClientCriterion::Matcher(m)
}
fn gen_not<'a, 'b: 'a>(m: &'a Self::Criterion<'b>) -> Self::Criterion<'a> {
ClientCriterion::Not(m)
}
fn gen_all<'a, 'b: 'a>(m: &'a [Self::Criterion<'b>]) -> Self::Criterion<'a> {
ClientCriterion::All(m)
}
fn gen_any<'a, 'b: 'a>(m: &'a [Self::Criterion<'b>]) -> Self::Criterion<'a> {
ClientCriterion::Any(m)
}
fn gen_exactly<'a, 'b: 'a>(n: usize, m: &'a [Self::Criterion<'b>]) -> Self::Criterion<'a> {
ClientCriterion::Exactly(n, m)
}
}
pub struct RuleMapper<R>
where
R: Rule,
{
state: Rc<State>,
names: AHashMap<String, usize>,
pending: AHashSet<usize>,
mapped: AHashMap<usize, R::Matcher>,
}
pub struct MatcherTemp<R>(R::Matcher)
where
R: Rule;
impl<R> Drop for MatcherTemp<R>
where
R: Rule,
{
fn drop(&mut self) {
R::destroy(self.0);
}
}
impl<R> RuleMapper<R>
where
R: Rule,
{
fn map_rule(&mut self, rules: &[R], idx: usize) -> Option<R::Matcher> {
if let Some(matcher) = self.mapped.get(&idx) {
return Some(*matcher);
}
if !self.pending.insert(idx) {
if let Some(name) = rules.get(idx).and_then(|r| r.name()) {
log::error!("{} rule `{name}` has a loop", R::NAME_UPPER);
}
return None;
}
let rule = &rules[idx];
let matcher = self.map_match(rules, rule.match_())?;
self.mapped.insert(idx, matcher);
rule.bind(&self.state, matcher);
Some(matcher)
}
fn map_temporary_match(&mut self, rules: &[R], matcher: &R::Match) -> Option<MatcherTemp<R>> {
self.map_match(rules, matcher).map(MatcherTemp)
}
fn map_match(&mut self, rules: &[R], matcher: &R::Match) -> Option<R::Matcher> {
let mut all = vec![];
self.map_generic_match(rules, &mut all, R::generic(matcher))?;
R::map_custom(&self.state, &mut all, matcher)?;
if all.len() == 1 {
return Some(ManuallyDrop::new(all.pop().unwrap()).0);
}
let all: Vec<_> = all.iter().map(|m| R::gen_matcher(m.0)).collect();
Some(R::create(R::gen_all(&all)))
}
fn map_generic_match(
&mut self,
rules: &[R],
all: &mut Vec<MatcherTemp<R>>,
matcher: &GenericMatch<R::Match>,
) -> Option<()> {
let m = |c: R::Criterion<'_>| MatcherTemp(R::create(c));
if let Some(name) = &matcher.name {
let Some(&idx) = self.names.get(&**name) else {
log::error!("There is no {} rule named `{name}`", R::NAME_LOWER);
return None;
};
let matcher = self.map_rule(rules, idx)?;
all.push(m(R::gen_matcher(matcher)));
}
if let Some(not) = &matcher.not {
let matcher = self.map_temporary_match(rules, not)?;
all.push(m(R::gen_not(&R::gen_matcher(matcher.0))));
}
if let Some(list) = &matcher.all {
for match_ in list {
all.push(self.map_temporary_match(rules, match_)?);
}
}
if let Some(list) = &matcher.any {
let mut any = vec![];
for match_ in list {
any.push(self.map_temporary_match(rules, match_)?);
}
let any: Vec<_> = any.iter().map(|m| R::gen_matcher(m.0)).collect();
all.push(m(R::gen_any(&any)));
}
if let Some(exactly) = &matcher.exactly {
let mut list = vec![];
for match_ in &exactly.list {
list.push(self.map_temporary_match(rules, match_)?);
}
let list: Vec<_> = list.iter().map(|m| R::gen_matcher(m.0)).collect();
all.push(m(R::gen_exactly(exactly.num, &list)))
}
Some(())
}
}