converter.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package currency
  2. import (
  3. "fmt"
  4. "strings"
  5. "sync"
  6. "time"
  7. )
  8. type currencyConverter struct {
  9. client client
  10. cache cache
  11. config Config
  12. mu sync.RWMutex
  13. }
  14. func NewConverter(config Config) (Converter, error) {
  15. if config.APIKey == "" {
  16. return nil, fmt.Errorf("API key is required")
  17. }
  18. if config.CacheTTL == 0 {
  19. config.CacheTTL = 24 * time.Hour
  20. }
  21. if config.APITimeout == 0 {
  22. config.APITimeout = 10 * time.Second
  23. }
  24. client := newExchangeRateClient(config.APIKey, config.APITimeout)
  25. cache := newMemoryCache(config.CacheTTL)
  26. return &currencyConverter{
  27. client: client,
  28. cache: cache,
  29. config: config,
  30. }, nil
  31. }
  32. func (c *currencyConverter) Convert(amount float64, from, to string) (float64, error) {
  33. from = strings.ToUpper(strings.TrimSpace(from))
  34. to = strings.ToUpper(strings.TrimSpace(to))
  35. if from == to {
  36. return amount, nil
  37. }
  38. rate, err := c.GetRate(from, to)
  39. if err != nil {
  40. return 0, fmt.Errorf("failed to get exchange rate from %s to %s: %w", from, to, err)
  41. }
  42. return amount * rate, nil
  43. }
  44. func (c *currencyConverter) GetRate(from, to string) (float64, error) {
  45. from = strings.ToUpper(strings.TrimSpace(from))
  46. to = strings.ToUpper(strings.TrimSpace(to))
  47. if from == to {
  48. return 1.0, nil
  49. }
  50. cachedRates, found := c.cache.get(from)
  51. if found && cachedRates.rates != nil {
  52. if rate, exists := cachedRates.rates[to]; exists {
  53. return rate, nil
  54. }
  55. }
  56. rates, err := c.fetchAndCacheRates(from)
  57. if err != nil {
  58. return 0, err
  59. }
  60. rate, exists := rates[to]
  61. if !exists {
  62. return 0, fmt.Errorf("currency %s not supported or not found in exchange rates", to)
  63. }
  64. return rate, nil
  65. }
  66. func (c *currencyConverter) fetchAndCacheRates(baseCurrency string) (map[string]float64, error) {
  67. c.mu.Lock()
  68. defer c.mu.Unlock()
  69. if cachedRates, found := c.cache.get(baseCurrency); found {
  70. return cachedRates.rates, nil
  71. }
  72. response, err := c.client.fetchRates(baseCurrency)
  73. if err != nil {
  74. return nil, fmt.Errorf("failed to fetch rates from API: %w", err)
  75. }
  76. cachedRates := &cachedRates{
  77. rates: response.ConversionRates,
  78. baseCode: response.BaseCode,
  79. fetchedAt: time.Now(),
  80. }
  81. c.cache.set(baseCurrency, cachedRates)
  82. return response.ConversionRates, nil
  83. }