Даниел обнови решението на 01.12.2015 20:47 (преди над 2 години)
+package main
+
+import (
+        "encoding/json"
+        "encoding/xml"
+        "fmt"
+)
+
+type empty struct{}
+type semaphore chan empty
+
+type book struct {
+        ISBN   string `xml:"isbn,attr"`
+        Title  string `xml:"title"`
+        Genre  string `xml:"genre"`
+        Author struct {
+                First_name string `xml:"first_name"`
+                Last_name  string `xml:"last_name"`
+        } `xml:"author"`
+        copies struct {
+                available, registered int
+        }
+}
+
+func (b *book) String() string {
+        return "[" + b.ISBN + "] " + b.Title + " от " +
+                b.Author.First_name + " " + b.Author.Last_name
+}
+
+type Bibliotheke struct {
+        librarians semaphore
+        books      map[string]book
+}
+
+func NewLibrary(librarians int) Library {
+        var b *Bibliotheke = new(Bibliotheke)
+        b.librarians = make(semaphore, librarians)
+        b.books = make(map[string]book)
+        return b
+}
+
+func (b *Bibliotheke) AddBook(book book) (int, error) {
+        if _, ok := b.books[book.ISBN]; ok {
+                book.copies = b.books[book.ISBN].copies
+        }
+
+        if book.copies.registered == 4 {
+                return book.copies.registered, fmt.Errorf("Има 4 копия на книга %s", book.ISBN)
+        }
+
+        book.copies.registered += 1
+        book.copies.available += 1
+
+        b.books[book.ISBN] = book
+
+        return book.copies.registered, nil
+
+}
+
+func (b *Bibliotheke) AddBookJSON(Data []byte) (int, error) {
+        var book book
+
+        if err := json.Unmarshal(Data, &book); err != nil {
+                fmt.Errorf("%s", err)
+        }
+        return b.AddBook(book)
+}
+
+func (b *Bibliotheke) AddBookXML(Data []byte) (int, error) {
+        var book book
+
+        if err := xml.Unmarshal(Data, &book); err != nil {
+                fmt.Errorf("%s", err)
+        }
+
+        return b.AddBook(book)
+}
+
+func (b *Bibliotheke) Hello() (chan<- LibraryRequest, <-chan LibraryResponse) {
+        b.librarians <- empty{}
+        requests, responses := make(chan LibraryRequest), make(chan LibraryResponse)
+
+        go func() {
+                for req := range requests {
+                        var resp BibliothekeResponse
+
+                        switch req.GetType() {
+                        case 1:
+                                resp = b.borrowBook(req.GetISBN())
+                        case 2:
+                                resp = b.returnBook(req.GetISBN())
+                        case 3:
+                                resp = b.bookAvailability(req.GetISBN())
+                        }
+                        responses <- &resp
+                }
+                <-b.librarians
+                close(responses)
+        }()
+
+        return requests, responses
+}
+
+type BibliothekeResponse struct {
+        pbook                 *book
+        err                   error
+        available, registered int
+}
+
+func (br *BibliothekeResponse) GetBook() (fmt.Stringer, error) {
+        return br.pbook, br.err
+}
+
+func (br *BibliothekeResponse) GetAvailability() (int, int) {
+        return br.available, br.registered
+}
+
+func (b *Bibliotheke) borrowBook(isbn string) (resp BibliothekeResponse) {
+        if book, ok := b.books[isbn]; !ok {
+                resp.err = fmt.Errorf("Непозната книга %s", isbn)
+        } else if book.copies.available == 0 {
+                resp.err = fmt.Errorf("Няма наличност на книга %s", isbn)
+                resp.registered = book.copies.registered
+        } else {
+                book.copies.available--
+                b.books[isbn] = book
+
+                resp.pbook = &book
+                resp.available = book.copies.available
+                resp.registered = book.copies.registered
+        }
+
+        return resp
+}
+
+func (b *Bibliotheke) returnBook(isbn string) (resp BibliothekeResponse) {
+        if book, ok := b.books[isbn]; !ok {
+                resp.err = fmt.Errorf("Непозната книга %s", isbn)
+        } else if book.copies.available == book.copies.registered {
+                resp.err = fmt.Errorf("Всички копия са налични %s", isbn)
+                resp.available = book.copies.available
+                resp.registered = book.copies.registered
+        } else {
+                book.copies.available++
+                b.books[isbn] = book
+
+                resp.pbook = &book
+                resp.available = book.copies.available
+                resp.registered = book.copies.registered
+        }
+
+        return resp
+}
+
+func (b *Bibliotheke) bookAvailability(isbn string) (resp BibliothekeResponse) {
+        if book, ok := b.books[isbn]; !ok {
+                resp.err = fmt.Errorf("Непозната книга %s", isbn)
+        } else {
+                resp.pbook = &book
+                resp.available = book.copies.available
+                resp.registered = book.copies.registered
+        }
+
+        return resp
+}
+
+// Interfaces
+type Library interface {
+
+        // Добавя книга от json
+        // Oтговаря с общия брой копия в библиотеката (не само наличните).
+        // Aко са повече от 4 - връща грешка
+        AddBookJSON(data []byte) (int, error)
+
+        // Добавя книга от xml
+        // Oтговаря с общия брой копия в библиотеката (не само наличните).
+        // Ако са повече от 4 - връщаме грешка
+        AddBookXML(data []byte) (int, error)
+
+        // Ангажира свободен "библиотекар" да ни обработва заявките.
+        // Библиотекарите са фиксиран брой - подават се като параметър на NewLibrary
+        // Блокира ако всички библиотекари са заети.
+        // Връщат се два канала:
+        // първият е само за писане -  по него ще изпращаме заявките
+        // вторият е само за четене - по него ще получаваме отговорите.
+        // Ако затворим канала със заявките - освобождаваме библиотекаря.
+        Hello() (chan<- LibraryRequest, <-chan LibraryResponse)
+}
+
+type LibraryRequest interface {
+        // Тип на заявката:
+        // 1 - Borrow book
+        // 2 - Return book
+        // 3 - Get availability information about book
+        GetType() int
+
+        // Връща isbn на книгата, за която се отнася Request-a
+        GetISBN() string
+}
+
+type LibraryResponse interface {
+        // Ако книгата съществува/налична е - обект имплементиращ Stringer (повече информация по-долу)
+        // Aко книгата не съществува първият резултат е nil.
+        // Връща се и подобаващa грешка (виж по-долу) - ако такава е възникнала.
+        // Когато се е резултат на заявка от тип 2 (Return book) - не е нужно да я закачаме към отговора.
+        GetBook() (fmt.Stringer, error)
+
+        // available - Колко наличности от книгата имаме останали след изпълнението на заявката.
+        // Тоест, ако сме имали 3 копия от Х и това е отговор на Take заявка - тук ще има 2.
+        // registered - Колко копия от тази книга има регистрирани в библиотеката (макс 4).
+        GetAvailability() (available int, registered int)
+}
Забравих, че map-ът не е thread safe, но ще го оправя като имам време : )
Прав си, не е thread safe. Ще е добре да го оправиш ;)
