strategy.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package storage
  2. import (
  3. "database/sql"
  4. "errors"
  5. "log"
  6. )
  7. type Strategy interface {
  8. Name() string
  9. Alias(url LongUrl) (ShortUrl, error)
  10. UseCount(storage Storage) int
  11. }
  12. type baseStrategy struct{}
  13. func (y baseStrategy) Name() string {
  14. return "base"
  15. }
  16. func (y baseStrategy) Alias(long LongUrl) (ShortUrl, error) {
  17. var ret ShortUrl
  18. var err error = errors.New("Base strategy is abstract")
  19. return ret, err
  20. }
  21. /**
  22. Any nonzero result is likely an error.
  23. */
  24. func (y baseStrategy) UseCount(s Storage) int {
  25. sql := `
  26. SELECT COUNT(*)
  27. FROM shorturl
  28. WHERE strategy = ?
  29. `
  30. var count int
  31. err := s.DB.QueryRow(sql, y.Name()).Scan(&count)
  32. if err != nil {
  33. count = 0
  34. log.Printf("Failed querying database for base strategy use count: %v\n", err)
  35. }
  36. return count
  37. }
  38. /*
  39. Legacy strategy : hex dump for crc32 hash of source URL.
  40. - Pros :
  41. - URLs are easy to communicate over the phone, especially to programmers, even in poor sound conditions.
  42. - Cons :
  43. - They are rather long
  44. - Does not handle collisions: first come, first serve. Later entries are simply rejected.
  45. */
  46. type HexCrc32Strategy struct {
  47. base baseStrategy
  48. }
  49. func (s HexCrc32Strategy) Name() string {
  50. return "hexCrc32"
  51. }
  52. func (s HexCrc32Strategy) Alias(url LongUrl) (ShortUrl, error) {
  53. var ret ShortUrl
  54. return ret, errors.New("HexCrc32 not implemented yet")
  55. }
  56. func (y HexCrc32Strategy) UseCount(s Storage) int {
  57. return y.base.UseCount(s)
  58. }
  59. type ManualStrategy struct {
  60. base baseStrategy
  61. }
  62. func (y ManualStrategy) Name() string {
  63. return "manual"
  64. }
  65. func (y ManualStrategy) Alias(long LongUrl) (ShortUrl, error) {
  66. var ret ShortUrl
  67. return ret, errors.New("Manual not implemented yet")
  68. }
  69. func (y ManualStrategy) UseCount(s Storage) int {
  70. return y.base.UseCount(s)
  71. }
  72. var Strategies map[string]Strategy
  73. func init() {
  74. var strategyImplementations []Strategy = []Strategy{
  75. baseStrategy{},
  76. HexCrc32Strategy{},
  77. ManualStrategy{},
  78. }
  79. Strategies = make(map[string]Strategy, len(strategyImplementations))
  80. for _, s := range strategyImplementations {
  81. Strategies[s.Name()] = s
  82. }
  83. }
  84. func StrategyStatistics(s Storage) map[string]int64 {
  85. var ret map[string]int64 = make(map[string]int64, len(Strategies))
  86. var err error
  87. var strategyResult sql.NullString
  88. var countResult sql.NullInt64
  89. sql := `
  90. SELECT strategy, COUNT(*)
  91. FROM shorturl
  92. GROUP BY strategy
  93. `
  94. rows, err := s.DB.Query(sql)
  95. if err != nil {
  96. log.Printf("Failed querying database for strategy statistics: %v\n", err)
  97. }
  98. defer rows.Close()
  99. for rows.Next() {
  100. if err = rows.Scan(&strategyResult, &countResult); err != nil {
  101. log.Fatal(err)
  102. }
  103. validStrategy, ok := Strategies[strategyResult.String]
  104. if !ok {
  105. log.Fatalf("'%s' is not a valid strategy\n", strategyResult)
  106. }
  107. ret[validStrategy.Name()] = countResult.Int64
  108. }
  109. for name, _ := range Strategies {
  110. _, ok := ret[name]
  111. if !ok {
  112. ret[name] = 0
  113. }
  114. }
  115. return ret
  116. }