git_checks_core/
utils.rs

1// Copyright Kitware, Inc.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::borrow::Cow;
10
11use git_workarea::GitContext;
12use log::error;
13
14use crate::context::CheckGitContext;
15
16/// A git context for a submodule for use within checks.
17///
18/// Checks which need to inspect submodules should use this to obtain a `GitContext` for the
19/// submodule.
20#[derive(Debug)]
21pub struct SubmoduleContext<'a> {
22    /// The name of the submodule (usually the same as `path`).
23    pub name: Cow<'a, str>,
24    /// The path of the submodule within the repository.
25    pub path: Cow<'a, str>,
26    /// The clone URL for the submodule.
27    pub url: Cow<'a, str>,
28    /// The branch the submodule tracks.
29    pub branch: Cow<'a, str>,
30
31    /// The context to use to query the submodule.
32    pub context: GitContext,
33}
34
35impl<'a> SubmoduleContext<'a> {
36    /// Create submodule context for the given path.
37    ///
38    /// Returns `None` if the requisite information is not available within the context being used.
39    pub fn new(ctx: &'a CheckGitContext, diff_path: &str) -> Option<Self> {
40        ctx.submodule_config()
41            .iter()
42            // Extract the current path's configuration.
43            .filter_map(|(name, config)| {
44                config
45                    .get("url")
46                    .and_then(|url| config.get("path").map(|path| (url, path)))
47                    .and_then(|(url, path)| {
48                        if path == diff_path {
49                            let branch = config.get("branch").map(String::as_str);
50
51                            Some((name, path, url, branch))
52                        } else {
53                            None
54                        }
55                    })
56            })
57            // We only care about the first instance which matches.
58            .next()
59            // Turn it into a SubmoduleContext.
60            .map(|(name, path, url, branch)| {
61                let gitdir = ctx.gitdir().join("modules").join(name);
62                let context = GitContext::new(&gitdir);
63
64                let branch = if let Some(branch) = branch {
65                    branch.into()
66                } else {
67                    match context.default_branch() {
68                        Ok(Some(branch)) => branch.into(),
69                        Ok(None) => "master".into(),
70                        Err(err) => {
71                            error!(
72                                "failed to determine the default branch in {}: {}",
73                                gitdir.display(),
74                                err,
75                            );
76
77                            "master".into()
78                        },
79                    }
80                };
81
82                SubmoduleContext {
83                    name: name.into(),
84                    path: path.into(),
85                    url: url.into(),
86                    branch,
87
88                    context,
89                }
90            })
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use std::path::Path;
97
98    use git_workarea::{CommitId, Identity};
99
100    use crate::context::CheckGitContext;
101    use crate::test::*;
102    use crate::utils::SubmoduleContext;
103
104    const DEFAULT_NAME_SUBMODULE: &str = "fe90ee22ae3ce4b4dc41f8d0876e59355ff1e21c";
105    const CUSTOM_NAME_SUBMODULE: &str = "4f645a7cc4c6a210193e1df5931f6336fa10459f";
106
107    fn make_submodule_context(path: &Path, commit: &CommitId) -> CheckGitContext {
108        let ctx = make_context_submodule(path, commit);
109        let workarea = ctx.prepare(commit).unwrap();
110        let who = Identity::new("Rust Git Checks Tests", "rust-git-checks@example.com");
111
112        CheckGitContext::new(workarea, who)
113    }
114
115    #[test]
116    fn test_submodule_context_no_exist() {
117        let tempdir = make_temp_dir();
118        let ctx = make_submodule_context(tempdir.path(), &CommitId::new(DEFAULT_NAME_SUBMODULE));
119        assert!(SubmoduleContext::new(&ctx, "no-submodule").is_none());
120    }
121
122    #[test]
123    fn test_submodule_context_default_name() {
124        let tempdir = make_temp_dir();
125        let ctx = make_submodule_context(tempdir.path(), &CommitId::new(DEFAULT_NAME_SUBMODULE));
126        let sub_ctx = SubmoduleContext::new(&ctx, "submodule").unwrap();
127
128        assert_eq!(sub_ctx.name, "submodule");
129        assert_eq!(sub_ctx.path, "submodule");
130        assert_eq!(
131            sub_ctx.url,
132            "https://gitlab.kitware.com/utils/test-repo.git",
133        );
134        assert_eq!(sub_ctx.branch, "master");
135        assert_eq!(
136            sub_ctx.context.gitdir(),
137            tempdir.path().join("origin/.git/modules/submodule"),
138        );
139    }
140
141    #[test]
142    fn test_submodule_context_custom_name() {
143        let tempdir = make_temp_dir();
144        let ctx = make_submodule_context(tempdir.path(), &CommitId::new(CUSTOM_NAME_SUBMODULE));
145        let sub_ctx = SubmoduleContext::new(&ctx, "submodule").unwrap();
146
147        assert_eq!(sub_ctx.name, "custom-name");
148        assert_eq!(sub_ctx.path, "submodule");
149        assert_eq!(
150            sub_ctx.url,
151            "https://gitlab.kitware.com/utils/test-repo.git",
152        );
153        assert_eq!(sub_ctx.branch, "master");
154        assert_eq!(
155            sub_ctx.context.gitdir(),
156            tempdir.path().join("origin/.git/modules/custom-name"),
157        );
158    }
159}