github.com/omniscale/go-osm@v0.3.1/parser/changeset/parser.go (about) 1 package changeset 2 3 import ( 4 "compress/gzip" 5 "context" 6 "encoding/xml" 7 "fmt" 8 "io" 9 10 "github.com/omniscale/go-osm" 11 "github.com/omniscale/go-osm/parser/changeset/internal/osmxml" 12 ) 13 14 type Parser struct { 15 reader io.Reader 16 conf Config 17 err error 18 } 19 20 type Config struct { 21 // Changesets specifies the destination for parsed changesets. 22 Changesets chan osm.Changeset 23 24 // KeepOpen specifies whether the destination channel should be keept open 25 // after Parse(). By default, the Changesets channel is closed after Parse(). 26 KeepOpen bool 27 } 28 29 // New creates a new parser for the provided input. Config specifies the destinations for the parsed changesets. 30 func New(r io.Reader, conf Config) *Parser { 31 return &Parser{reader: r, conf: conf} 32 } 33 34 // NewGZIP returns a parser from a GZIP compressed io.Reader 35 func NewGZIP(r io.Reader, conf Config) (*Parser, error) { 36 r, err := gzip.NewReader(r) 37 if err != nil { 38 return nil, err 39 } 40 return New(r, conf), nil 41 } 42 43 // Error returns the first error that occurred during Header/Parse calls. 44 func (p *Parser) Error() error { 45 return p.err 46 } 47 48 func (p *Parser) Parse(ctx context.Context) (err error) { 49 if p.err != nil { 50 return p.err 51 } 52 defer func() { 53 if err != nil { 54 p.err = err 55 } 56 }() 57 58 if !p.conf.KeepOpen { 59 defer func() { 60 if p.conf.Changesets != nil { 61 close(p.conf.Changesets) 62 } 63 }() 64 } 65 66 dec := xml.NewDecoder(p.reader) 67 cf := osmxml.ChangeFile{} 68 if err := dec.Decode(&cf); err != nil { 69 return fmt.Errorf("decoding changes file: %w", err) 70 } 71 72 for _, ch := range cf.Changes { 73 result := osm.Changeset{ 74 ID: ch.ID, 75 CreatedAt: ch.CreatedAt, 76 ClosedAt: ch.ClosedAt, 77 Open: ch.Open, 78 UserID: ch.UserID, 79 UserName: ch.UserName, 80 NumChanges: ch.NumChanges, 81 MaxExtent: [4]float64{ 82 ch.MinLon, 83 ch.MinLat, 84 ch.MaxLon, 85 ch.MaxLat, 86 }, 87 } 88 89 tags := make(osm.Tags, len(ch.Tags)) 90 for _, t := range ch.Tags { 91 tags[t.Key] = t.Value 92 } 93 result.Tags = tags 94 95 comment := make([]osm.Comment, len(ch.Comments)) 96 for i, t := range ch.Comments { 97 comment[i] = osm.Comment{ 98 UserID: t.UserID, 99 UserName: t.UserName, 100 CreatedAt: t.Date, 101 Text: t.Text, 102 } 103 } 104 result.Comments = comment 105 106 select { 107 case <-ctx.Done(): 108 case p.conf.Changesets <- result: 109 } 110 } 111 112 return nil 113 }