main
  1use crate::config::{Action, Rule};
  2use crate::github::{GitHubClient, Notification};
  3
  4fn truncate_str(s: &str, max: usize) -> String {
  5    if s.len() > max {
  6        format!("{}", &s[..max - 1])
  7    } else {
  8        s.to_string()
  9    }
 10}
 11
 12
 13pub struct ActionResult {
 14    pub notification_id: String,
 15    pub action: Action,
 16    pub success: bool,
 17    pub error: Option<String>,
 18    pub rule_name: String,
 19    pub repository: String,
 20    pub subject_title: String,
 21    pub subject_type: String,
 22}
 23
 24pub struct ActionExecutor<'a> {
 25    client: &'a GitHubClient,
 26    dry_run: bool,
 27}
 28
 29impl<'a> ActionExecutor<'a> {
 30    pub fn new(client: &'a GitHubClient, dry_run: bool) -> Self {
 31        Self { client, dry_run }
 32    }
 33
 34    /// Execute an action on a notification
 35    pub fn execute(
 36        &self,
 37        notification: &Notification,
 38        action: &Action,
 39        rule_name: &str,
 40    ) -> ActionResult {
 41        let mut result = ActionResult {
 42            notification_id: notification.id.clone(),
 43            action: action.clone(),
 44            success: true,
 45            error: None,
 46            rule_name: rule_name.to_string(),
 47            repository: notification.repository.full_name.clone(),
 48            subject_title: notification.subject.title.clone(),
 49            subject_type: notification.subject.subject_type.clone(),
 50        };
 51
 52        // Skip action doesn't do anything
 53        if matches!(action, Action::Skip) {
 54            return result;
 55        }
 56
 57        // In dry-run mode, just report success
 58        if self.dry_run {
 59            return result;
 60        }
 61
 62        // Execute the actual action
 63        let exec_result = match action {
 64            Action::Done => self.client.mark_thread_done(&notification.id),
 65            Action::Skip => Ok(()), // Already handled above
 66        };
 67
 68        if let Err(e) = exec_result {
 69            result.success = false;
 70            result.error = Some(e.to_string());
 71        }
 72
 73        result
 74    }
 75
 76    /// Execute actions on a batch of notifications
 77    pub fn execute_batch(
 78        &self,
 79        notifications: &[Notification],
 80        rules: &[&Rule],
 81        progress: Option<&indicatif::ProgressBar>,
 82    ) -> Vec<ActionResult> {
 83        use crate::rules::RuleMatcher;
 84
 85        let mut results = Vec::new();
 86
 87        for (i, notification) in notifications.iter().enumerate() {
 88            if let Some(pb) = progress {
 89                pb.set_position(i as u64);
 90            }
 91
 92            if let Some((rule_index, rule)) = RuleMatcher::find_matching_rule(notification, rules)
 93            {
 94                let default_name = format!("Rule {}", rule_index + 1);
 95                let rule_name = rule
 96                    .name
 97                    .as_ref()
 98                    .map(|s| s.as_str())
 99                    .unwrap_or(&default_name);
100
101                if let Some(pb) = progress {
102                    pb.set_message(format!(
103                        "{} {}",
104                        rule.action,
105                        truncate_str(&notification.repository.full_name, 30),
106                    ));
107                }
108
109                let result = self.execute(notification, &rule.action, rule_name);
110                results.push(result);
111            }
112        }
113
114        if let Some(pb) = progress {
115            pb.finish_and_clear();
116        }
117
118        results
119    }
120}