github.com/sacloud/iaas-api-go@v1.12.0/internal/dsl/operation.go (about)

     1  // Copyright 2022-2023 The sacloud/iaas-api-go Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dsl
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  )
    21  
    22  // Operations リソースへの操作(スライス)
    23  type Operations []*Operation
    24  
    25  // Operation リソースへの操作
    26  type Operation struct {
    27  	ResourceName        string
    28  	Name                string        // 操作名、メソッド名となる
    29  	Method              string        // HTTPリクエストメソッド GET/POST/PUT/DELETE
    30  	PathFormat          string        // パスのフォーマット、省略した場合はDefaultPathFormatが設定される
    31  	Arguments           Arguments     // 引数の定義
    32  	Results             Results       // レスポンス
    33  	RequestEnvelope     *EnvelopeType // リクエスト時のエンベロープ
    34  	ResponseEnvelope    *EnvelopeType // レスポンス時のエンベロープ
    35  	UseWrappedResult    bool          // trueの場合APIからの戻り値としてラッパー型(xxxResult)を返す
    36  	LockLevel           LockLevel     // APIコール時のロックレベル
    37  	LockKeyCustomFormat string        // ロックキーのgoテンプレートフォーマット(PathFormatと同じパラメータが利用可能)
    38  }
    39  
    40  // LockKeyFormat ロックキーのフォーマット、ロックなしの場合空になる
    41  func (o *Operation) LockKeyFormat() string {
    42  	if o.LockLevel == LockLevelNone {
    43  		return ""
    44  	}
    45  	if o.LockKeyCustomFormat != "" {
    46  		return o.LockKeyCustomFormat
    47  	}
    48  	switch o.LockLevel {
    49  	case LockLevelResource:
    50  		return o.GetPathFormat()
    51  	case LockLevelAPI:
    52  		return fmt.Sprintf("%s.%s.%s", o.ResourceName, o.Name, o.Method)
    53  	case LockLevelGlobal:
    54  		return "GlobalLock"
    55  	default:
    56  		return ""
    57  	}
    58  }
    59  
    60  // GetPathFormat パスのフォーマット
    61  func (o *Operation) GetPathFormat() string {
    62  	if o.PathFormat != "" {
    63  		return o.PathFormat
    64  	}
    65  	return DefaultPathFormat
    66  }
    67  
    68  // ImportStatements コード生成時に利用するimport文を生成する
    69  func (o *Operation) ImportStatements(additionalImports ...string) []string {
    70  	ss := wrapByDoubleQuote(additionalImports...)
    71  
    72  	for _, arg := range o.Arguments {
    73  		ss = append(ss, arg.ImportStatements()...)
    74  	}
    75  
    76  	for _, m := range o.Results {
    77  		ss = append(ss, m.ImportStatements()...)
    78  	}
    79  
    80  	return uniqStrings(ss)
    81  }
    82  
    83  // MethodName コード生成時に利用する、メソッド名を出力する
    84  func (o *Operation) MethodName() string {
    85  	return o.Name
    86  }
    87  
    88  // ReturnErrorStatement コード生成時に利用する、エラーをreturnする文を生成する
    89  func (o *Operation) ReturnErrorStatement() string {
    90  	if o.HasResults() {
    91  		if o.UseWrappedResult {
    92  			return "nil, err"
    93  		}
    94  		var ret string
    95  		for range o.Results {
    96  			ret += "nil,"
    97  		}
    98  		ret += "err"
    99  		return ret
   100  	}
   101  	return "err"
   102  }
   103  
   104  // ReturnStatement コード生成時に利用するreturn部分を生成する
   105  func (o *Operation) ReturnStatement() string {
   106  	if !o.HasResults() {
   107  		return "err"
   108  	}
   109  	if o.UseWrappedResult {
   110  		return "results, err"
   111  	}
   112  	var ret string
   113  	for _, r := range o.Results {
   114  		ret += fmt.Sprintf("results.%s,", r.DestField)
   115  	}
   116  	ret += "nil"
   117  	return ret
   118  }
   119  
   120  // ResultsStatement 戻り値定義部のソースを出力
   121  func (o *Operation) ResultsStatement() string {
   122  	if !o.HasResults() {
   123  		return "error"
   124  	}
   125  	if o.UseWrappedResult {
   126  		return fmt.Sprintf("(%s, error)", o.resultType().GoTypeSourceCode())
   127  	}
   128  	var ret string
   129  	for _, r := range o.Results {
   130  		ret += r.GoTypeSourceCode() + ","
   131  	}
   132  	return fmt.Sprintf("(%s error)", ret)
   133  }
   134  
   135  // ResultsTypeInfo 戻り値の型情報(error型を含まない)
   136  func (o *Operation) ResultsTypeInfo() []*ResultTypeInfo {
   137  	var info []*ResultTypeInfo
   138  	if !o.HasResults() {
   139  		return info
   140  	}
   141  	if o.UseWrappedResult {
   142  		info = append(info, &ResultTypeInfo{
   143  			VarName:   "result",
   144  			FieldName: "Result",
   145  			Type:      o.resultType().Type(),
   146  		})
   147  		return info
   148  	}
   149  	for _, r := range o.Results {
   150  		info = append(info, &ResultTypeInfo{
   151  			VarName:   "result" + r.DestField,
   152  			FieldName: r.DestField,
   153  			Type:      r.Type(),
   154  		})
   155  	}
   156  	return info
   157  }
   158  
   159  // ResultsAssignStatement API戻り値を変数にアサインする場合の変数部分のソースを出力、主にtraceで利用する
   160  func (o *Operation) ResultsAssignStatement() string {
   161  	if !o.HasResults() {
   162  		return "err"
   163  	}
   164  	if o.UseWrappedResult {
   165  		return "result, err"
   166  	}
   167  	var ret string
   168  	for _, r := range o.Results {
   169  		ret += fmt.Sprintf(",result%s", r.DestField)
   170  	}
   171  	return fmt.Sprintf("%s, err", ret)
   172  }
   173  
   174  // RequestEnvelopeStructName エンベロープのstruct名(camel-case)
   175  func (o *Operation) RequestEnvelopeStructName() string {
   176  	return fmt.Sprintf("%s%sRequestEnvelope", firstRuneToLower(o.ResourceName), o.Name)
   177  }
   178  
   179  // ResponseEnvelopeStructName エンベロープのstruct名(camel-case)
   180  func (o *Operation) ResponseEnvelopeStructName() string {
   181  	return fmt.Sprintf("%s%sResponseEnvelope", firstRuneToLower(o.ResourceName), o.Name)
   182  }
   183  
   184  // ResultTypeName API戻り値の型名
   185  func (o *Operation) ResultTypeName() string {
   186  	if o.UseWrappedResult {
   187  		return o.resultType().GoType()
   188  	}
   189  	return firstRuneToLower(o.resultType().GoType())
   190  }
   191  
   192  // HasResults 戻り値が定義されているかを取得
   193  func (o *Operation) HasResults() bool {
   194  	return len(o.Results) > 0
   195  }
   196  
   197  // StubFieldDefines スタブ生成時のフィールド定義文を全フィールド分出力
   198  func (o *Operation) StubFieldDefines() []string {
   199  	if !o.HasResults() {
   200  		return nil
   201  	}
   202  	if o.UseWrappedResult {
   203  		return []string{fmt.Sprintf("Values %s", o.resultType().GoTypeSourceCode())}
   204  	}
   205  	var rets []string
   206  	for _, r := range o.Results {
   207  		rets = append(rets, fmt.Sprintf("%s %s", r.DestField, r.GoTypeSourceCode()))
   208  	}
   209  	return rets
   210  }
   211  
   212  // StubReturnStatement スタブ生成時のreturn文
   213  func (o *Operation) StubReturnStatement(receiverName string) string {
   214  	if !o.HasResults() {
   215  		return fmt.Sprintf("return %s.%sStubResult.Err", receiverName, o.MethodName())
   216  	}
   217  	var rets []string
   218  	if o.UseWrappedResult {
   219  		rets = append(rets, fmt.Sprintf("%s.%sStubResult.Values", receiverName, o.MethodName()))
   220  	} else {
   221  		for _, r := range o.Results {
   222  			rets = append(rets, fmt.Sprintf("%s.%sStubResult.%s", receiverName, o.MethodName(), r.DestField))
   223  		}
   224  	}
   225  
   226  	rets = append(rets, fmt.Sprintf("%s.%sStubResult.Err", receiverName, o.MethodName()))
   227  	return fmt.Sprintf("return %s", strings.Join(rets, ","))
   228  }
   229  
   230  // Models オペレーション配下の(Nameで)ユニークなモデル一覧を取得
   231  func (o *Operation) Models() Models {
   232  	ms := o.Results.Models()
   233  	for _, arg := range o.Arguments {
   234  		m, ok := arg.Type.(*Model)
   235  		if ok {
   236  			ms = append(ms, m)
   237  			ms = append(ms, m.FieldModels()...)
   238  		}
   239  	}
   240  	return ms.UniqByName()
   241  }
   242  
   243  // HasRequestEnvelope リクエストエンベロープが設定されているか
   244  func (o *Operation) HasRequestEnvelope() bool {
   245  	return o.RequestEnvelope != nil
   246  }
   247  
   248  // RequestPayloads リクエストペイロードを取得
   249  func (o *Operation) RequestPayloads() []*EnvelopePayloadDesc {
   250  	if o.HasRequestEnvelope() {
   251  		return o.RequestEnvelope.Payloads
   252  	}
   253  	return nil
   254  }
   255  
   256  // HasResponseEnvelope レスポンスエンベロープが設定されているか
   257  func (o *Operation) HasResponseEnvelope() bool {
   258  	return o.ResponseEnvelope != nil
   259  }
   260  
   261  // ResponsePayloads レスポンスペイロードを取得
   262  func (o *Operation) ResponsePayloads() []*EnvelopePayloadDesc {
   263  	if o.HasResponseEnvelope() {
   264  		return o.ResponseEnvelope.Payloads
   265  	}
   266  	return nil
   267  }
   268  
   269  // IsRequestSingular リクエストが単数系か
   270  func (o *Operation) IsRequestSingular() bool {
   271  	if o.HasRequestEnvelope() {
   272  		return o.RequestEnvelope.IsSingular()
   273  	}
   274  	return false
   275  }
   276  
   277  // IsRequestPlural リクエストが複数形か
   278  func (o *Operation) IsRequestPlural() bool {
   279  	if o.HasRequestEnvelope() {
   280  		return o.RequestEnvelope.IsPlural()
   281  	}
   282  	return false
   283  }
   284  
   285  // IsResponseSingular レスポンスが単数系か
   286  func (o *Operation) IsResponseSingular() bool {
   287  	if o.HasResponseEnvelope() {
   288  		return o.ResponseEnvelope.IsSingular()
   289  	}
   290  	return false
   291  }
   292  
   293  // IsResponsePlural レスポンスが複数形か
   294  func (o *Operation) IsResponsePlural() bool {
   295  	if o.HasResponseEnvelope() {
   296  		return o.ResponseEnvelope.IsPlural()
   297  	}
   298  	return false
   299  }
   300  
   301  func (o *Operation) resultType() *ResultType {
   302  	return &ResultType{
   303  		resourceName: o.ResourceName,
   304  		operation:    o,
   305  		results:      o.Results,
   306  	}
   307  }