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            vec![
50                format!("mit.author.coauthors.{index}.name"),
51                format!("mit.author.coauthors.{index}.email"),
52            ]
53            .into_iter()
54        })
55        .collect()
56}
57
58fn set_vcs_coauthors(config: &mut dyn Vcs, authors: &[&Author<'_>]) -> Result<()> {
59    authors
60        .iter()
61        .enumerate()
62        .try_for_each(|(index, author)| set_vcs_coauthor(config, index, author))
63}
64
65fn set_vcs_coauthor(config: &mut dyn Vcs, index: usize, author: &Author<'_>) -> Result<()> {
66    set_vcs_coauthor_name(config, index, author)?;
67    set_vcs_coauthor_email(config, index, author)?;
68
69    Ok(())
70}
71
72fn set_vcs_coauthor_name(config: &mut dyn Vcs, index: usize, author: &Author<'_>) -> Result<()> {
73    config.set_str(&format!("mit.author.coauthors.{index}.name"), author.name())?;
74    Ok(())
75}
76
77fn set_vcs_coauthor_email(config: &mut dyn Vcs, index: usize, author: &Author<'_>) -> Result<()> {
78    config.set_str(
79        &format!("mit.author.coauthors.{index}.email"),
80        author.email(),
81    )?;
82    Ok(())
83}
84
85fn set_vcs_user(config: &mut dyn Vcs, author: &Author<'_>) -> Result<()> {
86    config.set_str("user.name", author.name())?;
87    config.set_str("user.email", author.email())?;
88    set_author_signing_key(config, author)?;
89
90    Ok(())
91}
92
93fn set_author_signing_key(config: &mut dyn Vcs, author: &Author<'_>) -> Result<()> {
94    match author.signingkey() {
95        Some(key) => config
96            .set_str("user.signingkey", key)
97            .wrap_err("failed to set git author's signing key "),
98        None => config.remove("user.signingkey").or_else(|_| Ok(())),
99    }
100}
101
102fn set_vcs_expires_time(config: &mut dyn Vcs, expires_in: Duration) -> Result<()> {
103    let now = SystemTime::now()
104        .duration_since(UNIX_EPOCH)
105        .into_diagnostic()?;
106    let expiry_time = now.add(expires_in).as_secs().try_into().into_diagnostic()?;
107    config
108        .set_i64(CONFIG_KEY_EXPIRES, expiry_time)
109        .wrap_err("failed to set author expiry name")
110}