github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/cmd/generates/sources/annotations.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package sources
    19  
    20  import (
    21  	"bufio"
    22  	"bytes"
    23  	"fmt"
    24  	"github.com/aacfactory/errors"
    25  	"io"
    26  	"strings"
    27  )
    28  
    29  func NewAnnotation(name string, params ...string) Annotation {
    30  	var pp []string
    31  	if len(params) > 0 {
    32  		pp = params
    33  	}
    34  	return Annotation{
    35  		Name:   name,
    36  		Params: pp,
    37  	}
    38  }
    39  
    40  type Annotation struct {
    41  	Name   string
    42  	Params []string
    43  }
    44  
    45  type Annotations []Annotation
    46  
    47  func (annotations *Annotations) Get(name string) (annotation Annotation, has bool) {
    48  	ss := *annotations
    49  	for _, target := range ss {
    50  		if target.Name == name {
    51  			annotation = target
    52  			has = true
    53  			return
    54  		}
    55  	}
    56  	return
    57  }
    58  
    59  func (annotations *Annotations) FirstParam(name string) (value string, has bool) {
    60  	ss := *annotations
    61  	for _, target := range ss {
    62  		if target.Name == name {
    63  			if len(target.Params) > 0 {
    64  				value = target.Params[0]
    65  				has = true
    66  			}
    67  			return
    68  		}
    69  	}
    70  	return
    71  }
    72  
    73  func (annotations *Annotations) Value(name string) (value string, has bool) {
    74  	ss := *annotations
    75  	for _, target := range ss {
    76  		if target.Name == name {
    77  			if len(target.Params) > 0 {
    78  				for _, param := range target.Params {
    79  					value = value + " " + param
    80  				}
    81  				value = strings.TrimSpace(value)
    82  				has = true
    83  			}
    84  			return
    85  		}
    86  	}
    87  	return
    88  }
    89  
    90  func (annotations *Annotations) Add(name string, param string) {
    91  	ss := *annotations
    92  	for i, s := range ss {
    93  		if s.Name == name {
    94  			if param == "" {
    95  				return
    96  			}
    97  			s.Params = append(s.Params, param)
    98  			ss[i] = s
    99  			*annotations = ss
   100  			return
   101  		}
   102  	}
   103  	params := make([]string, 0, 1)
   104  	if param != "" {
   105  		params = append(params, param)
   106  	}
   107  	ss = append(ss, Annotation{
   108  		Name:   name,
   109  		Params: params,
   110  	})
   111  	*annotations = ss
   112  }
   113  
   114  func (annotations *Annotations) Set(name string, param string) {
   115  	if param != "" {
   116  		param, _ = strings.CutSuffix(param, "\n")
   117  		param = strings.ReplaceAll(param, "'>>>'", ">>>")
   118  		param = strings.ReplaceAll(param, "'<<<'", "<<<")
   119  		param = strings.TrimSpace(param)
   120  	}
   121  	ss := *annotations
   122  	for i, s := range ss {
   123  		if s.Name == name {
   124  			params := make([]string, 0, 1)
   125  			if param != "" {
   126  				params = append(params, param)
   127  			}
   128  			s.Params = params
   129  			ss[i] = s
   130  			*annotations = ss
   131  			return
   132  		}
   133  	}
   134  	params := make([]string, 0, 1)
   135  	if param != "" {
   136  		params = append(params, param)
   137  	}
   138  	ss = append(ss, Annotation{
   139  		Name:   name,
   140  		Params: params,
   141  	})
   142  	*annotations = ss
   143  }
   144  
   145  func ParseAnnotations(s string) (annotations Annotations, err error) {
   146  	annotations = make(Annotations, 0, 1)
   147  	if s == "" || !strings.Contains(s, "@") {
   148  		return
   149  	}
   150  	reader := bufio.NewReader(bytes.NewReader([]byte(s)))
   151  	for {
   152  		line, _, readErr := reader.ReadLine()
   153  		if readErr != nil {
   154  			if readErr == io.EOF {
   155  				break
   156  			}
   157  			err = errors.Warning("sources: parse annotations failed").WithCause(readErr).WithMeta("source", s)
   158  			return
   159  		}
   160  		if len(line) == 0 {
   161  			continue
   162  		}
   163  		line = bytes.TrimSpace(line)
   164  		if len(line) == 0 {
   165  			continue
   166  		}
   167  		if line[0] != '@' {
   168  			continue
   169  		}
   170  		name := ""
   171  
   172  		content := line[1:]
   173  		paramsIdx := bytes.IndexByte(content, ' ')
   174  		if paramsIdx < 1 {
   175  			// no params
   176  			name = string(content)
   177  			_, has := annotations.Get(name)
   178  			if has {
   179  				err = errors.Warning("sources: parse annotations failed").WithCause(fmt.Errorf("@%s is duplicated", name)).WithMeta("source", s)
   180  				return
   181  			}
   182  			annotations.Set(name, "")
   183  			continue
   184  		} else {
   185  			name = strings.TrimSpace(string(content[0:paramsIdx]))
   186  			_, has := annotations.Get(name)
   187  			if has {
   188  				err = errors.Warning("sources: parse annotations failed").WithCause(fmt.Errorf("@%s is duplicated", name)).WithMeta("source", s)
   189  				return
   190  			}
   191  			content = bytes.TrimSpace(content[paramsIdx:])
   192  			if bytes.Index(content, []byte(">>>")) == 0 {
   193  				// block
   194  				block := bytes.NewBuffer(make([]byte, 0, 1))
   195  				block.Write([]byte{'\n'})
   196  				content = bytes.TrimSpace(content[3:])
   197  				if endIdx := bytes.Index(content, []byte("<<<")); endIdx > -1 {
   198  					content = bytes.TrimSpace(content[0:endIdx])
   199  					block.Write(content)
   200  					continue
   201  				}
   202  				if len(content) > 0 {
   203  					block.Write(content)
   204  				}
   205  				for {
   206  					line, _, readErr = reader.ReadLine()
   207  					if readErr != nil {
   208  						err = errors.Warning("sources: parse annotations failed").WithCause(fmt.Errorf("@%s is incompleted", name)).WithMeta("source", s)
   209  						return
   210  					}
   211  					content = bytes.TrimSpace(line)
   212  					if len(content) == 0 {
   213  						block.Write([]byte{'\n'})
   214  						continue
   215  					}
   216  					if content[0] == '@' {
   217  						err = errors.Warning("sources: parse annotations failed").WithCause(fmt.Errorf("@%s is incompleted", name)).WithMeta("source", s)
   218  						return
   219  					}
   220  					if bytes.Index(content, []byte("<<<")) > -1 {
   221  						content, _ = bytes.CutSuffix(content, []byte("<<<"))
   222  						content = bytes.TrimSpace(content)
   223  						block.Write([]byte{'\n'})
   224  						block.Write(content)
   225  						break
   226  					}
   227  					block.Write([]byte{'\n'})
   228  					block.Write(content)
   229  				}
   230  				annotations.Set(name, block.String()[1:])
   231  			} else {
   232  				params := strings.Split(string(content), " ")
   233  				for _, param := range params {
   234  					param = strings.TrimSpace(param)
   235  					annotations.Add(name, param)
   236  				}
   237  			}
   238  		}
   239  	}
   240  	return
   241  }