Марио обнови решението на 06.12.2015 17:40 (преди над 2 години)
+package main
+
+import (
+ "encoding/json"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "sync"
+)
+
+type Author struct {
+ FirstName string `json:"first_name" xml:"first_name"`
+ LastName string `json:"last_name" xml:"last_name"`
+}
+
+func (author Author) String() string {
+ return author.FirstName + " " + author.LastName
+}
+
+type Book struct {
+ XMLName xml.Name `xml:"book"`
+ ISBN string `xml:"isbn,attr"`
+ Title string
+ Author Author
+}
+
+func (book Book) String() string {
+ return fmt.Sprintf("[%s] %s от %s", book.ISBN, book.Title, book.Author)
+}
+
+type LibraryResponse interface {
+ GetBook() (fmt.Stringer, error)
+ GetAvailability() (available int, registered int)
+}
+
+type Response struct {
+ book Book
+ availability int
+ registered int
+ err error
+}
+
+func (response Response) GetBook() (fmt.Stringer, error) {
+ if response.err == nil {
+ return response.book, nil
+ } else {
+ return nil, response.err
+ }
+}
+
+func (response Response) GetAvailability() (available int, registered int) {
+ return response.availability, response.registered
+}
+
+type LibraryRequest interface {
+ GetType() int
+ GetISBN() string
+}
+
+type Librarian struct {
+ request chan LibraryRequest
+ response chan LibraryResponse
+ store LibraryStore
+}
+
+type Library interface {
+ AddBookJSON(data []byte) (int, error)
+ AddBookXML(data []byte) (int, error)
+ Hello() (chan<- LibraryRequest, <-chan LibraryResponse)
+}
+
+type LibraryStore struct {
+ librarians chan Librarian
+ books map[string][]Book
+ available map[string]int
+ lock sync.Mutex
+}
+
+func (store LibraryStore) addBook(book Book) (int, error) {
+ if _, ok := store.books[book.ISBN]; !ok {
+ store.books[book.ISBN] = []Book{}
+ store.available[book.ISBN] = 0
+ }
+
+ booksCount := len(store.books[book.ISBN])
+ if booksCount < 4 {
+ store.books[book.ISBN] = append(store.books[book.ISBN], book)
+ store.available[book.ISBN] += 1
+ return booksCount + 1, nil
+ } else {
+ return booksCount, errors.New("Има 4 копия на книга " + book.ISBN)
+ }
+
+}
+
+func (store LibraryStore) AddBookJSON(data []byte) (int, error) {
+ var book Book
+ err := json.Unmarshal(data, &book)
+
+ if err != nil {
+ return 0, err
+ }
+
+ store.lock.Lock()
+ defer store.lock.Unlock()
+
+ return store.addBook(book)
+}
+
+func (store LibraryStore) AddBookXML(data []byte) (int, error) {
+ var book Book
+ err := xml.Unmarshal(data, &book)
+
+ if err != nil {
+ return 0, err
+ }
+
+ store.lock.Lock()
+ defer store.lock.Unlock()
+
+ return store.addBook(book)
+}
+
+func handleBorrow(store LibraryStore, request LibraryRequest) Response {
+ books, ok := store.books[request.GetISBN()]
+ availability := store.available[request.GetISBN()]
+ var response Response
+
+ if !ok {
+ response = Response{err: errors.New("Непозната книга " + request.GetISBN())}
+ } else if availability == 0 {
+ response = Response{
+ registered: len(books),
+ err: errors.New("Няма наличност на книга " + request.GetISBN())}
+ } else {
+ availability -= 1
+ store.available[request.GetISBN()] = availability
+
+ response = Response{
+ availability: availability,
+ registered: len(books),
+ book: books[0],
+ }
+ }
+
+ return response
+}
+
+func handleReturn(store LibraryStore, request LibraryRequest) Response {
+ books, ok := store.books[request.GetISBN()]
+ availability := store.available[request.GetISBN()]
+ var response Response
+
+ if !ok {
+ response = Response{err: errors.New("Непозната книга " + request.GetISBN())}
+ } else if availability == len(books) {
+ response = Response{
+ err: errors.New("Всички копия са налични " + request.GetISBN()),
+ registered: len(books),
+ availability: availability,
+ }
+ } else {
+ availability += 1
+ store.available[request.GetISBN()] = availability
+
+ response = Response{
+ availability: availability,
+ registered: len(books),
+ book: books[0],
+ }
+ }
+
+ return response
+}
+
+func handleAvailability(store LibraryStore, request LibraryRequest) Response {
+ books, ok := store.books[request.GetISBN()]
+ availability := store.available[request.GetISBN()]
+ var response Response
+
+ if !ok {
+ response = Response{err: errors.New("Непозната книга " + request.GetISBN())}
+ } else {
+ store.available[request.GetISBN()] = availability
+
+ response = Response{
+ availability: availability,
+ registered: len(books),
+ book: books[0],
+ }
+ }
+
+ return response
+}
+
+func (store LibraryStore) Hello() (chan<- LibraryRequest, <-chan LibraryResponse) {
+ librarian := <-store.librarians
+ librarian.request = make(chan LibraryRequest)
+ librarian.response = make(chan LibraryResponse)
+
+ go func() {
+ for request := range librarian.request {
+ store := librarian.store
+ store.lock.Lock()
+ var response Response
+
+ switch request.GetType() {
+ case 1:
+ response = handleBorrow(store, request)
+ case 2:
+ response = handleReturn(store, request)
+ case 3:
+ response = handleAvailability(store, request)
+ }
+
+ librarian.response <- response
+
+ store.lock.Unlock()
+ }
+ store.librarians <- Librarian{}
+ }()
+
+ return librarian.request, librarian.response
+}
+
+func NewLibrary(librariansCount int) Library {
+ store := LibraryStore{
+ books: make(map[string][]Book),
+ available: make(map[string]int),
+ }
+
+ librarians := make(chan Librarian, librariansCount)
+ for i := 0; i < librariansCount; i++ {
+ librarians <- Librarian{store: store}
+ }
+
+ store.librarians = librarians
+ return store
+}