...

Source file src/github.com/emicklei/proto/service.go

Documentation: github.com/emicklei/proto

     1  // Copyright (c) 2017 Ernest Micklei
     2  //
     3  // MIT License
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining
     6  // a copy of this software and associated documentation files (the
     7  // "Software"), to deal in the Software without restriction, including
     8  // without limitation the rights to use, copy, modify, merge, publish,
     9  // distribute, sublicense, and/or sell copies of the Software, and to
    10  // permit persons to whom the Software is furnished to do so, subject to
    11  // the following conditions:
    12  //
    13  // The above copyright notice and this permission notice shall be
    14  // included in all copies or substantial portions of the Software.
    15  //
    16  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    17  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    18  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    19  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    20  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    21  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    22  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    23  
    24  package proto
    25  
    26  import (
    27  	"text/scanner"
    28  )
    29  
    30  // Service defines a set of RPC calls.
    31  type Service struct {
    32  	Position scanner.Position
    33  	Comment  *Comment
    34  	Name     string
    35  	Elements []Visitee
    36  	Parent   Visitee
    37  }
    38  
    39  // Accept dispatches the call to the visitor.
    40  func (s *Service) Accept(v Visitor) {
    41  	v.VisitService(s)
    42  }
    43  
    44  // Doc is part of Documented
    45  func (s *Service) Doc() *Comment {
    46  	return s.Comment
    47  }
    48  
    49  // addElement is part of elementContainer
    50  func (s *Service) addElement(v Visitee) {
    51  	v.parent(s)
    52  	s.Elements = append(s.Elements, v)
    53  }
    54  
    55  // elements is part of elementContainer
    56  func (s *Service) elements() []Visitee {
    57  	return s.Elements
    58  }
    59  
    60  // takeLastComment is part of elementContainer
    61  // removes and returns the last elements of the list if it is a Comment.
    62  func (s *Service) takeLastComment(expectedOnLine int) (last *Comment) {
    63  	last, s.Elements = takeLastCommentIfEndsOnLine(s.Elements, expectedOnLine)
    64  	return
    65  }
    66  
    67  // parse continues after reading "service"
    68  func (s *Service) parse(p *Parser) error {
    69  	pos, tok, lit := p.nextIdentifier()
    70  	if tok != tIDENT {
    71  		if !isKeyword(tok) {
    72  			return p.unexpected(lit, "service identifier", s)
    73  		}
    74  	}
    75  	s.Name = lit
    76  	consumeCommentFor(p, s)
    77  	pos, tok, lit = p.next()
    78  	if tok != tLEFTCURLY {
    79  		return p.unexpected(lit, "service opening {", s)
    80  	}
    81  	for {
    82  		pos, tok, lit = p.next()
    83  		switch tok {
    84  		case tCOMMENT:
    85  			if com := mergeOrReturnComment(s.Elements, lit, pos); com != nil { // not merged?
    86  				s.addElement(com)
    87  			}
    88  		case tOPTION:
    89  			opt := new(Option)
    90  			opt.Position = pos
    91  			opt.Comment, s.Elements = takeLastCommentIfEndsOnLine(s.elements(), pos.Line-1)
    92  			if err := opt.parse(p); err != nil {
    93  				return err
    94  			}
    95  			s.addElement(opt)
    96  		case tRPC:
    97  			rpc := new(RPC)
    98  			rpc.Position = pos
    99  			rpc.Comment, s.Elements = takeLastCommentIfEndsOnLine(s.Elements, pos.Line-1)
   100  			err := rpc.parse(p)
   101  			if err != nil {
   102  				return err
   103  			}
   104  			s.addElement(rpc)
   105  			maybeScanInlineComment(p, s)
   106  		case tSEMICOLON:
   107  			maybeScanInlineComment(p, s)
   108  		case tRIGHTCURLY:
   109  			goto done
   110  		default:
   111  			return p.unexpected(lit, "service comment|rpc", s)
   112  		}
   113  	}
   114  done:
   115  	return nil
   116  }
   117  
   118  func (s *Service) parent(v Visitee) { s.Parent = v }
   119  
   120  // RPC represents an rpc entry in a message.
   121  type RPC struct {
   122  	Position       scanner.Position
   123  	Comment        *Comment
   124  	Name           string
   125  	RequestType    string
   126  	StreamsRequest bool
   127  	ReturnsType    string
   128  	StreamsReturns bool
   129  	Elements       []Visitee
   130  	InlineComment  *Comment
   131  	Parent         Visitee
   132  
   133  	// Options field is DEPRECATED, use Elements instead.
   134  	Options []*Option
   135  }
   136  
   137  // Accept dispatches the call to the visitor.
   138  func (r *RPC) Accept(v Visitor) {
   139  	v.VisitRPC(r)
   140  }
   141  
   142  // Doc is part of Documented
   143  func (r *RPC) Doc() *Comment {
   144  	return r.Comment
   145  }
   146  
   147  // inlineComment is part of commentInliner.
   148  func (r *RPC) inlineComment(c *Comment) {
   149  	r.InlineComment = c
   150  }
   151  
   152  // parse continues after reading "rpc"
   153  func (r *RPC) parse(p *Parser) error {
   154  	pos, tok, lit := p.next()
   155  	if tok != tIDENT {
   156  		return p.unexpected(lit, "rpc method", r)
   157  	}
   158  	r.Name = lit
   159  	pos, tok, lit = p.next()
   160  	if tok != tLEFTPAREN {
   161  		return p.unexpected(lit, "rpc type opening (", r)
   162  	}
   163  	pos, tok, lit = p.nextTypeName()
   164  	if tSTREAM == tok {
   165  		r.StreamsRequest = true
   166  		pos, tok, lit = p.nextTypeName()
   167  	}
   168  	if tok != tIDENT {
   169  		return p.unexpected(lit, "rpc stream | request type", r)
   170  	}
   171  	r.RequestType = lit
   172  	pos, tok, lit = p.next()
   173  	if tok != tRIGHTPAREN {
   174  		return p.unexpected(lit, "rpc type closing )", r)
   175  	}
   176  	pos, tok, lit = p.next()
   177  	if tok != tRETURNS {
   178  		return p.unexpected(lit, "rpc returns", r)
   179  	}
   180  	pos, tok, lit = p.next()
   181  	if tok != tLEFTPAREN {
   182  		return p.unexpected(lit, "rpc type opening (", r)
   183  	}
   184  	pos, tok, lit = p.nextTypeName()
   185  	if tSTREAM == tok {
   186  		r.StreamsReturns = true
   187  		pos, tok, lit = p.nextTypeName()
   188  	}
   189  	if tok != tIDENT {
   190  		return p.unexpected(lit, "rpc stream | returns type", r)
   191  	}
   192  	r.ReturnsType = lit
   193  	pos, tok, lit = p.next()
   194  	if tok != tRIGHTPAREN {
   195  		return p.unexpected(lit, "rpc type closing )", r)
   196  	}
   197  	pos, tok, lit = p.next()
   198  	if tSEMICOLON == tok {
   199  		p.nextPut(pos, tok, lit) // allow for inline comment parsing
   200  		return nil
   201  	}
   202  	if tLEFTCURLY == tok {
   203  		// parse options
   204  		for {
   205  			pos, tok, lit = p.next()
   206  			if tRIGHTCURLY == tok {
   207  				break
   208  			}
   209  			if isComment(lit) {
   210  				if com := mergeOrReturnComment(r.elements(), lit, pos); com != nil { // not merged?
   211  					r.addElement(com)
   212  					continue
   213  				}
   214  			}
   215  			if tSEMICOLON == tok {
   216  				maybeScanInlineComment(p, r)
   217  				continue
   218  			}
   219  			if tOPTION == tok {
   220  				o := new(Option)
   221  				o.Position = pos
   222  				if err := o.parse(p); err != nil {
   223  					return err
   224  				}
   225  				r.addElement(o)
   226  			}
   227  		}
   228  	}
   229  	return nil
   230  }
   231  
   232  // addElement is part of elementContainer
   233  func (r *RPC) addElement(v Visitee) {
   234  	v.parent(r)
   235  	r.Elements = append(r.Elements, v)
   236  	// handle deprecated field
   237  	if option, ok := v.(*Option); ok {
   238  		r.Options = append(r.Options, option)
   239  	}
   240  }
   241  
   242  // elements is part of elementContainer
   243  func (r *RPC) elements() []Visitee {
   244  	return r.Elements
   245  }
   246  
   247  func (r *RPC) takeLastComment(expectedOnLine int) (last *Comment) {
   248  	last, r.Elements = takeLastCommentIfEndsOnLine(r.Elements, expectedOnLine)
   249  	return
   250  }
   251  
   252  func (r *RPC) parent(v Visitee) { r.Parent = v }
   253  

View as plain text