// package contract -- контракт каждого протона и нейтрона package contract import ( "os" . "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers" ) // isContractDisabled -- признак включённости контрактов // По умолчанию определяется переменной окружения IS_CONTRACT (любое непустое значение включает контракты). var isContractDisabled = os.Getenv("IS_CONTRACT") == "" // stage -- значение переменной окружения STAGE // Если STAGE != "prod", контракты всегда включены. var stage = os.Getenv("STAGE") // Contract -- контракт каждого протона и нейтрона // T -- тип контекста, передаваемого в пред-, ин- и постусловия type Contract struct { // fnPre -- предусловие: должно быть истинно перед выполнением операции fnPre func() // fnPost -- постусловие: должно быть истинно после выполнения операции fnPost func() } // ContractOpt -- опция для настройки контракта type ContractOpt func(*Contract) // ContractOptPre -- задать предусловие func ContractOptPre(fn func()) ContractOpt { return func(sf *Contract) { sf.fnPre = fn } } // ContractOptPost -- задать постусловие func ContractOptPost(fn func()) ContractOpt { return func(sf *Contract) { sf.fnPost = fn } } // NewContract -- создать новый контракт с опциями func NewContract(opts ...ContractOpt) *Contract { sf := &Contract{} for _, opt := range opts { opt(sf) } return sf } // Wrap -- оборачивает произвольную функцию выполнением контракта // Схема вызова: Pre(ctx) -> fn(ctx) -> Post(ctx). // Если соответствующее условие не задано, оно пропускается. func (sf *Contract) Wrap(fn func()) func() { Hassert(fn != nil, "Contract[T].Wrap(): fn==nil") // В непроизводственных окружениях (STAGE != "prod") всегда оборачиваем if stage == "prod" && isContractDisabled { // В prod оборачиваем только если явно включены контракты return fn } return func() { if sf.fnPre != nil { sf.fnPre() } fn() if sf.fnPost != nil { sf.fnPost() } } }