Юлия обнови решението на 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
+}