Юлия обнови решението на 06.12.2015 17:33 (преди над 2 години)
+package main
+
+import (
+        "encoding/json"
+        "encoding/xml"
+        "fmt"
+        "sync"
+)
+
+const (
+        _ = iota
+        TakeBook
+        ReturnBook
+        GetAvailability
+)
+
+type Author struct {
+        FirstName  string `json:"first_name" xml:"first_name"`
+        SecondName string `json:"last_name" xml:"last_name"`
+}
+
+type Book struct {
+        Isbn    string `json:"isbn" xml:"isbn,attr"`
+        Title   string `json:"title" xml:"title"`
+        Author  Author `json:"author" xml:"author"`
+        Ratings []int  `json:"ratings" xml:"ratings>rating"`
+}
+
+func (b *Book) String() string {
+        return fmt.Sprintf("[%s] %s от %s %s", b.Isbn, b.Title, b.Author.FirstName, b.Author.SecondName)
+}
+
+type Library interface {
+        AddBookJSON(data []byte) (int, error)
+        AddBookXML(data []byte) (int, error)
+        Hello() (chan<- LibraryRequest, <-chan LibraryResponse)
+}
+type Librarians struct {
+        count     int
+        mutex     *sync.Mutex
+        condition *sync.Cond
+}
+
+type MyLibrary struct {
+        syncLibrary *SyncLibrary
+        librarians  *Librarians
+}
+
+type SyncLibrary struct {
+        books map[string]*LibraryBook
+        mutex *sync.Mutex
+}
+
+func (b *MyLibrary) AddBook(data []byte, format string) (int, error) {
+        var book Book
+        var err error
+        library := b.syncLibrary.books
+        libraryMutex := b.syncLibrary.mutex
+
+        if format == "json" {
+                err = json.Unmarshal(data, &book)
+        } else {
+                err = xml.Unmarshal(data, &book)
+        }
+        if err != nil {
+                return 0, err
+        }
+        libraryMutex.Lock()
+        value, ok := library[book.Isbn]
+        libraryMutex.Unlock()
+
+        if !ok {
+                libraryMutex.Lock()
+                library[book.Isbn] = &LibraryBook{book, 1, 0}
+                libraryMutex.Unlock()
+                return 1, nil
+        } else {
+                if value.registered == 4 {
+                        return 0, fmt.Errorf("Има 4 копия на книга %s", book.Isbn)
+                }
+                count := library[book.Isbn].add("registered")
+                return count, nil
+        }
+}
+func (b *MyLibrary) AddBookJSON(data []byte) (int, error) {
+        return b.AddBook(data, "json")
+}
+
+func (b *MyLibrary) AddBookXML(data []byte) (int, error) {
+        return b.AddBook(data, "xml")
+}
+
+func (l *MyLibrary) TakeLibrarian() {
+        librarians := l.librarians
+        librarians.mutex.Lock()
+        defer librarians.mutex.Unlock()
+        librarians.condition.L.Lock()
+        for librarians.count == 0 {
+                        librarians.condition.Wait()
+        }
+        librarians.count -= 1
+        librarians.condition.L.Unlock()
+}
+
+func (l *MyLibrary) ReturnLibrarian() {
+        librarians := l.librarians
+        librarians.mutex.Lock()
+        defer librarians.mutex.Unlock()
+        librarians.count += 1
+        if librarians.count == 1 {
+                librarians.condition.Broadcast()
+        }
+}
+
+func (b *MyLibrary) Hello() (chan<- LibraryRequest, <-chan LibraryResponse) {
+        b.TakeLibrarian()
+        requestChannel := make(chan LibraryRequest, 10000)
+        responseChannel := make(chan LibraryResponse, 10000)
+        go serveRequests(b, requestChannel, responseChannel)
+        return requestChannel, responseChannel
+}
+
+func serveRequests(library *MyLibrary, requests chan LibraryRequest, responses chan LibraryResponse) {
+        for request := range requests {
+                switch request.GetType() {
+                case TakeBook:
+                        takeBook(library, request, responses)
+                case ReturnBook:
+                        returnBook(library, request, responses)
+                case GetAvailability:
+                        getAvailability(library, request, responses)
+                }
+        }
+        library.ReturnLibrarian()
+}
+
+func takeBook(lib *MyLibrary, request LibraryRequest, responses chan LibraryResponse) {
+        library := lib.syncLibrary.books
+        libraryMutex := lib.syncLibrary.mutex
+        libraryMutex.Lock()
+        bookInfo, ok := library[request.GetISBN()]
+        if !ok {
+                response := &LibraryResponseInfo{nil, 0, 0, fmt.Errorf("Непозната книга %s", request.GetISBN())}
+                libraryMutex.Unlock()
+                responses <- response
+                return
+        }
+        if bookInfo.get("available") == 0 {
+                response := &LibraryResponseInfo{&bookInfo.book, bookInfo.get("available"), bookInfo.get("registered"), fmt.Errorf("Няма наличност на книга %s", request.GetISBN())}
+                libraryMutex.Unlock()
+                responses <- response
+                return
+        }
+        bookInfo.add("borrowed")
+        response := &LibraryResponseInfo{&bookInfo.book, bookInfo.get("available"), bookInfo.get("registered"), nil}
+        libraryMutex.Unlock()
+        responses <- response
+}
+
+func returnBook(lib *MyLibrary, request LibraryRequest, responses chan LibraryResponse) {
+        library := lib.syncLibrary.books
+        libraryMutex := lib.syncLibrary.mutex
+        libraryMutex.Lock()
+        bookInfo, ok := library[request.GetISBN()]
+        if !ok {
+                response := &LibraryResponseInfo{nil, 0, 0, fmt.Errorf("Непозната книга %s", request.GetISBN())}
+                libraryMutex.Unlock()
+                responses <- response
+                return
+        } else if bookInfo.get("borrowed") == bookInfo.get("registered") {
+                response := &LibraryResponseInfo{nil, bookInfo.get("available"), bookInfo.get("registered"), fmt.Errorf("Всички копия са налични %s", request.GetISBN())}
+                libraryMutex.Unlock()
+                responses <- response
+                return
+        }
+        bookInfo.returnBorrowed()
+        response := &LibraryResponseInfo{&bookInfo.book, bookInfo.get("available"), bookInfo.get("registered"), nil}
+        libraryMutex.Unlock()
+        responses <- response
+}
+
+func getAvailability(lib *MyLibrary, request LibraryRequest, responses chan LibraryResponse) {
+        library := lib.syncLibrary.books
+        libraryMutex := lib.syncLibrary.mutex
+        libraryMutex.Lock()
+        bookInfo, ok := library[request.GetISBN()]
+        if !ok {
+                response := &LibraryResponseInfo{nil, 0, 0, fmt.Errorf("Непозната книга %s", request.GetISBN())}
+                libraryMutex.Unlock()
+                responses <- response
+                return
+        } else {
+                response := &LibraryResponseInfo{&bookInfo.book, bookInfo.get("available"), bookInfo.get("registered"), nil}
+                libraryMutex.Unlock()
+                responses <- response
+        }
+}
+
+type LibraryBook struct {
+        book       Book
+        registered int
+        borrowed   int
+}
+
+func (lb *LibraryBook) add(field string) int {
+        switch field {
+        case "registered":
+                lb.registered++
+                return lb.registered
+        case "borrowed":
+                lb.borrowed++
+                return lb.borrowed
+        }
+        return -1
+}
+
+func (lb *LibraryBook) returnBorrowed() int {
+        lb.borrowed--
+        return lb.borrowed
+}
+
+func (lb *LibraryBook) get(field string) int {
+        switch field {
+        case "borrowed":
+                return lb.borrowed
+        case "registered":
+                return lb.registered
+        case "available":
+                return lb.registered - lb.borrowed
+        }
+        return -1
+}
+
+func NewLibrary(librariansNum int) Library {
+        lib := make(map[string]*LibraryBook)
+        locker := new(sync.Mutex)
+        librarians := &Librarians{librariansNum, new(sync.Mutex), sync.NewCond(locker)}
+        syncLibrary := &SyncLibrary{lib, new(sync.Mutex)}
+        library := &MyLibrary{syncLibrary, librarians}
+        return library
+}
+
+type LibraryRequest interface {
+        GetType() int
+        GetISBN() string
+}
+
+type LibraryRequestInfo struct {
+        requestType       int
+        bookRequestedISBN string
+}
+
+func (r *LibraryRequestInfo) GetType() int {
+        return r.requestType
+}
+
+func (r *LibraryRequestInfo) GetISBN() string {
+        return r.bookRequestedISBN
+}
+
+type LibraryResponse interface {
+        GetBook() (fmt.Stringer, error)
+        GetAvailability() (available int, registered int)
+}
+
+type LibraryResponseInfo struct {
+        book          *Book
+        availableLeft int
+        registered    int
+        error         error
+}
+
+func (l *LibraryResponseInfo) GetBook() (fmt.Stringer, error) {
+        if l.book == nil {
+                return nil, l.error
+        }
+        return l.book, nil
+}
+
+func (l *LibraryResponseInfo) GetAvailability() (available int, registered int) {
+        return l.availableLeft, l.registered
+}
