ctrlc.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package ctrlc
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "os/signal"
  7. "strings"
  8. "syscall"
  9. "time"
  10. )
  11. const C_ICON_START = "🌟"
  12. const C_ICON_WARN = "⚡️"
  13. const C_ICON_HOT = "🔥"
  14. const C_ICON_MAG = "✨"
  15. const C_ICON_SC = "🌳"
  16. type Iface interface {
  17. Shutdown(ctx context.Context) error
  18. }
  19. type CallbackFunc func(ctx context.Context, shutdown context.CancelFunc) *[]Iface
  20. func App(f CallbackFunc) {
  21. AppWithTimeOut(8*time.Second, f)
  22. }
  23. func AppWithTimeOut(t time.Duration, f CallbackFunc) {
  24. stop := make(chan os.Signal, 1)
  25. signal.Notify(stop, syscall.SIGTERM)
  26. signal.Notify(stop, syscall.SIGINT)
  27. fmt.Printf(
  28. icon_start(UseColors())+"%s\n",
  29. cly(
  30. UseColors(),
  31. fmt.Sprintf(
  32. "Application started (timeout %d sec)",
  33. t/time.Second,
  34. ),
  35. ),
  36. )
  37. sctx, shutdown := context.WithCancel(context.Background())
  38. ifaces := f(sctx, shutdown)
  39. select {
  40. case <-sctx.Done():
  41. fmt.Printf(
  42. "\r"+icon_warn(UseColors())+"%s\n",
  43. cly(
  44. UseColors(),
  45. fmt.Sprintf(
  46. "Shutting down (application) (timeout %d sec)",
  47. t/time.Second,
  48. ),
  49. ),
  50. )
  51. case val := <-stop:
  52. switch val {
  53. case syscall.SIGINT:
  54. fmt.Printf(
  55. "\r"+icon_warn(UseColors())+"%s\n",
  56. cly(
  57. UseColors(),
  58. fmt.Sprintf(
  59. "Shutting down (interrupt) (timeout %d sec)",
  60. t/time.Second,
  61. ),
  62. ),
  63. )
  64. case syscall.SIGTERM:
  65. fmt.Printf(
  66. icon_warn(UseColors())+"%s\n",
  67. cly(
  68. UseColors(),
  69. fmt.Sprintf(
  70. "Shutting down (terminate) (timeout %d sec)",
  71. t/time.Second,
  72. ),
  73. ),
  74. )
  75. default:
  76. fmt.Printf(
  77. icon_warn(UseColors())+"%s\n",
  78. cly(
  79. UseColors(),
  80. fmt.Sprintf(
  81. "Shutting down (timeout %d sec)",
  82. t/time.Second,
  83. ),
  84. ),
  85. )
  86. }
  87. }
  88. shutdown()
  89. errors := false
  90. ctx, cancel := context.WithTimeout(context.Background(), t)
  91. for _, iface := range *ifaces {
  92. if err := iface.Shutdown(ctx); err != nil {
  93. if !errors {
  94. errors = true
  95. }
  96. var msg string
  97. switch err.(type) {
  98. case *Error:
  99. msg = fmt.Sprintf("%s", err.Error())
  100. default:
  101. msg = fmt.Sprintf("Shutdown error (%T): %s", iface, err.Error())
  102. }
  103. fmt.Printf(
  104. icon_hot(UseColors())+"%s\n",
  105. clr(UseColors(), msg),
  106. )
  107. }
  108. }
  109. cancel()
  110. if errors {
  111. fmt.Printf(
  112. icon_mag(UseColors())+"%s\n",
  113. cly(
  114. UseColors(),
  115. fmt.Sprintf(
  116. "Application exited with errors (timeout %d sec)",
  117. t/time.Second,
  118. ),
  119. ),
  120. )
  121. os.Exit(1)
  122. } else {
  123. fmt.Printf(
  124. icon_sc(UseColors())+"%s\n",
  125. clg(
  126. UseColors(),
  127. fmt.Sprintf(
  128. "Application exited successfully (timeout %d sec)",
  129. t/time.Second,
  130. ),
  131. ),
  132. )
  133. }
  134. }
  135. func UseColors() bool {
  136. useColors := strings.Contains(
  137. fmt.Sprintf("%s", os.Args),
  138. "--color=always",
  139. )
  140. if !useColors {
  141. useColors = strings.Contains(
  142. fmt.Sprintf("%s", os.Args),
  143. "-color=always",
  144. )
  145. }
  146. if !useColors {
  147. useColors = strings.Contains(
  148. fmt.Sprintf("%s", os.Args),
  149. "color=always",
  150. )
  151. }
  152. if !useColors {
  153. useColors = strings.Contains(
  154. fmt.Sprintf("%s", os.Args),
  155. "--color always",
  156. )
  157. }
  158. if !useColors {
  159. useColors = strings.Contains(
  160. fmt.Sprintf("%s", os.Args),
  161. "-color always",
  162. )
  163. }
  164. if !useColors {
  165. useColors = strings.Contains(
  166. fmt.Sprintf("%s", os.Args),
  167. "color always",
  168. )
  169. }
  170. return !IS_WIN_PLATFORM && useColors
  171. }
  172. func MakeError(shutdown context.CancelFunc, ifaces ...Iface) *[]Iface {
  173. shutdown()
  174. return &ifaces
  175. }