contract.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. // package contract -- контракт каждого протона и нейтрона.
  2. package contract
  3. import (
  4. "os"
  5. mKh "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
  6. )
  7. // isContractDisabled -- признак включённости контрактов.
  8. // По умолчанию определяется переменной окружения IS_CONTRACT (любое непустое значение включает контракты).
  9. var isContractDisabled = os.Getenv("IS_CONTRACT") == ""
  10. // stage -- значение переменной окружения STAGE.
  11. // Если STAGE != "prod", контракты всегда включены.
  12. var stage = os.Getenv("STAGE")
  13. // Contract -- контракт каждого протона и нейтрона.
  14. // T -- тип контекста, передаваемого в пред-, ин- и постусловия.
  15. type Contract struct {
  16. // fnPre -- предусловие: должно быть истинно перед выполнением операции
  17. fnPre func()
  18. // fnPost -- постусловие: должно быть истинно после выполнения операции
  19. fnPost func()
  20. }
  21. // ContractOpt -- опция для настройки контракта.
  22. type ContractOpt func(*Contract)
  23. // ContractOptPre -- задать предусловие.
  24. func ContractOptPre(fn func()) ContractOpt {
  25. return func(sf *Contract) {
  26. sf.fnPre = fn
  27. }
  28. }
  29. // ContractOptPost -- задать постусловие.
  30. func ContractOptPost(fn func()) ContractOpt {
  31. return func(sf *Contract) {
  32. sf.fnPost = fn
  33. }
  34. }
  35. // NewContract -- создать новый контракт с опциями.
  36. func NewContract(opts ...ContractOpt) *Contract {
  37. sf := &Contract{}
  38. for _, opt := range opts {
  39. opt(sf)
  40. }
  41. return sf
  42. }
  43. // Wrap -- оборачивает произвольную функцию выполнением контракта.
  44. // Схема вызова: Pre(ctx) -> fn(ctx) -> Post(ctx).
  45. // Если соответствующее условие не задано, оно пропускается.
  46. func (sf *Contract) Wrap(fn func()) func() {
  47. mKh.Hassert(fn != nil, "Contract[T].Wrap(): fn==nil")
  48. // В непроизводственных окружениях (STAGE != "prod") всегда оборачиваем
  49. if stage == "prod" && isContractDisabled {
  50. // В prod оборачиваем только если явно включены контракты
  51. return fn
  52. }
  53. return func() {
  54. if sf.fnPre != nil {
  55. sf.fnPre()
  56. }
  57. fn()
  58. if sf.fnPost != nil {
  59. sf.fnPost()
  60. }
  61. }
  62. }