Skip to main content

jj_lib/
signing_factory.rs

1// Copyright 2026 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Contains helpers to create a [`Signer`] from the given [`UserSettings`].
16
17use thiserror::Error;
18
19use crate::config::ConfigGetError;
20use crate::gpg_signing::GpgBackend;
21use crate::gpg_signing::GpgsmBackend;
22use crate::settings::UserSettings;
23use crate::signing::Signer;
24use crate::signing::SigningBackend;
25use crate::ssh_signing::SshBackend;
26#[cfg(feature = "testing")]
27use crate::test_signing_backend::TestSigningBackend;
28
29/// An error type for the signing backend initialization.
30#[derive(Debug, Error)]
31pub enum SignInitError {
32    /// If the backend name specified in the config is not known.
33    #[error("Unknown signing backend configured: {0}")]
34    UnknownBackend(String),
35    /// Failed to load backend configuration.
36    #[error("Failed to configure signing backend")]
37    BackendConfig(#[source] ConfigGetError),
38}
39
40/// Creates a signer based on user settings. Uses all known backends, and
41/// chooses one of them to be used for signing depending on the config.
42pub fn signer_from_settings(settings: &UserSettings) -> Result<Signer, SignInitError> {
43    let mut backends: Vec<Box<dyn SigningBackend>> = vec![
44        Box::new(GpgBackend::from_settings(settings).map_err(SignInitError::BackendConfig)?),
45        Box::new(GpgsmBackend::from_settings(settings).map_err(SignInitError::BackendConfig)?),
46        Box::new(SshBackend::from_settings(settings).map_err(SignInitError::BackendConfig)?),
47        #[cfg(feature = "testing")]
48        Box::new(
49            TestSigningBackend::from_settings(settings).map_err(SignInitError::BackendConfig)?,
50        ),
51    ];
52
53    let main_backend = settings
54        .signing_backend()
55        .map_err(SignInitError::BackendConfig)?
56        .map(|backend| {
57            backends
58                .iter()
59                .position(|b| b.name() == backend)
60                .map(|i| backends.remove(i))
61                .ok_or(SignInitError::UnknownBackend(backend))
62        })
63        .transpose()?;
64
65    Ok(Signer::new(main_backend, backends))
66}