1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//
use actix::prelude::*;

use derive_more::Deref as _;
use tracing::Instrument as _;

use crate::messages::MessageToken;
use git_next_core::git;

impl Handler<crate::messages::ValidateRepo> for crate::RepoActor {
    type Result = ();
    #[tracing::instrument(name = "RepoActor::ValidateRepo", skip_all, fields(repo = %self.repo_details, token = %msg.deref()))]
    fn handle(
        &mut self,
        msg: crate::messages::ValidateRepo,
        ctx: &mut Self::Context,
    ) -> Self::Result {
        crate::logger(self.log.as_ref(), "start: ValidateRepo");

        // Message Token - make sure we are only triggered for the latest/current token
        match self.token_status(msg.unwrap()) {
            TokenStatus::Current => {} // do nothing
            TokenStatus::Expired => {
                crate::logger(
                    self.log.as_ref(),
                    format!("discarded: old message token: {}", self.message_token),
                );
                return; // message is expired
            }
            TokenStatus::New(message_token) => {
                self.message_token = message_token;
                crate::logger(
                    self.log.as_ref(),
                    format!("new message token: {}", self.message_token),
                );
            }
        }
        crate::logger(
            self.log.as_ref(),
            format!("accepted token: {}", self.message_token),
        );

        // Repository positions
        let Some(ref open_repository) = self.open_repository else {
            crate::logger(self.log.as_ref(), "no open repository");
            return;
        };
        crate::logger(self.log.as_ref(), "have open repository");
        let Some(repo_config) = self.repo_details.repo_config.clone() else {
            crate::logger(self.log.as_ref(), "no repo config");
            return;
        };
        crate::logger(self.log.as_ref(), "have repo config");

        match git::validation::positions::validate_positions(
            &**open_repository,
            &self.repo_details,
            repo_config,
        ) {
            Ok(git::validation::positions::Positions {
                main,
                next,
                dev,
                dev_commit_history,
            }) => {
                tracing::debug!(%main, %next, %dev, "positions");
                if next != main {
                    crate::do_send(
                        ctx.address(),
                        crate::messages::CheckCIStatus::new(next),
                        self.log.as_ref(),
                    );
                } else if next != dev {
                    crate::do_send(
                        ctx.address(),
                        crate::messages::AdvanceNext::new((next, dev_commit_history)),
                        self.log.as_ref(),
                    )
                } else {
                    // do nothing
                }
            }
            Err(git::validation::positions::Error::Retryable(message)) => {
                crate::logger(self.log.as_ref(), message);
                let addr = ctx.address();
                let message_token = self.message_token;
                let sleep_duration = self.sleep_duration;
                let log = self.log.clone();
                async move {
                    tracing::debug!("sleeping before retrying...");
                    crate::logger(log.as_ref(), "before sleep");
                    tokio::time::sleep(sleep_duration).await;
                    crate::logger(log.as_ref(), "after sleep");
                    crate::do_send(
                        addr,
                        crate::messages::ValidateRepo::new(message_token),
                        log.as_ref(),
                    );
                }
                .in_current_span()
                .into_actor(self)
                .wait(ctx);
            }
            Err(git::validation::positions::Error::UserIntervention(user_notification)) => {
                crate::notify_user(
                    self.notify_user_recipient.as_ref(),
                    user_notification,
                    self.log.as_ref(),
                )
            }
            Err(git::validation::positions::Error::NonRetryable(message)) => {
                crate::logger(self.log.as_ref(), message);
            }
        }
    }
}

enum TokenStatus {
    Current,
    Expired,
    New(MessageToken),
}
impl crate::RepoActor {
    fn token_status(&self, new: MessageToken) -> TokenStatus {
        let current = &self.message_token;
        if &new > current {
            return TokenStatus::New(new);
        }
        if current > &new {
            return TokenStatus::Expired;
        }
        TokenStatus::Current
    }
}