// package contract -- контракт каждого протона и нейтрона. package contract import ( "os" mKh "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() { mKh.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() } } }