parse.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package pokemonparsing
  2. import (
  3. "sync"
  4. p "github.com/ajswis/go-pkparse-server/pokemon"
  5. "github.com/ajswis/go-pkparse-server/pokemon-parsing/gen7"
  6. )
  7. // NOTE: For unit testing simplicity, perhaps defining a parsing interface and
  8. // wrapper type for all parsers would make isolating tests easier.
  9. // Parse returns either a parsed Pokemon with all applicable struct fields
  10. // populated, or an error detailing what was wrong.
  11. func Parse(rawPokemon p.RawPokemon) (*p.Pokemon, error) {
  12. // TODO: Conditionally swap to different generation parsers depending on given
  13. // byte slice.
  14. return gen7.Parse(rawPokemon)
  15. }
  16. // ParseAll returns either a slice of parsed Pokemon with all applicable struct
  17. // fields populated, or an error detailing what was wrong with any attempt.
  18. //
  19. // Additionally, ParseAll will parallelize large data sets. This concurrency
  20. // might be a preemptive optimization at the moment, but once parsers are
  21. // fully realized, benchmarks can determine the usefulness of parallelization.
  22. func ParseAll(rawPokemon []p.RawPokemon) ([]*p.Pokemon, error) {
  23. if len(rawPokemon) >= 10 {
  24. return concurrentParseAll(rawPokemon)
  25. }
  26. return serialParseAll(rawPokemon)
  27. }
  28. func serialParseAll(rawPokemon []p.RawPokemon) ([]*p.Pokemon, error) {
  29. var pkmn = []*p.Pokemon{}
  30. for _, p := range rawPokemon {
  31. parsed, err := Parse(p)
  32. if err != nil {
  33. return nil, err
  34. }
  35. pkmn = append(pkmn, parsed)
  36. }
  37. return pkmn, nil
  38. }
  39. type parseResult struct {
  40. pkmn *p.Pokemon
  41. err error
  42. }
  43. // concurrentParseAll fans out the data set to Parse in parallel, but does not
  44. // end early on a failure or cancelled request. Providing done channels can
  45. // resolve this, but it would probably be worth waiting until handling of
  46. // `context.Context`s is applied.
  47. func concurrentParseAll(rawPokemon []p.RawPokemon) ([]*p.Pokemon, error) {
  48. var wg sync.WaitGroup
  49. pkmnc := make(chan *parseResult)
  50. for _, pkmn := range rawPokemon {
  51. wg.Add(1)
  52. go func(unparsed p.RawPokemon) {
  53. defer wg.Done()
  54. res, err := Parse(unparsed)
  55. pkmnc <- &parseResult{pkmn: res, err: err}
  56. }(pkmn)
  57. }
  58. go func() {
  59. wg.Wait()
  60. close(pkmnc)
  61. }()
  62. var parsedPokemon []*p.Pokemon
  63. for res := range pkmnc {
  64. if res.err != nil {
  65. return nil, res.err
  66. }
  67. parsedPokemon = append(parsedPokemon, res.pkmn)
  68. }
  69. return parsedPokemon, nil
  70. }