git-checks-config 0.3.1

Configuration parsing for checks.
Documentation
// Copyright Kitware, Inc.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::error::Error;

use erased_serde::Deserializer;
use git_checks_core::{BranchCheck, Check, TopicCheck};
use serde::de::DeserializeOwned;

/// Trait for a deserialization structure of a check.
///
/// This trait should be implemented for any structure which can be deserialized and construct a
/// check.
pub trait IntoCheck: DeserializeOwned {
    /// The check parsed by this configuration.
    type Check;

    /// Create a new instance of the check from the configuration.
    fn into_check(self) -> Self::Check;
}

type CtorResult<T> = Result<T, Box<dyn Error + Send + Sync>>;

/// A constructor for a check from a deserialization structure.
///
/// This structure should only be created by the `register_checks` macro.
#[doc(hidden)]
pub struct CheckCtor<T> {
    pub f: fn(&mut dyn Deserializer) -> CtorResult<T>,
}

/// Internal trait for use by the `register_checks` macro implementation.
#[doc(hidden)]
pub trait CheckConfig {
    type CheckTrait: ?Sized;
}

/// Registry type for branch checks.
///
/// Query `inventory` using this type to find all branch checks.
pub struct BranchCheckConfig {
    name: &'static str,
    ctor: CheckCtor<DynBranchCheck>,
}

/// Internal type for use by the `register_checks` macro implementation.
#[doc(hidden)]
pub struct DynBranchCheck(Box<dyn BranchCheck>);

impl BranchCheckConfig {
    /// This structure should only be created by the `register_checks` macro.
    #[doc(hidden)]
    pub const fn new(name: &'static str, ctor: CheckCtor<DynBranchCheck>) -> Self {
        Self {
            name,
            ctor,
        }
    }

    /// The name of the branch check.
    pub fn name(&self) -> &'static str {
        self.name
    }

    /// Create an instance of this check from a deserialization structure.
    pub fn create(&self, conf: &mut dyn Deserializer) -> CtorResult<Box<dyn BranchCheck>> {
        Ok((self.ctor.f)(conf)?.0)
    }

    /// Internal function for use by the `register_checks` macro implementation.
    #[doc(hidden)]
    pub fn ctor<C>(conf: &mut dyn Deserializer) -> CtorResult<DynBranchCheck>
    where
        C: IntoCheck,
        C::Check: BranchCheck + 'static,
    {
        erased_serde::deserialize(conf)
            .map(|c: C| DynBranchCheck(Box::new(c.into_check())))
            .map_err(Into::into)
    }
}

impl CheckConfig for BranchCheckConfig {
    type CheckTrait = dyn BranchCheck;
}

/// Registry type for commit checks.
///
/// Query `inventory` using this type to find all commit checks.
pub struct CommitCheckConfig {
    name: &'static str,
    ctor: CheckCtor<DynCheck>,
}

/// Internal type for use by the `register_checks` macro implementation.
#[doc(hidden)]
pub struct DynCheck(Box<dyn Check>);

impl CommitCheckConfig {
    /// This structure should only be created by the `register_checks` macro.
    #[doc(hidden)]
    pub const fn new(name: &'static str, ctor: CheckCtor<DynCheck>) -> Self {
        Self {
            name,
            ctor,
        }
    }

    /// The name of the commit check.
    pub fn name(&self) -> &'static str {
        self.name
    }

    /// Create an instance of this check from a deserialization structure.
    pub fn create(&self, conf: &mut dyn Deserializer) -> CtorResult<Box<dyn Check>> {
        Ok((self.ctor.f)(conf)?.0)
    }

    /// Internal function for use by the `register_checks` macro implementation.
    #[doc(hidden)]
    pub fn ctor<C>(conf: &mut dyn Deserializer) -> CtorResult<DynCheck>
    where
        C: IntoCheck,
        C::Check: Check + 'static,
    {
        erased_serde::deserialize(conf)
            .map(|c: C| DynCheck(Box::new(c.into_check())))
            .map_err(Into::into)
    }
}

impl CheckConfig for CommitCheckConfig {
    type CheckTrait = dyn Check;
}

/// Registry type for topic checks.
///
/// Query `inventory` using this type to find all topic checks.
pub struct TopicCheckConfig {
    name: &'static str,
    ctor: CheckCtor<DynTopicCheck>,
}

/// Internal type for use by the `register_checks` macro implementation.
#[doc(hidden)]
pub struct DynTopicCheck(Box<dyn TopicCheck>);

impl TopicCheckConfig {
    /// This structure should only be created by the `register_checks` macro.
    #[doc(hidden)]
    pub const fn new(name: &'static str, ctor: CheckCtor<DynTopicCheck>) -> Self {
        Self {
            name,
            ctor,
        }
    }

    /// The name of the topic check.
    pub fn name(&self) -> &'static str {
        self.name
    }

    /// Create an instance of this check from a deserialization structure.
    pub fn create(&self, conf: &mut dyn Deserializer) -> CtorResult<Box<dyn TopicCheck>> {
        Ok((self.ctor.f)(conf)?.0)
    }

    /// Internal function for use by the `register_checks` macro implementation.
    #[doc(hidden)]
    pub fn ctor<C>(conf: &mut dyn Deserializer) -> CtorResult<DynTopicCheck>
    where
        C: IntoCheck,
        C::Check: TopicCheck + 'static,
    {
        erased_serde::deserialize(conf)
            .map(|c: C| DynTopicCheck(Box::new(c.into_check())))
            .map_err(Into::into)
    }
}

impl CheckConfig for TopicCheckConfig {
    type CheckTrait = dyn TopicCheck;
}

inventory::collect!(BranchCheckConfig);
inventory::collect!(CommitCheckConfig);
inventory::collect!(TopicCheckConfig);