001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2025-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.common.secret.context;
019
020import dev.enola.common.context.TLC;
021import dev.enola.common.secret.Secret;
022import dev.enola.common.secret.SecretManager;
023import dev.enola.common.secret.UnavailableSecretManager;
024
025import java.io.IOException;
026import java.util.Optional;
027
028/**
029 * SecretManagerTLC is a {@link SecretManager} implementation that looks up the current {@link
030 * SecretManager} from the {@link TLC}. If it's not found there, then it falls back to one passed to
031 * the constructor, which defaults to an {@link dev.enola.common.secret.UnavailableSecretManager}.
032 */
033public class SecretManagerTLC implements SecretManager {
034
035    // We probably don't really need a Singleton<SecretManager> ...
036    // public static final Singleton<SecretManager> SINGLETON = new Singleton<>() {};
037
038    private final SecretManager fallback;
039
040    /** Creates a new instance which falls back to the SecretManager passed to this constructor. */
041    public SecretManagerTLC(SecretManager fallback) {
042        this.fallback = fallback;
043    }
044
045    public SecretManagerTLC() {
046        this(new UnavailableSecretManager());
047    }
048
049    @Override
050    public void store(String key, char[] value) throws IOException {
051        delegate().store(key, value);
052    }
053
054    @Override
055    public Optional<Secret> getOptional(String key) throws IOException {
056        return delegate().getOptional(key);
057    }
058
059    @Override
060    public Secret get(String key) throws IllegalStateException, IOException {
061        return delegate().get(key);
062    }
063
064    @Override
065    public void delete(String key) throws IOException {
066        delegate().delete(key);
067    }
068
069    private SecretManager delegate() {
070        return TLC.optional(SecretManager.class).orElse(fallback);
071    }
072}