Станислав обнови решението на 30.11.2015 00:28 (преди над 2 години)
+package main
+
+import (
+ "encoding/json"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "sync"
+)
+
+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)
+}
+
+// Author is a representation of a book author.
+type Author struct {
+ FirstName string `json:"first_name" xml:"first_name"`
+ LastName string `json:"last_name" xml:"last_name"`
+}
+
+// String returns a string representation of the author.
+func (a *Author) String() string {
+ return a.FirstName + " " + a.LastName
+}
+
+// Book is a representation of a book.
+type Book struct {
+ ISBN string `json:"isbn" xml:"isbn"`
+ Title string `json:"title" xml:"title"`
+ Author *Author `json:"author" xml:"author"`
+ Ratings []int `json:"ratings" xml:"ratings"`
+
+ sync.Mutex
+ limit int
+ registered int
+ borrowed int
+}
+
+// Register increments the number of copies of this book in the library.
+func (b *Book) Register() {
+ b.Lock()
+ defer b.Unlock()
+
+ if b.registered < b.limit {
+ b.registered++
+ }
+}
+
+// Borrow decrements the number of available copies of this book in the library.
+func (b *Book) Borrow() error {
+ b.Lock()
+ defer b.Unlock()
+
+ var err error
+ if b.borrowed < b.registered {
+ b.borrowed++
+ } else {
+ err = errors.New("Няма наличност на книга " + b.ISBN)
+ }
+ return err
+}
+
+// Return increments the number of available copies of this book in the library.
+func (b *Book) Return() error {
+ b.Lock()
+ defer b.Unlock()
+
+ var err error
+ if b.borrowed > 0 {
+ b.borrowed--
+ } else {
+ err = errors.New("Всички копия са налични " + b.ISBN)
+ }
+ return err
+}
+
+// Registered returns the number of registered copies of this book in the library.
+func (b *Book) Registered() (int, error) {
+ b.Lock()
+ defer b.Unlock()
+
+ total := b.registered
+ if total < b.limit {
+ return total, nil
+ } else {
+ return total, errors.New("Има 4 копия на книга " + b.ISBN)
+ }
+}
+
+// Available returns the number of available copies of this book in the library.
+func (b *Book) Available() int {
+ b.Lock()
+ defer b.Unlock()
+
+ return b.registered - b.borrowed
+}
+
+// String returns a string representation of the book.
+func (b *Book) String() string {
+ return "[" + b.ISBN + "] " + b.Title + " от " + b.Author.String()
+}
+
+// ChannelLibraryResponse is a representation of a response to a library request.
+type ChannelLibraryResponse struct {
+ book *Book
+ err error
+ registered int
+ available int
+}
+
+// GetBook returns the target book of the library request and error if any occurs.
+func (mr *ChannelLibraryResponse) GetBook() (fmt.Stringer, error) {
+ return mr.book, mr.err
+}
+
+// GetAvailability returns the available and registered copies of the target book.
+func (mr *ChannelLibraryResponse) GetAvailability() (available int, registered int) {
+ return mr.available, mr.registered
+}
+
+// ChannelLibrary implements a library with librarians operating over a buffered channel.
+type ChannelLibrary struct {
+ books map[string]*Book
+
+ sync.Mutex
+ librarians chan<- func()
+}
+
+// AddBook registers a book copy in the library.
+// Returns the number of registered copies of the book and error if any occurs.
+func (cl *ChannelLibrary) AddBook(book *Book) (int, error) {
+ cl.Lock()
+ defer cl.Unlock()
+
+ if b, ok := cl.books[book.ISBN]; ok {
+ book = b
+ } else {
+ book.limit = 4
+ cl.books[book.ISBN] = book
+ }
+
+ book.Register()
+ return book.Registered()
+}
+
+// AddBookJSON registers a book copy in the library from its JSON representation.
+// Returns the number of registered copies of the book and error if any occurs.
+func (cl *ChannelLibrary) AddBookJSON(data []byte) (int, error) {
+ var book Book
+ json.Unmarshal(data, &book)
+ return cl.AddBook(&book)
+}
+
+// AddBookXML registers a book copy in the library from its XML representation.
+// Returns the number of registered copies of the book and error if any occurs.
+func (cl *ChannelLibrary) AddBookXML(data []byte) (int, error) {
+ var book Book
+ xml.Unmarshal(data, &book)
+ return cl.AddBook(&book)
+}
+
+// GetBook returns a book from the library by its ISBN number and error if any occurs.
+func (cl *ChannelLibrary) GetBook(isbn string) (*Book, error) {
+ cl.Lock()
+ defer cl.Unlock()
+
+ book, ok := cl.books[isbn]
+ if ok {
+ return book, nil
+ } else {
+ return nil, errors.New("Непозната книга " + isbn)
+ }
+}
+
+// Hello initiates communication with a librarian. Blocks if no librarian is free.
+// Returns requests and responses channels that are used for the communication.
+func (cl *ChannelLibrary) Hello() (chan<- LibraryRequest, <-chan LibraryResponse) {
+ requests := make(chan LibraryRequest)
+ responses := make(chan LibraryResponse)
+
+ cl.librarians <- func() {
+ for request := range requests {
+ book, err := cl.GetBook(request.GetISBN())
+ var registered, available int
+
+ if err == nil {
+ switch request.GetType() {
+ case 1:
+ err = book.Borrow()
+ case 2:
+ err = book.Return()
+ case 3:
+ }
+ registered, _ = book.Registered()
+ available = book.Available()
+ }
+ responses <- &ChannelLibraryResponse{book, err, registered, available}
+ }
+ }
+
+ return requests, responses
+}
+
+// NewLibrary initializes a new ChannelLibrary with specific number of librarians.
+func NewLibrary(librariansCount int) Library {
+ librarians := make(chan func(), librariansCount)
+
+ go func() {
+ for work := range librarians {
+ work()
+ }
+ }()
+
+ return &ChannelLibrary{
+ books: map[string]*Book{},
+ librarians: librarians,
+ }
+}