github.com/mailru/activerecord@v1.12.2/internal/pkg/checker/checker.go (about) 1 package checker 2 3 import ( 4 "log" 5 "strconv" 6 7 "github.com/mailru/activerecord/internal/pkg/arerror" 8 "github.com/mailru/activerecord/internal/pkg/ds" 9 "github.com/mailru/activerecord/pkg/octopus" 10 ) 11 12 // Checker структура описывающая checker 13 type Checker struct { 14 files map[string]*ds.RecordPackage 15 } 16 17 // Init конструктор checker-а 18 func Init(files map[string]*ds.RecordPackage) *Checker { 19 checker := Checker{ 20 files: files, 21 } 22 23 return &checker 24 } 25 26 // checkBackend проверка на указание бекенда 27 // В данный момент поддерживается один и толлько один бекенд 28 func checkBackend(cl *ds.RecordPackage) error { 29 if len(cl.Backends) == 0 { 30 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckBackendEmpty} 31 } 32 33 if len(cl.Backends) > 1 { 34 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckPkgBackendToMatch} 35 } 36 37 return nil 38 } 39 40 // checkLinkedObject проверка существования сущностей на которые ссылаются другие сущности 41 func checkLinkedObject(cl *ds.RecordPackage, linkedObjects map[string]string) error { 42 for _, fobj := range cl.FieldsObjectMap { 43 if _, ok := linkedObjects[fobj.ObjectName]; !ok { 44 return &arerror.ErrCheckPackageLinkedDecl{Pkg: cl.Namespace.PackageName, Object: fobj.ObjectName, Err: arerror.ErrCheckObjectNotFound} 45 } 46 } 47 48 return nil 49 } 50 51 // checkNamespace проверка правильного описания неймспейса у сущности 52 func checkNamespace(ns ds.NamespaceDeclaration) error { 53 if ns.PackageName == "" || ns.PublicName == "" { 54 return &arerror.ErrCheckPackageNamespaceDecl{Pkg: ns.PackageName, Name: ns.PublicName, Err: arerror.ErrCheckEmptyNamespace} 55 } 56 57 return nil 58 } 59 60 // checkFields функция проверки правильности описания полей структуры 61 // - указан допустимый тип полей 62 // - описаны все необходимые сериализаторы для полей с сериализацией 63 // - поля с мутаторами не могут быть праймари клчом 64 // - поля с мутаторами не могут быть сериализованными 65 // - поля с мутаторами не могут являться ссылками на другие сущности 66 // - сериализуемые поля не могут быть ссылками на другие сущности 67 // - есть первичный ключ 68 // - имена сущностей на которые ссылаемся на могут пересекаться с именами полей 69 // 70 //nolint:gocognit,gocyclo 71 func checkFields(cl *ds.RecordPackage) error { 72 if len(cl.Fields) > 0 && len(cl.ProcOutFields) > 0 { 73 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsManyDecl} 74 } 75 76 if !cl.ProcOutFields.Validate() { 77 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsOrderDecl} 78 } 79 80 primaryFound := false 81 82 octopusAvailFormat := map[octopus.Format]bool{} 83 for _, form := range octopus.AllFormat { 84 octopusAvailFormat[form] = true 85 } 86 87 for _, fld := range cl.Fields { 88 if _, ex := octopusAvailFormat[fld.Format]; !ex { 89 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} 90 } 91 92 if len(fld.Serializer) > 0 { 93 if _, ex := cl.SerializerMap[fld.Serializer[0]]; len(cl.SerializerMap) == 0 || !ex { 94 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotFound} 95 } 96 97 } 98 99 customMutCnt := 0 100 if len(fld.Mutators) > 0 { 101 fieldMutatorsChecker := ds.GetFieldMutatorsChecker() 102 103 for _, m := range fld.Mutators { 104 _, ex := fieldMutatorsChecker[m] 105 106 md, ok := cl.MutatorMap[m] 107 if ok { 108 customMutCnt++ 109 if customMutCnt > 1 { 110 return &arerror.ErrCheckPackageFieldMutatorDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Mutator: m, Err: arerror.ErrParseFieldMutatorInvalid} 111 } 112 } 113 114 if !ok && !ex { 115 return &arerror.ErrCheckPackageFieldMutatorDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Mutator: m, Err: arerror.ErrParseFieldMutatorInvalid} 116 } 117 118 if len(md.PartialFields) > 0 && len(fld.Serializer) == 0 { 119 return &arerror.ErrCheckPackageFieldMutatorDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Mutator: m, Err: arerror.ErrParseFieldMutatorTypeHasNotSerializer} 120 } 121 } 122 123 if fld.PrimaryKey { 124 return &arerror.ErrCheckPackageFieldMutatorDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Mutator: string(fld.Mutators[0]), Err: arerror.ErrCheckFieldMutatorConflictPK} 125 } 126 127 if fld.ObjectLink != "" { 128 return &arerror.ErrCheckPackageFieldMutatorDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Mutator: string(fld.Mutators[0]), Err: arerror.ErrCheckFieldMutatorConflictObject} 129 } 130 } 131 132 if len(fld.Serializer) > 0 && fld.ObjectLink != "" { 133 return &arerror.ErrCheckPackageFieldMutatorDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerConflictObject} 134 } 135 136 if fo, ex := cl.FieldsObjectMap[fld.Name]; ex { 137 return &arerror.ErrParseTypeFieldStructDecl{Name: fo.Name, Err: arerror.ErrRedefined} 138 } 139 140 if fld.PrimaryKey { 141 primaryFound = true 142 } 143 } 144 145 for _, fld := range cl.ProcOutFields.List() { 146 if _, ex := octopusAvailFormat[fld.Format]; !ex { 147 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} 148 } 149 150 if len(fld.Serializer) > 0 { 151 if _, ex := cl.SerializerMap[fld.Serializer[0]]; !ex { 152 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotFound} 153 } 154 } 155 156 if fld.Type == 0 { 157 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldTypeNotFound} 158 } 159 } 160 161 octopusProcAvailFormat := map[octopus.Format]bool{} 162 for _, form := range octopus.AllProcFormat { 163 octopusProcAvailFormat[form] = true 164 } 165 166 for _, fld := range cl.ProcInFields { 167 if _, ex := octopusProcAvailFormat[fld.Format]; !ex { 168 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} 169 } 170 171 if len(fld.Serializer) > 0 { 172 if _, ex := cl.SerializerMap[fld.Serializer[0]]; !ex { 173 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotFound} 174 } 175 } 176 177 if fld.Type == 0 { 178 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldTypeNotFound} 179 } 180 } 181 182 if len(cl.Fields) == 0 && len(cl.ProcOutFields) == 0 { 183 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsEmpty} 184 } 185 186 if len(cl.Fields) > 0 && !primaryFound { 187 return &arerror.ErrCheckPackageIndexDecl{Pkg: cl.Namespace.PackageName, Index: "primary", Err: arerror.ErrIndexNotExist} 188 } 189 190 return nil 191 } 192 193 // Check основная функция, которая запускает процесс проверки 194 // Должна вызываться только после окончания процесса парсинга всех деклараций 195 func Check(files map[string]*ds.RecordPackage, linkedObjects map[string]string) error { 196 for _, cl := range files { 197 if err := checkBackend(cl); err != nil { 198 return err 199 } 200 201 if err := checkNamespace(cl.Namespace); err != nil { 202 return err 203 } 204 205 if err := checkLinkedObject(cl, linkedObjects); err != nil { 206 return err 207 } 208 209 if err := checkFields(cl); err != nil { 210 return err 211 } 212 213 // Бекендозависимые проверки 214 for _, backend := range cl.Backends { 215 switch backend { 216 case "tarantool16": 217 fallthrough 218 case "tarantool2": 219 if err := checkTarantool(cl); err != nil { 220 return err 221 } 222 case "tarantool15": 223 fallthrough 224 case "octopus": 225 if err := checkOctopus(cl); err != nil { 226 return err 227 } 228 default: 229 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Backend: backend, Err: arerror.ErrCheckBackendUnknown} 230 } 231 } 232 } 233 234 return nil 235 } 236 237 //nolint:gocognit,gocyclo 238 func checkOctopus(cl *ds.RecordPackage) error { 239 if cl.Server.Host == "" && cl.Server.Conf == "" { 240 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckServerEmpty} 241 } 242 243 if cl.Server.Host == "" && cl.Server.Port != "" { 244 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckPortEmpty} 245 } 246 247 if cl.Server.Host != "" && cl.Server.Conf != "" { 248 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckServerConflict} 249 } 250 251 for _, fl := range cl.Fields { 252 if (fl.Format == "string" || fl.Format == "[]byte") && fl.Size == 0 { 253 log.Printf("Warn: field `%s` declaration. Field with type string or []byte not contain size.", fl.Name) 254 } 255 } 256 257 for _, ind := range cl.Indexes { 258 if len(ind.Fields) == 0 { 259 return &arerror.ErrCheckPackageIndexDecl{Pkg: cl.Namespace.PackageName, Index: ind.Name, Err: arerror.ErrCheckFieldIndexEmpty} 260 } 261 } 262 263 if len(cl.Fields) > 0 { 264 _, err := strconv.ParseInt(cl.Namespace.ObjectName, 10, 64) 265 if err != nil { 266 return &arerror.ErrCheckPackageNamespaceDecl{Pkg: cl.Namespace.PackageName, Name: cl.Namespace.ObjectName, Err: arerror.ErrCheckFieldInvalidFormat} 267 } 268 } 269 270 octopusProcAvailFormat := map[octopus.Format]bool{} 271 for _, form := range []octopus.Format{octopus.String, octopus.StringArray, octopus.ByteArray} { 272 octopusProcAvailFormat[form] = true 273 } 274 275 for _, fld := range cl.ProcInFields { 276 if _, ex := octopusProcAvailFormat[fld.Format]; !ex { 277 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} 278 } 279 280 if fld.Format != octopus.String && len(fld.Serializer) == 0 { 281 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotFound} 282 } 283 } 284 285 return nil 286 } 287 288 //nolint:gocognit,gocyclo 289 func checkTarantool(cl *ds.RecordPackage) error { 290 if cl.Server.Host == "" && cl.Server.Conf == "" { 291 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckServerEmpty} 292 } 293 294 if cl.Server.Host == "" && cl.Server.Port != "" { 295 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckPortEmpty} 296 } 297 298 if cl.Server.Host != "" && cl.Server.Conf != "" { 299 return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckServerConflict} 300 } 301 302 for _, fl := range cl.Fields { 303 if (fl.Format == "string" || fl.Format == "[]byte") && fl.Size == 0 { 304 log.Printf("Warn: field `%s` declaration. Field with type string or []byte not contain size.", fl.Name) 305 } 306 } 307 308 for _, ind := range cl.Indexes { 309 if len(ind.Fields) == 0 { 310 return &arerror.ErrCheckPackageIndexDecl{Pkg: cl.Namespace.PackageName, Index: ind.Name, Err: arerror.ErrCheckFieldIndexEmpty} 311 } 312 } 313 314 for _, fld := range cl.ProcInFields { 315 if fld.Format != octopus.String && len(fld.Serializer) == 0 { 316 return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotFound} 317 } 318 } 319 320 return nil 321 }