manager.go raw

   1  // Package transport provides a manager for pluggable network transports.
   2  package transport
   3  
   4  import (
   5  	"context"
   6  	"fmt"
   7  	"sync"
   8  
   9  	"next.orly.dev/pkg/lol/log"
  10  
  11  	iface "next.orly.dev/pkg/interfaces/transport"
  12  )
  13  
  14  // Manager manages multiple transports and coordinates their lifecycle.
  15  type Manager struct {
  16  	mu         sync.RWMutex
  17  	transports []iface.Transport
  18  }
  19  
  20  // NewManager creates a new transport manager.
  21  func NewManager() *Manager {
  22  	return &Manager{}
  23  }
  24  
  25  // Add registers a transport with the manager.
  26  func (m *Manager) Add(t iface.Transport) {
  27  	m.mu.Lock()
  28  	defer m.mu.Unlock()
  29  	m.transports = append(m.transports, t)
  30  }
  31  
  32  // StartAll starts all registered transports in order.
  33  // If a transport fails to start, it is logged and skipped.
  34  // Returns an error only if no transports started successfully.
  35  func (m *Manager) StartAll(ctx context.Context) error {
  36  	m.mu.RLock()
  37  	defer m.mu.RUnlock()
  38  
  39  	started := 0
  40  	for _, t := range m.transports {
  41  		log.I.F("starting transport: %s", t.Name())
  42  		if err := t.Start(ctx); err != nil {
  43  			log.E.F("transport %s failed to start: %v (skipping)", t.Name(), err)
  44  			continue
  45  		}
  46  		log.I.F("transport started: %s", t.Name())
  47  		started++
  48  	}
  49  	if started == 0 {
  50  		return fmt.Errorf("no transports started successfully")
  51  	}
  52  	return nil
  53  }
  54  
  55  // StopAll stops all transports in reverse order.
  56  func (m *Manager) StopAll(ctx context.Context) error {
  57  	m.mu.RLock()
  58  	defer m.mu.RUnlock()
  59  
  60  	var firstErr error
  61  	for i := len(m.transports) - 1; i >= 0; i-- {
  62  		t := m.transports[i]
  63  		log.I.F("stopping transport: %s", t.Name())
  64  		if err := t.Stop(ctx); err != nil {
  65  			log.E.F("failed to stop transport %s: %v", t.Name(), err)
  66  			if firstErr == nil {
  67  				firstErr = err
  68  			}
  69  		} else {
  70  			log.I.F("transport stopped: %s", t.Name())
  71  		}
  72  	}
  73  	return firstErr
  74  }
  75  
  76  // Addresses returns all addresses from all transports.
  77  func (m *Manager) Addresses() []string {
  78  	m.mu.RLock()
  79  	defer m.mu.RUnlock()
  80  
  81  	var addrs []string
  82  	for _, t := range m.transports {
  83  		addrs = append(addrs, t.Addresses()...)
  84  	}
  85  	return addrs
  86  }
  87