Skip to main content

mit_commit_message_lints/mit/cmd/
set_commit_authors.rs

1use std::{
2    convert::TryInto,
3    ops::Add,
4    time::{Duration, SystemTime, UNIX_EPOCH},
5};
6
7use miette::{IntoDiagnostic, Result, WrapErr};
8
9use crate::{
10    external::Vcs,
11    mit::{
12        cmd::{errors::Error::NoAuthorsToSet, vcs::has_vcs_coauthor, CONFIG_KEY_EXPIRES},
13        Author,
14    },
15};
16
17/// # Errors
18///
19/// If writing to the git mit file fails for some reason.
20/// This reason will be specific to VCS implementation
21pub fn set_commit_authors(
22    config: &mut dyn Vcs,
23    authors: &[&Author<'_>],
24    expires_in: Duration,
25) -> Result<()> {
26    let (first_author, others) = authors.split_first().ok_or(NoAuthorsToSet)?;
27
28    remove_coauthors(config)?;
29    set_vcs_user(config, first_author)?;
30    set_vcs_coauthors(config, others)?;
31    set_vcs_expires_time(config, expires_in)?;
32
33    Ok(())
34}
35
36fn remove_coauthors(config: &mut dyn Vcs) -> Result<()> {
37    get_defined_vcs_coauthor_keys(config)
38        .into_iter()
39        .try_for_each(|key| config.remove(&key))?;
40
41    Ok(())
42}
43
44#[allow(clippy::maybe_infinite_iter)]
45fn get_defined_vcs_coauthor_keys(config: &dyn Vcs) -> Vec<String> {
46    (0..)
47        .take_while(|index| has_vcs_coauthor(config, *index))
48        .flat_map(|index| {
49            [
50                format!("mit.author.coauthors.{index}.name"),
51                format!("mit.author.coauthors.{index}.email"),
52            ]
53        })
54        .collect()
55}
56
57fn set_vcs_coauthors(config: &mut dyn Vcs, authors: &[&Author<'_>]) -> Result<()> {
58    authors
59        .iter()
60        .enumerate()
61        .try_for_each(|(index, author)| set_vcs_coauthor(config, index, author))
62}
63
64fn set_vcs_coauthor(config: &mut dyn Vcs, index: usize, author: &Author<'_>) -> Result<()> {
65    set_vcs_coauthor_name(config, index, author)?;
66    set_vcs_coauthor_email(config, index, author)?;
67
68    Ok(())
69}
70
71fn set_vcs_coauthor_name(config: &mut dyn Vcs, index: usize, author: &Author<'_>) -> Result<()> {
72    config.set_str(&format!("mit.author.coauthors.{index}.name"), author.name())?;
73    Ok(())
74}
75
76fn set_vcs_coauthor_email(config: &mut dyn Vcs, index: usize, author: &Author<'_>) -> Result<()> {
77    config.set_str(
78        &format!("mit.author.coauthors.{index}.email"),
79        author.email(),
80    )?;
81    Ok(())
82}
83
84fn set_vcs_user(config: &mut dyn Vcs, author: &Author<'_>) -> Result<()> {
85    config.set_str("user.name", author.name())?;
86    config.set_str("user.email", author.email())?;
87    set_author_signing_key(config, author)?;
88
89    Ok(())
90}
91
92fn set_author_signing_key(config: &mut dyn Vcs, author: &Author<'_>) -> Result<()> {
93    if let Some(key) = author.signingkey() {
94        config
95            .set_str("user.signingkey", key)
96            .wrap_err("failed to set git author's signing key ")
97    } else {
98        if config.get_str("user.signingkey")?.is_some() {
99            config
100                .remove("user.signingkey")
101                .wrap_err("failed to remove git author's signing key")?;
102        }
103        Ok(())
104    }
105}
106
107fn set_vcs_expires_time(config: &mut dyn Vcs, expires_in: Duration) -> Result<()> {
108    let now = SystemTime::now()
109        .duration_since(UNIX_EPOCH)
110        .into_diagnostic()?;
111    let expiry_time = now.add(expires_in).as_secs().try_into().into_diagnostic()?;
112    config
113        .set_i64(CONFIG_KEY_EXPIRES, expiry_time)
114        .wrap_err("failed to set author expiry time")
115}