-- This file is part of purebred -- Copyright (C) 2021 Fraser Tweedale -- -- purebred is free software: you can redistribute it and/or modify -- it under the terms of the GNU Affero General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU Affero General Public License for more details. -- -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see . {-| The Purebred plugin system. -} module Purebred.Plugin ( -- * Using plugins -- $using usePlugin , PluginDict -- ** Advanced usage for security-conscious users -- $paranoia -- * Defining plugins -- $defining , Plugin(..) -- ** Capabilities -- $capabilities , Pure, CanIO, CanReadConfig, CanRWAppState, Unconstrained -- ** Hooks -- $hooks , PreSendHook(..) , getPreSendHook , ConfigHook(..) , getConfigHook ) where import Purebred.Plugin.Internal {- $using To use plugins, update your @~\/.config\/purebred\/purebred.hs@ to use the 'Purebred.purebred' entry point with a list of plugins. Apply 'usePlugin' to each plugin value to prepare it for use. @ main = 'Purebred.purebred' [ 'usePlugin' ('Purebred.Plugin.TweakConfig.tweakConfig' tweak) , 'usePlugin' Purebred.Plugin.ICU.plugin ] @ Some plugins could require additional configuration arguments. Refer to the documentation for individual plugin modules to find out how to configure them. Purebred plugins have the type @'Plugin' hooks@. @hooks@ encodes the hooks implemented by the plugin, and the required capabilities. In this way, the plugin type is (machine-enforced) documentation about the possible range of plugin behaviour. -} {- $paranoia Security-conscious users can use type annotations to protect against plugins acquiring new capabilities upon upgrade. In the example below, the ICU plugin is fixed at @ConfigHook Pure@. If a new version changes to @ConfigHook CanIO@ or uses additional hooks, the program will not compile. The user can decide whether to accept the new type (by updating the annotation), or remove the plugin. @ main = 'Purebred.purebred' [ 'usePlugin' (Purebred.Plugin.TweakConfig.tweakConfig tweak) , 'usePlugin' (Purebred.Plugin.ICU.plugin :: 'Plugin' ('ConfigHook' 'Pure')) ] @ -} {- $defining The type of a plugin is @'Plugin' hooks@ where @hooks@ is either a single hook function newtype, or a nested tuple of hook functions. Many plugins will only use a single hook. Some will use multiple hooks. To define a plugin, apply the 'Plugin' constructor to a plugin name, 'Version', and hooks. If the plugin requires user configuration (as in the following example), use function argument(s). @ -- | Add a signature to outgoing messages signature :: Text -> Plugin (PreSendHook CanReadConfig) signature str = Plugin "UserAgent" version (PreSendHook hook) where hook msg = view confCharsets >>= manipulateTheMessage msg @ Each hook has a set of /available/ capabilities, and plugins declare the /required/ capabilities for the implemented hooks. Available capabilities differ and are documented at each hook newtype, and in the table below. -} {- $capabilities The plugin system defines the following capabilities. +---------------------+--------------------------------+ | Capabilitity | Description | | | | +=====================+================================+ | 'Pure' | The hook function can only | | | perform a pure computation | | | based on its input. | +---------------------+--------------------------------+ | 'CanReadConfig' | The hook function can read the | | | configuration via 'ask'. | +---------------------+--------------------------------+ | 'CanRWAppState' | The hook function can read and | | | write the 'AppState' via 'get' | | | and 'put'. | +---------------------+--------------------------------+ | 'CanIO' | The hook function can perform | | | I/O via 'liftIO'. | +---------------------+--------------------------------+ | 'Unconstrained' | Meta-capability that implies | | | all defined capabilities. | +---------------------+--------------------------------+ Not all capabilities are available to all hooks (see hooks table below). -} {- $hooks +---------------------+----------------------------+------------------+ | Hook | Description | Available | | | | capabilities | +=====================+============================+==================+ | 'ConfigHook' | Modify config at startup | 'CanIO' | +---------------------+----------------------------+------------------+ | 'PreSendHook' | Modify or process message | 'Unconstrained' | | | before sending | | +---------------------+----------------------------+------------------+ -}