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  }