package main import ( "log" ) // Book maps names to repeaters // // It ensures that names map 1-1 to repeaters. type Book struct { entries map[string]*Repeater events chan bookEvent makeRepeater func() *Repeater } func NewBook() Book { return Book{ entries: make(map[string]*Repeater), events: make(chan bookEvent, 5), makeRepeater: NewRepeater, } } type bookEventType int const ( joinEvent = bookEventType(iota) partEvent sendEvent ) type bookEvent struct { eventType bookEventType name string sender MessageSender m Message } // Join adds a writer to a named repeater func (b Book) Join(name string, sender MessageSender) { b.events <- bookEvent{ eventType: joinEvent, name: name, sender: sender, } } // Part removes a writer from a named repeater func (b Book) Part(name string, sender MessageSender) { b.events <- bookEvent{ eventType: partEvent, name: name, sender: sender, } } // Send transmits a message to the named repeater func (b Book) Send(name string, m Message) { b.events <- bookEvent{ eventType: sendEvent, name: name, m: m, } } // Run is the endless run loop func (b Book) Run() { for { b.loop() } } func (b Book) loop() { event := <-b.events repeater, ok := b.entries[event.name] switch event.eventType { case joinEvent: if !ok { repeater = b.makeRepeater() b.entries[event.name] = repeater } repeater.Join(event.sender) case partEvent: if !ok { log.Println("WARN: Parting an empty channel:", event.name) break } repeater.Part(event.sender) if repeater.Listeners() == 0 { delete(b.entries, event.name) } case sendEvent: if !ok { log.Println("WARN: Sending to an empty channel:", event.name) break } repeater.Send(event.m) } }