codealong_github/
pull_request_analyzer.rs

1use git2::{Oid, Repository};
2use slog::Logger;
3
4use codealong::{with_authentication, DiffAnalyzer, RepoConfig};
5
6use crate::analyzed_pull_request::AnalyzedPullRequest;
7use crate::error::{Error, Result};
8use crate::pull_request::{PullRequest, Ref};
9
10pub struct PullRequestAnalyzer<'a> {
11    repo: &'a Repository,
12    config: &'a RepoConfig,
13    pr: PullRequest,
14    logger: Logger,
15}
16
17impl<'a> PullRequestAnalyzer<'a> {
18    pub fn new(
19        repo: &'a Repository,
20        pr: PullRequest,
21        config: &'a RepoConfig,
22        parent_logger: &Logger,
23    ) -> PullRequestAnalyzer<'a> {
24        let logger = parent_logger.new(o!("pull_request_id" => pr.id));
25        PullRequestAnalyzer {
26            repo,
27            pr,
28            config,
29            logger,
30        }
31    }
32
33    pub fn analyze(self) -> Result<AnalyzedPullRequest> {
34        debug!(self.logger, "Analyzing pull_request"; "updated_at" => &self.pr.updated_at.to_rfc2822(), "user" => &self.pr.user.login, "title" => &self.pr.title);
35        self.fetch_remote(&self.pr.base)?;
36        self.fetch_remote(&self.pr.head)?;
37
38        let diff = self
39            .repo
40            .find_commit(Oid::from_str(&self.pr.base.sha)?)
41            .map_err::<Error, _>(|e| e.into())
42            .and_then(|parent| {
43                self.repo
44                    .find_commit(Oid::from_str(&self.pr.head.sha)?)
45                    .map_err::<Error, _>(|e| e.into())
46                    .and_then(|commit| {
47                        Ok(DiffAnalyzer::new(
48                            &self.repo,
49                            &commit,
50                            Some(&parent),
51                            &self.config.config,
52                        )
53                        .analyze()?)
54                    })
55            })
56            .ok();
57
58        let normalized_author = self
59            .config
60            .config
61            .person_for_github_login(&self.pr.user.login);
62        debug!(self.logger, "Done analyzing");
63        Ok(AnalyzedPullRequest::new(self.pr, diff, normalized_author))
64    }
65
66    fn fetch_remote(&self, reference: &Ref) -> Result<()> {
67        if let Some(ref repo) = reference.repo {
68            let git_config = git2::Config::open_default()?;
69            let url = &repo.ssh_url;
70            with_authentication(url, &git_config, |f| {
71                let mut rcb = git2::RemoteCallbacks::new();
72                rcb.credentials(f);
73                let mut fo = git2::FetchOptions::new();
74                fo.remote_callbacks(rcb);
75
76                Ok(self.repo.remote_anonymous(url).and_then(|mut base| {
77                    base.fetch(&[&reference.reference], Some(&mut fo), None)
78                })?)
79            })?;
80        }
81        Ok(())
82    }
83}