Re: Sashiko email policy precedence
"Danilo Krummrich" <[email protected]> Mon, 01 Jun 2026 11:46:54 +0200
| Newsgroups | dev.linux.lists.sashiko |
|---|---|
| Message-ID | <[email protected]> |
On Mon Jun 1, 2026 at 8:16 AM CEST, Miguel Ojeda wrote: > On Mon, Jun 1, 2026 at 1:17=E2=80=AFAM Danilo Krummrich <[email protected]>= wrote: > IIUC, from what Danilo says, the current algorithm's conservative approac= h > would remove that setting as soon as the patch is sent to any list where = that > isn't the case, right? Only if the other list has a sashiko email policy entry in the first place, otherwise it is not part of the matching loop and no further logic is appli= ed. I'd assume that this is intentional, and can be read as "no entry, no expre= ssed preference". > if it is too complex to implement other logic, I guess it is an option. Flagging something as wide list seems rather simple [1], the tristate [2] w= ould work as well, but seems a bit more complicated and potentially requires adjusting the existing policies. (Test adjustments stripped from [2]; code is LLM generated.) [1] diff --git a/src/email_policy.rs b/src/email_policy.rs index 9fe1179330f3..1ae892e67535 100644 --- a/src/email_policy.rs +++ b/src/email_policy.rs @@ -43,6 +43,10 @@ pub struct SubsystemPolicy { pub embargo_hours: Option<u32>, #[serde(default)] pub send_positive_review: bool, + /// When true and co-matched with a non-wide list, only `cc` is + /// contributed. All boolean flags are deferred to the non-wide lists. + #[serde(default)] + pub wide_list: bool, } impl EmailPolicyConfig { diff --git a/src/email_router.rs b/src/email_router.rs index c17273aad8ff..216a85d7738d 100644 --- a/src/email_router.rs +++ b/src/email_router.rs @@ -93,7 +93,18 @@ impl EmailRouter { let mut send_positive_review =3D false; let mut cc =3D Vec::new(); - for p in active_policies { + let has_non_wide =3D active_policies.iter().any(|p| !p.wide_list); + + for p in &active_policies { + let deferred =3D p.wide_list && has_non_wide; + + if deferred { + for cr in &p.cc { + cc.push(cr.clone()); + } + continue; + } + if p.mute_all { mute_all =3D true; } [2] diff --git a/src/email_policy.rs b/src/email_policy.rs index 9fe1179330f3..5c9d94a4d416 100644 --- a/src/email_policy.rs +++ b/src/email_policy.rs @@ -23,14 +23,15 @@ pub struct EmailPolicyConfig { pub struct SubsystemPolicy { #[serde(default)] pub lists: Vec<String>, + /// None =3D no preference, Some(false) =3D private, Some(true) =3D pu= blic. #[serde(default)] - pub reply_all: bool, + pub reply_all: Option<bool>, #[serde(default)] - pub reply_to_author: bool, + pub reply_to_author: Option<bool>, #[serde(default)] - pub cc_individuals: bool, + pub cc_individuals: Option<bool>, #[serde(default)] - pub mute_all: bool, + pub mute_all: Option<bool>, #[serde(default)] pub cc: Vec<String>, #[serde(default)] @@ -42,7 +43,7 @@ pub struct SubsystemPolicy { #[serde(default)] pub embargo_hours: Option<u32>, #[serde(default)] - pub send_positive_review: bool, + pub send_positive_review: Option<bool>, } =20 impl EmailPolicyConfig { diff --git a/src/email_router.rs b/src/email_router.rs index c17273aad8ff..452c09411552 100644 --- a/src/email_router.rs +++ b/src/email_router.rs @@ -12,6 +12,19 @@ pub enum Action { =20 pub struct EmailRouter {} =20 +/// Merges two Option<bool> policy votes. `dominant` is the value that win= s on +/// conflict: `true` for mute_all, reply_to_author, cc_individuals, and +/// send_positive_review (any-true wins); `false` for reply_all (any-false= wins). +fn merge_flag(acc: Option<bool>, vote: Option<bool>, dominant: bool) -> Op= tion<bool> { + match (acc, vote) { + (_, None) =3D> acc, + (None, _) =3D> vote, + (Some(v), _) if v =3D=3D dominant =3D> acc, + (_, Some(v)) if v =3D=3D dominant =3D> vote, + _ =3D> acc, + } +} + impl EmailRouter { pub fn resolve_patchwork( policy: &EmailPolicyConfig, @@ -86,34 +99,30 @@ impl EmailRouter { active_policies.push(&policy.defaults); } =20 - let mut mute_all =3D false; - let mut is_private =3D false; - let mut reply_to_author =3D false; - let mut cc_individuals =3D false; - let mut send_positive_review =3D false; + let mut mute_all =3D None; + let mut reply_all =3D None; + let mut reply_to_author =3D None; + let mut cc_individuals =3D None; + let mut send_positive_review =3D None; let mut cc =3D Vec::new(); =20 for p in active_policies { - if p.mute_all { - mute_all =3D true; - } - if !p.reply_all { - is_private =3D true; - } - if p.reply_to_author { - reply_to_author =3D true; - } - if p.cc_individuals { - cc_individuals =3D true; - } - if p.send_positive_review { - send_positive_review =3D true; - } + mute_all =3D merge_flag(mute_all, p.mute_all, true); + reply_all =3D merge_flag(reply_all, p.reply_all, false); + reply_to_author =3D merge_flag(reply_to_author, p.reply_to_aut= hor, true); + cc_individuals =3D merge_flag(cc_individuals, p.cc_individuals= , true); + send_positive_review =3D merge_flag(send_positive_review, p.se= nd_positive_review, true); for cr in &p.cc { cc.push(cr.clone()); } } =20 + let mute_all =3D mute_all.unwrap_or(false); + let is_private =3D !reply_all.unwrap_or(false); + let reply_to_author =3D reply_to_author.unwrap_or(false); + let cc_individuals =3D cc_individuals.unwrap_or(false); + let send_positive_review =3D send_positive_review.unwrap_or(false)= ; + // Always append defaults.cc so users can define a global CC for cr in &policy.defaults.cc { cc.push(cr.clone());