package pokemonparsing import ( "sync" p "github.com/ajswis/go-pkparse-server/pokemon" "github.com/ajswis/go-pkparse-server/pokemon-parsing/gen7" ) // NOTE: For unit testing simplicity, perhaps defining a parsing interface and // wrapper type for all parsers would make isolating tests easier. // Parse returns either a parsed Pokemon with all applicable struct fields // populated, or an error detailing what was wrong. func Parse(rawPokemon p.RawPokemon) (*p.Pokemon, error) { // TODO: Conditionally swap to different generation parsers depending on given // byte slice. return gen7.Parse(rawPokemon) } // ParseAll returns either a slice of parsed Pokemon with all applicable struct // fields populated, or an error detailing what was wrong with any attempt. // // Additionally, ParseAll will parallelize large data sets. This concurrency // might be a preemptive optimization at the moment, but once parsers are // fully realized, benchmarks can determine the usefulness of parallelization. func ParseAll(rawPokemon []p.RawPokemon) ([]*p.Pokemon, error) { if len(rawPokemon) >= 10 { return concurrentParseAll(rawPokemon) } return serialParseAll(rawPokemon) } func serialParseAll(rawPokemon []p.RawPokemon) ([]*p.Pokemon, error) { var pkmn = []*p.Pokemon{} for _, p := range rawPokemon { parsed, err := Parse(p) if err != nil { return nil, err } pkmn = append(pkmn, parsed) } return pkmn, nil } type parseResult struct { pkmn *p.Pokemon err error } // concurrentParseAll fans out the data set to Parse in parallel, but does not // end early on a failure or cancelled request. Providing done channels can // resolve this, but it would probably be worth waiting until handling of // `context.Context`s is applied. func concurrentParseAll(rawPokemon []p.RawPokemon) ([]*p.Pokemon, error) { var wg sync.WaitGroup pkmnc := make(chan *parseResult) for _, pkmn := range rawPokemon { wg.Add(1) go func(unparsed p.RawPokemon) { defer wg.Done() res, err := Parse(unparsed) pkmnc <- &parseResult{pkmn: res, err: err} }(pkmn) } go func() { wg.Wait() close(pkmnc) }() var parsedPokemon []*p.Pokemon for res := range pkmnc { if res.err != nil { return nil, res.err } parsedPokemon = append(parsedPokemon, res.pkmn) } return parsedPokemon, nil }