Даниел обнови решението на 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. Ще е добре да го оправиш ;)