Ралица обнови решението на 06.12.2015 17:58 (преди над 2 години)
+package main
+
+import (
+ "encoding/json"
+ "encoding/xml"
+ "fmt"
+ "sync"
+)
+
+type Library interface {
+ AddBookJSON(data []byte) (int, error)
+ AddBookXML(data []byte) (int, error)
+ Hello() (chan<- LibraryRequest, <-chan LibraryResponse)
+}
+
+type LibraryRequest interface {
+ GetType() int
+ GetISBN() string
+}
+
+type LibraryResponse interface {
+ GetBook() (fmt.Stringer, error)
+ GetAvailability() (available int, registered int)
+}
+
+type DogeLibrarySizeError struct {
+ isbn string
+}
+
+func (d DogeLibrarySizeError) Error() string {
+ return "Има 4 копия на книга " + d.isbn
+}
+
+type AuthorType struct {
+ First_name string `xml:"first_name"`
+ Last_name string `xml:"last_name"`
+}
+
+type Book struct {
+ XMLName xml.Name `xml:"book"`
+ Isbn string `xml:"isbn,attr"`
+ Title string `xml:"title"`
+ Author AuthorType `xml:"author"`
+ Ratings []int `xml:"ratings>rating"`
+ Pages int `xml:"pages"`
+ Genre string `xml:"genre"`
+}
+
+func (b Book) String() string {
+ var (
+ isbn = b.Isbn
+ title = b.Title
+ firstNameOfAuthor = b.Author.First_name
+ lastNameOfAuthor = b.Author.Last_name
+ )
+
+ return "[" + isbn + "] " + title + " от " + firstNameOfAuthor + " " + lastNameOfAuthor
+}
+
+type BookItem struct {
+ book *Book
+ count int
+ registered int
+}
+
+type DogeLibrary struct {
+ books map[string]*BookItem
+ librarians int
+ sem chan struct{}
+ mutex sync.RWMutex
+}
+
+func (d *DogeLibrary) AddBookJSON(data []byte) (int, error) {
+ var (
+ book Book
+ unmarshalError error
+ )
+
+ unmarshalError = json.Unmarshal(data, &book)
+ if unmarshalError != nil {
+ panic(fmt.Sprintf("%v", unmarshalError))
+ }
+
+ var (
+ copiesCount = d.getAvailable(book.Isbn)
+ sizeError *DogeLibrarySizeError
+ )
+
+ if copiesCount >= 4 {
+ sizeError = &DogeLibrarySizeError{isbn: book.Isbn}
+ } else {
+ d.addBook(book)
+ }
+
+ return copiesCount, sizeError
+}
+
+func (d *DogeLibrary) AddBookXML(data []byte) (int, error) {
+ var (
+ book Book
+ unmarshalError error
+ )
+
+ unmarshalError = xml.Unmarshal(data, &book)
+ if unmarshalError != nil {
+ panic(fmt.Sprintf("%v", unmarshalError))
+ }
+
+ var (
+ copiesCount = d.getAvailable(book.Isbn)
+ sizeError *DogeLibrarySizeError
+ )
+
+ if copiesCount >= 4 {
+ sizeError = &DogeLibrarySizeError{isbn: book.Isbn}
+ } else {
+ d.addBook(book)
+ }
+
+ return copiesCount, sizeError
+}
+
+func NewLibrary(librarians int) Library {
+
+ var d = new(DogeLibrary)
+ d.books = make(map[string]*BookItem)
+ d.librarians = librarians
+ d.sem = make(chan struct{}, d.librarians)
+
+ for i := 0; i < d.librarians; i++ {
+ d.sem <- struct{}{}
+ }
+
+ return d
+}
+
+func (d *DogeLibrary) getAvailable(isbn string) int {
+ if d.exists(isbn) {
+ var bookItem = d.books[isbn]
+ return bookItem.count
+ } else {
+ return 0
+ }
+}
+
+func (d *DogeLibrary) getRegistered(isbn string) int {
+ if d.exists(isbn) {
+ var bookItem = d.books[isbn]
+ return bookItem.registered
+ } else {
+ return 0
+ }
+}
+
+func (d *DogeLibrary) exists(isbn string) bool {
+ var _, exists = d.books[isbn]
+ return exists
+}
+
+func (d *DogeLibrary) addBook(b Book) bool {
+ if d.exists(b.Isbn) {
+ var bookItem = d.books[b.Isbn]
+ if bookItem.registered+1 > 4 {
+ return false
+ } else {
+ bookItem.count++
+ bookItem.registered++
+ }
+ } else {
+ d.books[b.Isbn] = &BookItem{book: &b, count: 1, registered: 1}
+ }
+
+ return true
+}
+
+func (d *DogeLibrary) removeBook(isbn string) (*Book, bool) {
+ if d.exists(isbn) {
+ var bookItem = d.books[isbn]
+ bookItem.count--
+ return bookItem.book, true
+ } else {
+ return nil, false
+ }
+}
+
+type ErrorUnknownBook struct {
+ message string
+}
+
+func (e *ErrorUnknownBook) Error() string {
+ return e.message
+}
+
+func (d *DogeLibrary) Hello() (chan<- LibraryRequest, <-chan LibraryResponse) {
+
+ <-d.sem
+
+ var libraryRequestChannel = make(chan LibraryRequest)
+ var libraryResponseChannel = make(chan LibraryResponse, 1)
+
+ go func() {
+ for request := range libraryRequestChannel {
+
+ var requestType = request.GetType()
+ var isbn = request.GetISBN()
+
+ var response = new(DogeLibraryResponse)
+
+ d.mutex.Lock()
+ if d.exists(isbn) {
+ response.registered = d.getRegistered(isbn)
+ response.available = d.getAvailable(isbn)
+ response.error = nil
+ } else {
+ response.registered = 0
+ response.available = 0
+ response.error = &ErrorUnknownBook{message: "Непозната книга " + isbn}
+ }
+
+ if requestType == 1 {
+ var book, _ = d.removeBook(isbn)
+ response.book = book
+ } else if requestType == 2 {
+ //~ d.returnBook(isbn)
+ response.book = nil
+ } else if requestType == 3 {
+ //
+ }
+
+ d.mutex.Unlock()
+
+ libraryResponseChannel <- response
+ }
+
+ d.sem <- struct{}{}
+ }()
+
+ return libraryRequestChannel, libraryResponseChannel
+}
+
+type DogeLibraryResponse struct {
+ registered int
+ available int
+ book *Book
+ error error
+}
+
+func (d *DogeLibraryResponse) GetBook() (fmt.Stringer, error) {
+ return d.book, d.error
+}
+
+func (d *DogeLibraryResponse) GetAvailability() (available int, registered int) {
+ return d.available, d.registered
+}
+
+func Dump(d *DogeLibrary) {
+ for _, book := range d.books {
+ fmt.Println(book)
+ }
+}
+
+func main() {
+ var json = []byte(`{
+ "isbn": "0954540018",
+ "title": "Who Said the Race is Over?",
+ "author": {
+ "first_name": "Anno",
+ "last_name": "Birkin"
+ },
+ "genre": "poetry",
+ "pages": 80,
+ "ratings": [5, 4, 4, 5, 3]
+ }`)
+
+ var xml = []byte(`
+ <book isbn="0954540018">
+ <title>Who said the race is Over?</title>
+ <author>
+ <first_name>Anno</first_name>
+ <last_name>Birkin</last_name>
+ </author>
+ <genre>poetry</genre>
+ <pages>80</pages>
+ <ratings>
+ <rating>5</rating>
+ <rating>4</rating>
+ <rating>4</rating>
+ <rating>5</rating>
+ <rating>3</rating>
+ </ratings>
+ </book>
+ `)
+
+ var lib = NewLibrary(16)
+
+ lib.AddBookJSON(json)
+ lib.AddBookXML(xml)
+
+ Dump(lib.(*DogeLibrary))
+}