diff --git a/cmd/definitions/gen_func.go b/cmd/definitions/gen_func.go index 01c86474f..204a2e455 100644 --- a/cmd/definitions/gen_func.go +++ b/cmd/definitions/gen_func.go @@ -14,7 +14,7 @@ func generateFunc(ns *Namespace, path string) { nsNameP := templateutils.ToPascal(ns.Name) - for _, fn := range ns.Funcs { + for _, fn := range ns.ParsedFunctions() { if fn.Implemented { continue } @@ -22,19 +22,21 @@ func generateFunc(ns *Namespace, path string) { fnNameC := templateutils.ToCamel(fn.Name) fnNameP := templateutils.ToPascal(fn.Name) - if fn.Local { + op := fn.GetOperation() + + if op.Local { gfn := f.NewFunction(fnNameC). WithReceiver("s", "*"+nsNameP) - for _, v := range fn.Params { + for _, v := range op.ParsedParams() { // We need to remove pair from generated functions. - if v.Type() == "...Pair" { + if v.Type == "...Pair" { continue } - gfn.AddParameter(v.Name, v.Type()) + gfn.AddParameter(v.Name, v.Type) } gfn.AddParameter("opt", "pair"+nsNameP+fnNameP) - for _, v := range fn.Results { - gfn.AddResult(v.Name, v.Type()) + for _, v := range op.ParsedResults() { + gfn.AddResult(v.Name, v.Type) } gfn.AddBody(gg.S(`panic("not implemented")`)) continue @@ -43,16 +45,16 @@ func generateFunc(ns *Namespace, path string) { gfn := f.NewFunction(fnNameC). WithReceiver("s", "*"+nsNameP) gfn.AddParameter("ctx", "context.Context") - for _, v := range fn.Params { + for _, v := range op.ParsedParams() { // We need to remove pair from generated functions. - if v.Type() == "...Pair" { + if v.Type == "...Pair" { continue } - gfn.AddParameter(v.Name, v.Type()) + gfn.AddParameter(v.Name, v.Type) } gfn.AddParameter("opt", "pair"+nsNameP+fnNameP) - for _, v := range fn.Results { - gfn.AddResult(v.Name, v.Type()) + for _, v := range op.ParsedResults() { + gfn.AddResult(v.Name, v.Type) } gfn.AddBody(`panic("not implemented")`) continue diff --git a/cmd/definitions/gen_info.go b/cmd/definitions/gen_info.go index 211fb4121..053ba81dd 100644 --- a/cmd/definitions/gen_info.go +++ b/cmd/definitions/gen_info.go @@ -15,80 +15,81 @@ func generateInfo(data *Data, path string) { f.AddPackage("types") f.NewImport().AddPath("fmt") - for serviceName, infos := range data.InfosMap { - serviceNameC := templateutils.ToCamel(serviceName) - serviceNameP := templateutils.ToPascal(serviceName) - serviceNameK := templateutils.ToKebab(serviceName) + serviceName := "storage-meta" + infos := data.StorageMeta() - // Generate field bits - f.AddLineComment("Field index in %s bit", serviceNameC) - consts := f.NewConst() - for k, v := range infos { - consts.AddField( - gg.S("%sIndex%s", - serviceNameC, templateutils.ToPascal(v.Name)), - gg.S("1<<%d", k), - ) - } + serviceNameC := templateutils.ToCamel(serviceName) + serviceNameP := templateutils.ToPascal(serviceName) + serviceNameK := templateutils.ToKebab(serviceName) - // Generate struct - st := f.NewStruct(serviceNameP) - for _, v := range infos { - st.AddField(v.TypeName(), v.Type()) - } - st.AddLine() - st.AddLineComment("bit used as a bitmap for object value, 0 means not set, 1 means set") - st.AddField("bit", "uint64") - st.AddField("m", "map[string]interface{}") + // Generate field bits + f.AddLineComment("Field index in %s bit", serviceNameC) + consts := f.NewConst() + for k, v := range infos { + consts.AddField( + gg.S("%sIndex%s", + serviceNameC, templateutils.ToPascal(v.Name)), + gg.S("1<<%d", k), + ) + } + + // Generate struct + st := f.NewStruct(serviceNameP) + for _, v := range infos { + st.AddField(v.TypeName(), v.Type) + } + st.AddLine() + st.AddLineComment("bit used as a bitmap for object value, 0 means not set, 1 means set") + st.AddField("bit", "uint64") + st.AddField("m", "map[string]interface{}") - // Generate Get/Set functions. - for _, v := range infos { - // If the value is export, we don't need to generate MustGetXxxx - if v.Export { - f.NewFunction("Get"+v.DisplayName()). - WithReceiver("m", "*"+serviceNameP). - AddResult("", v.Type()). - AddBody(gg.Return("m." + v.TypeName())) - f.NewFunction("Set"+v.DisplayName()). - WithReceiver("m", "*"+serviceNameP). - AddParameter("v", v.Type()). - AddResult("", "*"+serviceNameP). - AddBody( - gg.S("m.%s = v", v.TypeName()), - gg.Return("m"), - ) - continue - } + // Generate Get/Set functions. + for _, v := range infos { + // If the value is export, we don't need to generate MustGetXxxx + if v.Export { f.NewFunction("Get"+v.DisplayName()). WithReceiver("m", "*"+serviceNameP). - AddResult("", v.Type()). - AddResult("", "bool"). - AddBody( - gg.If(gg.S("m.bit & %sIndex%s != 0", - serviceNameC, templateutils.ToPascal(v.Name))). - AddBody(gg.Return("m."+v.TypeName(), gg.Lit(true))), - gg.Return(templateutils.ZeroValue(v.Type()), gg.Lit(false)), - ) - f.NewFunction("MustGet"+v.DisplayName()). - WithReceiver("m", "*"+serviceNameP). - AddResult("", v.Type()). - AddBody( - gg.If(gg.S("m.bit & %sIndex%s == 0", - serviceNameC, templateutils.ToPascal(v.Name))). - AddBody(gg.S( - `panic(fmt.Sprintf("%s %s is not set"))`, - serviceNameK, v.Name)), - gg.Return("m."+v.TypeName())) + AddResult("", v.Type). + AddBody(gg.Return("m." + v.TypeName())) f.NewFunction("Set"+v.DisplayName()). WithReceiver("m", "*"+serviceNameP). - AddParameter("v", v.Type()). + AddParameter("v", v.Type). AddResult("", "*"+serviceNameP). AddBody( gg.S("m.%s = v", v.TypeName()), - gg.S("m.bit |= %sIndex%s", serviceNameC, templateutils.ToPascal(v.Name)), gg.Return("m"), ) + continue } + f.NewFunction("Get"+v.DisplayName()). + WithReceiver("m", "*"+serviceNameP). + AddResult("", v.Type). + AddResult("", "bool"). + AddBody( + gg.If(gg.S("m.bit & %sIndex%s != 0", + serviceNameC, templateutils.ToPascal(v.Name))). + AddBody(gg.Return("m."+v.TypeName(), gg.Lit(true))), + gg.Return(templateutils.ZeroValue(v.Type), gg.Lit(false)), + ) + f.NewFunction("MustGet"+v.DisplayName()). + WithReceiver("m", "*"+serviceNameP). + AddResult("", v.Type). + AddBody( + gg.If(gg.S("m.bit & %sIndex%s == 0", + serviceNameC, templateutils.ToPascal(v.Name))). + AddBody(gg.S( + `panic(fmt.Sprintf("%s %s is not set"))`, + serviceNameK, v.Name)), + gg.Return("m."+v.TypeName())) + f.NewFunction("Set"+v.DisplayName()). + WithReceiver("m", "*"+serviceNameP). + AddParameter("v", v.Type). + AddResult("", "*"+serviceNameP). + AddBody( + gg.S("m.%s = v", v.TypeName()), + gg.S("m.bit |= %sIndex%s", serviceNameC, templateutils.ToPascal(v.Name)), + gg.Return("m"), + ) } err := f.WriteFile(path) diff --git a/cmd/definitions/gen_iterator.go b/cmd/definitions/gen_iterator.go new file mode 100644 index 000000000..ed407a498 --- /dev/null +++ b/cmd/definitions/gen_iterator.go @@ -0,0 +1,128 @@ +//go:build tools +// +build tools + +package main + +import ( + "fmt" + + "github.com/Xuanwo/gg" + log "github.com/sirupsen/logrus" +) + +func generateIterator(path string) { + data := []string{ + "Block", + "Object", + "Part", + "Storager", + } + + f := gg.NewGroup() + f.AddLineComment("Code generated by go generate cmd/definitions; DO NOT EDIT.") + f.AddPackage("types") + f.NewImport(). + AddPath("context"). + AddPath("errors"). + AddPath("fmt") + + f.NewInterface("Continuable"). + NewFunction("ContinuationToken"). + AddResult("", "string") + + for _, name := range data { + typ := name + // Add a `*` if the type is not an interface. + if name != "Storager" { + typ = "*" + typ + } + + nextFuncName := fmt.Sprintf("Next%sFunc", name) + pageStructName := fmt.Sprintf("%sPage", name) + iteratorStructName := fmt.Sprintf("%sIterator", name) + + f.AddLineComment(`%s is the func used in iterator. + +Notes +- ErrDone should be return while there are no items any more. +- Input %s slice should be set every time. +`, nextFuncName, name) + f.AddType( + nextFuncName, + gg.Function(""). + AddParameter("ctx", "context.Context"). + AddParameter("page", "*"+pageStructName). + AddResult("", "error")) + + f.NewStruct(pageStructName). + AddField("Status", "Continuable"). + AddField("Data", "[]"+typ) + + f.NewStruct(iteratorStructName). + AddField("ctx", "context.Context"). + AddField("next", nextFuncName). + AddLine(). + AddField("index", "int"). + AddField("done", "bool"). + AddLine(). + AddField("o", pageStructName) + + f.NewFunction("New"+iteratorStructName). + AddParameter("ctx", "context.Context"). + AddParameter("next", nextFuncName). + AddParameter("status", "Continuable"). + AddResult("", "*"+iteratorStructName). + AddBody(gg.Return( + gg.Value("&"+iteratorStructName). + AddField("ctx", "ctx"). + AddField("next", "next"). + AddField("o", gg.Value(pageStructName). + AddField("Status", "status")))) + + f.NewFunction("ContinuationToken"). + WithReceiver("it", "*"+iteratorStructName). + AddResult("", "string"). + AddBody(gg.Return( + gg.Call("ContinuationToken").WithOwner("it.o.Status"))) + + f.NewFunction("Next"). + WithReceiver("it", "*"+iteratorStructName). + AddResult("object", typ). + AddResult("err", "error"). + AddBody( + gg.LineComment("Consume Data via index."), + gg.S(`// Consume Data via index. +if it.index < len(it.o.Data) { + it.index++ + return it.o.Data[it.index-1], nil +} +// Return IterateDone if iterator is already done. +if it.done { + return nil, IterateDone +} + +// Reset buf before call next. +it.o.Data = it.o.Data[:0] + +err = it.next(it.ctx ,&it.o) +if err != nil && !errors.Is(err, IterateDone) { + return nil, fmt.Errorf("iterator next failed: %w", err) +} +// Make iterator to done so that we will not fetch from upstream anymore. +if err != nil { + it.done = true +} +// Return IterateDone directly if we don't have any data. +if len(it.o.Data) == 0 { + return nil, IterateDone +} +// Return the first object. +it.index = 1 +return it.o.Data[0], nil`)) + } + + err := f.WriteFile(path) + if err != nil { + log.Fatalf("generate to %s: %v", path, err) + } +} diff --git a/cmd/definitions/gen_object.go b/cmd/definitions/gen_object.go index d6c3fc3ab..bcdb893df 100644 --- a/cmd/definitions/gen_object.go +++ b/cmd/definitions/gen_object.go @@ -20,7 +20,7 @@ func generateObject(data *Data, path string) { f.AddLineComment("Field index in object bit") cons := f.NewConst() - for k, v := range data.ObjectMeta { + for k, v := range data.ObjectMeta() { pname := templateutils.ToPascal(v.Name) cons.AddTypedField( "objectIndex"+pname, "uint64", gg.S("1<<%d", k)) @@ -37,11 +37,11 @@ NOTES: could be fetched via lazy stat logic: https://beyondstorage.io/docs/go-storage/internal/object-lazy-stat `) ob := f.NewStruct("Object") - for _, v := range data.ObjectMeta { + for _, v := range data.ObjectMeta() { if v.Description != "" { ob.AddLineComment(v.Description) } - ob.AddField(v.TypeName(), v.Type()) + ob.AddField(v.TypeName(), v.Type) } ob.AddLineComment("client is the client in which Object is alive.") @@ -52,17 +52,17 @@ NOTES: ob.AddField("done", "uint32") ob.AddField("m", "sync.Mutex") - for _, v := range data.ObjectMeta { + for _, v := range data.ObjectMeta() { pname := templateutils.ToPascal(v.Name) if v.Export { f.NewFunction("Get"+v.DisplayName()). WithReceiver("o", "*Object"). - AddResult("", v.Type()). + AddResult("", v.Type). AddBody(gg.Return(gg.S("o.%s", v.TypeName()))) f.NewFunction("Set"+v.DisplayName()). WithReceiver("o", "*Object"). - AddParameter("v", v.Type()). + AddParameter("v", v.Type). AddResult("", "*Object"). AddBody( gg.S("o.%s = v", v.TypeName()), @@ -77,7 +77,7 @@ NOTES: `, v.DisplayName(), v.DisplayName(), v.Description) f.NewFunction("Get"+v.DisplayName()). WithReceiver("o", "*Object"). - AddResult("", v.Type()). + AddResult("", v.Type). AddResult("", "bool"). AddBody( gg.S("o.stat()"), @@ -86,7 +86,7 @@ NOTES: AddBody( gg.Return("o."+v.TypeName(), gg.Lit(true)), ), - gg.Return(templateutils.ZeroValue(v.Type()), gg.Lit(false)), + gg.Return(templateutils.ZeroValue(v.Type), gg.Lit(false)), ) f.AddLineComment(`MustGet%s will get %s from Object. @@ -95,7 +95,7 @@ NOTES: `, v.DisplayName(), v.DisplayName(), v.Description) f.NewFunction("MustGet"+v.DisplayName()). WithReceiver("o", "*Object"). - AddResult("", v.Type()). + AddResult("", v.Type). AddBody( gg.S("o.stat()"), gg.Line(), @@ -112,7 +112,7 @@ NOTES: `, v.DisplayName(), v.DisplayName(), v.Description) f.NewFunction("Set"+v.DisplayName()). WithReceiver("o", "*Object"). - AddParameter("v", v.Type()). + AddParameter("v", v.Type). AddResult("", "*Object"). AddBody( gg.S("o.%s = v", v.TypeName()), @@ -123,7 +123,7 @@ NOTES: fn := f.NewFunction("clone"). WithReceiver("o", "*Object"). AddParameter("xo", "*Object") - for _, v := range data.ObjectMeta { + for _, v := range data.ObjectMeta() { fn.AddBody(gg.S("o.%s = xo.%s", v.TypeName(), v.TypeName())) } fn.AddBody(gg.S("o.bit = xo.bit")) diff --git a/cmd/definitions/gen_op.go b/cmd/definitions/gen_op.go index 9e23a30a5..8e9e1188c 100644 --- a/cmd/definitions/gen_op.go +++ b/cmd/definitions/gen_op.go @@ -19,7 +19,7 @@ func generateOperation(data *Data, path string) { AddPath("net/http"). AddPath("time") - for _, in := range data.Interfaces { + for _, in := range data.Interfaces() { f.AddLineComment("%s %s", in.DisplayName(), in.Description) inter := f.NewInterface(in.DisplayName()) @@ -33,11 +33,11 @@ func generateOperation(data *Data, path string) { inter.AddLineComment("%s %s", pname, op.Description) gop := inter.NewFunction(pname) - for _, p := range op.Params { - gop.AddParameter(p.Name, p.Type()) + for _, p := range op.ParsedParams() { + gop.AddParameter(p.Name, p.Type) } - for _, r := range op.Results { - gop.AddResult(r.Name, r.Type()) + for _, r := range op.ParsedResults() { + gop.AddResult(r.Name, r.Type) } // We need to generate XxxWithContext functions if not local. @@ -47,11 +47,11 @@ func generateOperation(data *Data, path string) { // Insert context param. gop.AddParameter("ctx", "context.Context") - for _, p := range op.Params { - gop.AddParameter(p.Name, p.Type()) + for _, p := range op.ParsedParams() { + gop.AddParameter(p.Name, p.Type) } - for _, r := range op.Results { - gop.AddResult(r.Name, r.Type()) + for _, r := range op.ParsedResults() { + gop.AddResult(r.Name, r.Type) } } // Insert an empty for different functions. @@ -64,7 +64,8 @@ func generateOperation(data *Data, path string) { f.NewStruct(stubName) f.NewFunction("mustEmbedUnimplemented"+in.DisplayName()). - WithReceiver("s", stubName) + WithReceiver("s", stubName). + AddBody(gg.Line()) f.NewFunction("String"). WithReceiver("s", stubName). AddResult("", "string"). @@ -77,11 +78,11 @@ func generateOperation(data *Data, path string) { gop := f.NewFunction(pname). WithReceiver("s", stubName) - for _, p := range op.Params { - gop.AddParameter(p.Name, p.Type()) + for _, p := range op.ParsedParams() { + gop.AddParameter(p.Name, p.Type) } - for _, r := range op.Results { - gop.AddResult(r.Name, r.Type()) + for _, r := range op.ParsedResults() { + gop.AddResult(r.Name, r.Type) } // If not local, we need to set error if !op.Local { @@ -96,11 +97,11 @@ func generateOperation(data *Data, path string) { // Insert context param. gop.AddParameter("ctx", "context.Context") - for _, p := range op.Params { - gop.AddParameter(p.Name, p.Type()) + for _, p := range op.ParsedParams() { + gop.AddParameter(p.Name, p.Type) } - for _, r := range op.Results { - gop.AddResult(r.Name, r.Type()) + for _, r := range op.ParsedResults() { + gop.AddResult(r.Name, r.Type) } gop.AddBody( gg.S(`err = NewOperationNotImplementedError("%s")`, op.Name), diff --git a/cmd/definitions/gen_pair.go b/cmd/definitions/gen_pair.go index 1684d5e07..fe283ba90 100644 --- a/cmd/definitions/gen_pair.go +++ b/cmd/definitions/gen_pair.go @@ -22,8 +22,8 @@ func generatePair(data *Data, path string) { AddPath("github.com/beyondstorage/go-storage/v4/pkg/httpclient"). AddDot("github.com/beyondstorage/go-storage/v4/types") - ps := make([]*Pair, 0, len(data.Pairs)) - for _, v := range data.Pairs { + ps := make([]*Pair, 0, len(data.PairsMap)) + for _, v := range data.PairsMap { v := v ps = append(ps, v) } @@ -38,28 +38,13 @@ func generatePair(data *Data, path string) { %s %s`, pname, v.Name, pname, v.Description) xfn := f.NewFunction("With" + pname) - xfn.AddParameter("v", v.Type()) + xfn.AddParameter("v", v.Type) xfn.AddResult("p", "Pair") xfn.AddBody( gg.Return( gg.Value("Pair"). AddField("Key", gg.Lit(v.Name)). AddField("Value", "v"))) - if v.Defaultable { - name := "default_" + v.Name - pname := templateutils.ToPascal(name) - - f.AddLineComment(`With%s will apply %s value to Options. - -%s %s`, pname, name, pname, v.Description) - - xfn := f.NewFunction("With" + pname) - xfn.AddParameter("v", v.Type()) - xfn.AddResult("p", "Pair") - xfn.AddBody( - gg.Return( - gg.Value("Pair").AddField("Key", gg.Lit("default_"+v.Name)).AddField("Value", "v"))) - } } err := f.WriteFile(path) diff --git a/cmd/definitions/gen_service.go b/cmd/definitions/gen_service.go index 74e99a304..d7af59cd2 100644 --- a/cmd/definitions/gen_service.go +++ b/cmd/definitions/gen_service.go @@ -39,7 +39,7 @@ func generateSrv(data *Service, path string) { // Generate object system metadata. f.AddLineComment("ObjectSystemMetadata stores system metadata for object.") osm := f.NewStruct("ObjectSystemMetadata") - for _, info := range data.Infos { + for _, info := range data.SortedInfos() { if info.Scope != "object" { continue } @@ -51,7 +51,7 @@ func generateSrv(data *Service, path string) { pname = info.DisplayName() } // FIXME: we will support comment on field later. - osm.AddField(pname, info.Type()) + osm.AddField(pname, info.Type) } f.AddLineComment(` @@ -87,7 +87,7 @@ setObjectSystemMetadata will set ObjectSystemMetadata into Object. // Generate storage system metadata. f.AddLineComment("StorageSystemMetadata stores system metadata for object.") ssm := f.NewStruct("StorageSystemMetadata") - for _, info := range data.Infos { + for _, info := range data.SortedInfos() { if info.Scope != "object" { continue } @@ -99,7 +99,7 @@ setObjectSystemMetadata will set ObjectSystemMetadata into Object. pname = info.DisplayName() } // FIXME: we will support comment on field later. - ssm.AddField(pname, info.Type()) + ssm.AddField(pname, info.Type) } f.AddLineComment(` @@ -130,7 +130,7 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. ) // Generate service pairs. - for _, pair := range data.Pairs() { + for _, pair := range data.SortedPairs() { // We don't need to generate global pairs here. if pair.Global { continue @@ -146,8 +146,8 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. // Set to true as default. value := "true" // bool type pairs don't need input. - if pair.Type() != "bool" { - fn.AddParameter("v", pair.Type()) + if pair.Type != "bool" { + fn.AddParameter("v", pair.Type) value = "v" } fn.AddResult("", "Pair") @@ -160,19 +160,19 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. // Generate pair map f.NewVar().AddField("pairMap", gg.Embed(func() gg.Node { i := gg.Value("map[string]string") - for _, pair := range data.Pairs() { - i.AddField(gg.Lit(pair.Name), gg.Lit(pair.Type())) + for _, pair := range data.SortedPairs() { + i.AddField(gg.Lit(pair.Name), gg.Lit(pair.Type)) } return i })) // Generate every namespace. - for _, ns := range data.Namespaces { + for _, ns := range data.SortedNamespaces() { nsNameP := templateutils.ToPascal(ns.Name) // Generate interface assert. inters := f.NewVar() - for _, inter := range ns.Interfaces { + for _, inter := range ns.ParsedInterfaces() { interNameP := templateutils.ToPascal(inter.Name) inters.AddTypedField( "_", interNameP, gg.S("&%s{}", nsNameP)) @@ -180,7 +180,7 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. // Generate feature struct. features := f.NewStruct(nsNameP + "Features") - for _, fs := range ns.Features { + for _, fs := range ns.ParsedFeatures() { features.AddLineComment(fs.Description) features.AddField(templateutils.ToPascal(fs.Name), "bool") } @@ -195,36 +195,28 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. // Generate required pairs. pairStruct.AddLineComment("Required pairs") - for _, pair := range ns.New.Required { + for _, pair := range ns.New.ParsedRequired() { pairNameP := templateutils.ToPascal(pair.Name) pairStruct.AddField("Has"+pairNameP, "bool") - pairStruct.AddField(pairNameP, pair.Type()) + pairStruct.AddField(pairNameP, pair.Type) } // Generate optional pairs. pairStruct.AddLineComment("Optional pairs") - for _, pair := range ns.New.Optional { + for _, pair := range ns.New.ParsedOptional() { pairNameP := templateutils.ToPascal(pair.Name) pairStruct.AddField("Has"+pairNameP, "bool") - pairStruct.AddField(pairNameP, pair.Type()) + pairStruct.AddField(pairNameP, pair.Type) } // Generate feature handle logic. pairStruct.AddLineComment("Enable features") - for _, feature := range ns.Features { + for _, feature := range ns.ParsedFeatures() { featureNameP := templateutils.ToPascal(feature.Name) pairStruct.AddField("hasEnable"+featureNameP, "bool") pairStruct.AddField("Enable"+featureNameP, "bool") } - // Generate default pairs. - pairStruct.AddLineComment("Default pairs") - for _, dp := range ns.Defaultable() { - dpNameP := templateutils.ToPascal(dp.Pair.Name) - pairStruct.AddField("hasDefault"+dpNameP, "bool") - pairStruct.AddField("Default"+dpNameP, dp.Pair.Type()) - } - // Generate parse newPair. pairParseName := fmt.Sprintf("parsePair%s%s", nsNameP, fnNewNameP) f.AddLineComment("%s will parse Pair slice into *%s", pairParseName, partStructName) @@ -240,25 +232,25 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. AddBody(gg.Embed(func() gg.Node { is := gg.Switch(gg.S("v.Key")) - for _, pair := range ns.New.Required { + for _, pair := range ns.New.ParsedRequired() { pairNameP := templateutils.ToPascal(pair.Name) is.NewCase(gg.Lit(pair.Name)).AddBody( gg.If(gg.S("result.Has%s", pairNameP)). AddBody(gg.Continue()), gg.S("result.Has%s = true", pairNameP), - gg.S("result.%s = v.Value.(%s)", pairNameP, pair.Type()), + gg.S("result.%s = v.Value.(%s)", pairNameP, pair.Type), ) } - for _, pair := range ns.New.Optional { + for _, pair := range ns.New.ParsedOptional() { pairNameP := templateutils.ToPascal(pair.Name) is.NewCase(gg.Lit(pair.Name)).AddBody( gg.If(gg.S("result.Has%s", pairNameP)). AddBody(gg.Continue()), gg.S("result.Has%s = true", pairNameP), - gg.S("result.%s = v.Value.(%s)", pairNameP, pair.Type()), + gg.S("result.%s = v.Value.(%s)", pairNameP, pair.Type), ) } - for _, feature := range ns.Features { + for _, feature := range ns.ParsedFeatures() { featureNameP := templateutils.ToPascal(feature.Name) is.NewCase(gg.Lit("enable_"+feature.Name)).AddBody( gg.If(gg.S("result.hasEnable%s", featureNameP)). @@ -267,22 +259,13 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. gg.S("result.Enable%s = true", featureNameP), ) } - for _, dp := range ns.Defaultable() { - dpNameP := templateutils.ToPascal(dp.Pair.Name) - is.NewCase(gg.Lit("default_"+dp.Pair.Name)).AddBody( - gg.If(gg.S("result.hasDefault%s", dpNameP)). - AddBody(gg.Continue()), - gg.S("result.hasDefault%s = true", dpNameP), - gg.S("result.Default%s = v.Value.(%s)", dpNameP, dp.Pair.Type()), - ) - } return is })), gg.LineComment("Enable features"), gg.Embed(func() gg.Node { // Generate features enable here. group := gg.NewGroup() - for _, feature := range ns.Features { + for _, feature := range ns.ParsedFeatures() { featureNameP := templateutils.ToPascal(feature.Name) gg.If(gg.S("result.hasEnable%s", featureNameP)). @@ -296,14 +279,15 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. gg.Embed(func() gg.Node { // Generate default pari handle logic here. group := gg.NewGroup() - for _, dp := range ns.Defaultable() { + + for _, dp := range ns.ParsedDefaultable() { pairNameP := templateutils.ToPascal(dp.Pair.Name) xif := group. - NewIf(gg.S("result.hasDefault%s", pairNameP)). + NewIf(gg.S("result.HasDefault%s", pairNameP)). AddBody(gg.S("result.HasDefault%sPairs = true", nsNameP)) for _, op := range dp.Funcs { - opN := templateutils.ToPascal(op) + opN := templateutils.ToPascal(op.Name) xif.AddBody(gg.S( "result.Default%sPairs.%s = append(result.Default%sPairs.%s, With%s(result.Default%s))", @@ -316,7 +300,7 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. // Generate required validate logic here. group := gg.NewGroup() - for _, pair := range ns.New.Required { + for _, pair := range ns.New.ParsedRequired() { pairNameP := templateutils.ToPascal(pair.Name) group.NewIf(gg.S("!result.Has%s", pairNameP)). AddBody(gg.S( @@ -331,13 +315,13 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. // Generate default pairs. f.AddLineComment("Default%sPairs is default pairs for specific action", nsNameP) dps := f.NewStruct(fmt.Sprintf("Default%sPairs", nsNameP)) - for _, fn := range ns.Funcs { + for _, fn := range ns.ParsedFunctions() { fnNameP := templateutils.ToPascal(fn.Name) dps.AddField(fnNameP, "[]Pair") } // Generate pair. - for _, fn := range ns.Funcs { + for _, fn := range ns.ParsedFunctions() { fnNameP := templateutils.ToPascal(fn.Name) pairStructName := fmt.Sprintf("pair%s%s", nsNameP, fnNameP) @@ -347,18 +331,18 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. // Generate required pairs. pairStruct.AddLineComment("Required pairs") - for _, pair := range fn.Required { + for _, pair := range fn.ParsedRequired() { pairNameP := templateutils.ToPascal(pair.Name) pairStruct.AddField("Has"+pairNameP, "bool") - pairStruct.AddField(pairNameP, pair.Type()) + pairStruct.AddField(pairNameP, pair.Type) } // Generate optional pairs. pairStruct.AddLineComment("Optional pairs") - for _, pair := range fn.Optional { + for _, pair := range fn.ParsedOptional() { pairNameP := templateutils.ToPascal(pair.Name) pairStruct.AddField("Has"+pairNameP, "bool") - pairStruct.AddField(pairNameP, pair.Type()) + pairStruct.AddField(pairNameP, pair.Type) } pairParseName := fmt.Sprintf("parsePair%s%s", nsNameP, fnNameP) @@ -375,22 +359,22 @@ GetStorageSystemMetadata will get StorageSystemMetadata from Storage. AddBody(gg.Embed(func() gg.Node { is := gg.Switch(gg.S("v.Key")) - for _, pair := range fn.Required { + for _, pair := range fn.ParsedRequired() { pairNameP := templateutils.ToPascal(pair.Name) is.NewCase(gg.Lit(pair.Name)).AddBody( gg.If(gg.S("result.Has%s", pairNameP)). AddBody(gg.Continue()), gg.S("result.Has%s = true", pairNameP), - gg.S("result.%s = v.Value.(%s)", pairNameP, pair.Type()), + gg.S("result.%s = v.Value.(%s)", pairNameP, pair.Type), ) } - for _, pair := range fn.Optional { + for _, pair := range fn.ParsedOptional() { pairNameP := templateutils.ToPascal(pair.Name) is.NewCase(gg.Lit(pair.Name)).AddBody( gg.If(gg.S("result.Has%s", pairNameP)). AddBody(gg.Continue()), gg.S("result.Has%s = true", pairNameP), - gg.S("result.%s = v.Value.(%s)", pairNameP, pair.Type()), + gg.S("result.%s = v.Value.(%s)", pairNameP, pair.Type), ) } dcas := is.NewDefault() @@ -411,7 +395,7 @@ If user enable this feature, service should ignore not support pair error.`), // Generate required validate logic here. group := gg.NewGroup() - for _, pair := range fn.Required { + for _, pair := range fn.ParsedRequired() { pairNameP := templateutils.ToPascal(pair.Name) group.NewIf(gg.S("!result.Has%s", pairNameP)). AddBody(gg.S( @@ -425,16 +409,17 @@ If user enable this feature, service should ignore not support pair error.`), } // Generate public functions. - for _, fn := range ns.Funcs { + for _, fn := range ns.ParsedFunctions() { fnNameP := templateutils.ToPascal(fn.Name) - if fn.Local { + op := fn.GetOperation() + if op.Local { // Generate a local function. xfn := f.NewFunction(fnNameP).WithReceiver("s", "*"+nsNameP) - for _, field := range fn.Params { - xfn.AddParameter(field.Name, field.Type()) + for _, field := range op.ParsedParams() { + xfn.AddParameter(field.Name, field.Type) } - for _, field := range fn.Results { - xfn.AddResult(field.Name, field.Type()) + for _, field := range op.ParsedResults() { + xfn.AddResult(field.Name, field.Type) } xfn.AddBody( gg.S("pairs = append(pairs, s.defaultPairs.%s...)", fnNameP), @@ -446,9 +431,9 @@ If user enable this feature, service should ignore not support pair error.`), gg.Embed(func() gg.Node { ic := gg.Call(templateutils.ToCamel(fn.Name)). WithOwner("s") - for _, v := range fn.Params { + for _, v := range op.ParsedParams() { // We don't need to call pair again. - if v.Type() == "...Pair" { + if v.Type == "...Pair" { continue } ic.AddParameter(v.Name) @@ -462,11 +447,11 @@ If user enable this feature, service should ignore not support pair error.`), // TODO: generate comment here. xfn := f.NewFunction(fnNameP). WithReceiver("s", "*"+nsNameP) - for _, field := range fn.Params { - xfn.AddParameter(field.Name, field.Type()) + for _, field := range op.ParsedParams() { + xfn.AddParameter(field.Name, field.Type) } - for _, field := range fn.Results { - xfn.AddResult(field.Name, field.Type()) + for _, field := range op.ParsedResults() { + xfn.AddResult(field.Name, field.Type) } xfn.AddBody( "ctx := context.Background()", @@ -475,8 +460,8 @@ If user enable this feature, service should ignore not support pair error.`), ic := gg.Call(fnNameP + "WithContext"). WithOwner("s") ic.AddParameter("ctx") - for _, v := range fn.Params { - if v.Type() == "...Pair" { + for _, v := range op.ParsedParams() { + if v.Type == "...Pair" { ic.AddParameter("pairs...") continue } @@ -488,19 +473,19 @@ If user enable this feature, service should ignore not support pair error.`), xfn = f.NewFunction(fnNameP+"WithContext"). WithReceiver("s", "*"+nsNameP) xfn.AddParameter("ctx", "context.Context") - for _, field := range fn.Params { - xfn.AddParameter(field.Name, field.Type()) + for _, field := range op.ParsedParams() { + xfn.AddParameter(field.Name, field.Type) } - for _, field := range fn.Results { - xfn.AddResult(field.Name, field.Type()) + for _, field := range op.ParsedResults() { + xfn.AddResult(field.Name, field.Type) } xfn.AddBody( gg.Defer(gg.Embed(func() gg.Node { caller := gg.Call("formatError").WithOwner("s") caller.AddParameter(gg.Lit(fn.Name)).AddParameter("err") - for _, v := range fn.Params { + for _, v := range op.ParsedParams() { // formatError only accept string as input. - if v.Type() != "string" { + if v.Type != "string" { continue } caller.AddParameter(v.Name) @@ -511,10 +496,10 @@ If user enable this feature, service should ignore not support pair error.`), return fn })), gg.Embed(func() gg.Node { - if fn.ObjectMode == "" { + if op.ObjectMode == "" { return gg.Line() } - mode := templateutils.ToPascal(fn.ObjectMode) + mode := templateutils.ToPascal(op.ObjectMode) return gg.If(gg.S("!o.Mode.Is%s()", mode)).AddBody( gg.S("err = services.ObjectModeInvalidError{Expected: Mode%s, Actual: o.Mode}", mode), gg.Return(), @@ -530,9 +515,9 @@ If user enable this feature, service should ignore not support pair error.`), ic := gg.Call(templateutils.ToCamel(fn.Name)). WithOwner("s") ic.AddParameter("ctx") - for _, v := range fn.Params { + for _, v := range op.ParsedParams() { // We don't need to call pair again. - if v.Type() == "...Pair" { + if v.Type == "...Pair" { continue } ic.AddParameter(v.Name) @@ -545,7 +530,7 @@ If user enable this feature, service should ignore not support pair error.`), // Generate init function initFn := f.NewFunction("init") - for _, ns := range data.Namespaces { + for _, ns := range data.SortedNamespaces() { nsNameP := templateutils.ToPascal(ns.Name) initFn.AddBody(gg.Call("Register" + nsNameP + "r"). WithOwner("services"). diff --git a/cmd/definitions/generate.go b/cmd/definitions/generate.go index 0042570d5..b6cfa6145 100644 --- a/cmd/definitions/generate.go +++ b/cmd/definitions/generate.go @@ -4,22 +4,20 @@ package main import ( - "fmt" "go/parser" "go/token" "os" "sort" - "text/template" - "github.com/Xuanwo/templateutils" "github.com/dave/dst" "github.com/dave/dst/decorator" log "github.com/sirupsen/logrus" - - "github.com/beyondstorage/go-storage/v4/cmd/definitions/bindata" ) func generateGlobal(data *Data) { + // Iterator generate + generateIterator("types/iterator.generated.go") + // Metas generate generateInfo(data, "types/info.generated.go") @@ -35,32 +33,12 @@ func generateGlobal(data *Data) { func generateService(data *Data) { generateSrv(data.Service, "generated.go") - for _, v := range data.Service.Namespaces { + for _, v := range data.Service.SortedNamespaces() { generateFunc(v, v.Name+".go") formatService(v.Name + ".go") } } -func generateT(tmpl *template.Template, filePath string, data interface{}) { - errorMsg := fmt.Sprintf("generate template %s to %s", tmpl.Name(), filePath) + ": %v" - - file, err := os.Create(filePath) - if err != nil { - log.Fatalf(errorMsg, err) - } - err = tmpl.Execute(file, data) - if err != nil { - log.Fatalf(errorMsg, err) - } -} - -func newTmpl(name string) *template.Template { - return template.Must( - template.New(name). - Funcs(templateutils.FuncMap()). - Parse(string(bindata.MustAsset(name + ".tmpl")))) -} - func formatService(filename string) { fset := token.NewFileSet() diff --git a/cmd/definitions/main.go b/cmd/definitions/main.go index 81aa8ae8c..e63899c48 100644 --- a/cmd/definitions/main.go +++ b/cmd/definitions/main.go @@ -7,43 +7,47 @@ import ( "os" log "github.com/sirupsen/logrus" - - "github.com/beyondstorage/go-storage/v4/cmd/definitions/specs" + "github.com/urfave/cli/v2" ) -func main() { - run(os.Args) -} - -func run(args []string) { - switch v := len(args); v { - case 1: - actionGlobal() - case 2: - actionService(args[1]) - default: - log.Fatalf("args length should be 1 or 2, actual %d", v) - } -} - -func actionGlobal() { - data := parse() - data.Sort() - - generateGlobal(data) +var app = &cli.App{ + Name: "definitions", + Usage: "definitions [service.toml]", + Before: func(c *cli.Context) error { + if c.Args().Len() > 1 { + log.Fatalf("args length should be 0 or 1, actual %d", c.Args().Len()) + } + return nil + }, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + Action: func(c *cli.Context) error { + if c.Bool("debug") { + log.SetLevel(log.DebugLevel) + log.SetReportCaller(true) + } + + if c.Args().Len() == 0 { + generateGlobal(NewData()) + return nil + } + + data := NewData() + filePath := c.Args().First() + data.LoadService(filePath) + generateService(data) + + log.Printf("%s generate finished", filePath) + return nil + }, } -func actionService(filePath string) { - data := parse() - - srv, err := specs.ParseService(filePath) +func main() { + err := app.Run(os.Args) if err != nil { - log.Fatalf("parse: %v", err) + os.Exit(1) } - data.Service = data.FormatService(srv) - - data.Sort() - - generateService(data) - log.Printf("%s generate finished", filePath) } diff --git a/cmd/definitions/parse.go b/cmd/definitions/parse.go index 6e616938a..61054cba1 100644 --- a/cmd/definitions/parse.go +++ b/cmd/definitions/parse.go @@ -10,33 +10,16 @@ import ( "github.com/Xuanwo/templateutils" log "github.com/sirupsen/logrus" - - "github.com/beyondstorage/go-storage/v4/cmd/definitions/specs" ) -func parse() (data *Data) { - injectPairs() - - data = NewData() - return data -} - -func injectPairs() { - ps := []specs.Pair{ - { - Name: "context", - Type: "context.Context", - }, - { - Name: "http_client_options", - Type: "*httpclient.Options", - }, - } - - for _, v := range ps { - specs.ParsedPairs = append(specs.ParsedPairs, v) - } -} +const ( + featurePath = "definitions/features.toml" + fieldPath = "definitions/fields.toml" + infoObjectMeta = "definitions/info_object_meta.toml" + infoStorageMeta = "definitions/info_storage_meta.toml" + operationPath = "definitions/operations.toml" + pairPath = "definitions/pairs.toml" +) func parseFunc(name string) map[string]*templateutils.Method { data := make(map[string]*templateutils.Method) @@ -61,36 +44,3 @@ func parseFunc(name string) map[string]*templateutils.Method { } return data } - -var typeMap = map[string]string{ - "context": "context.Context", - "http_client_options": "*httpclient.Options", - - "any": "interface{}", - "byte_array": "[]byte", - "string_array": "[]string", - "string_string_map": "map[string]string", - "time": "time.Time", - "BlockIterator": "*BlockIterator", - "IoCallback": "func([]byte)", - "Object": "*Object", - "ObjectIterator": "*ObjectIterator", - "Pairs": "...Pair", - "Part": "*Part", - "Parts": "[]*Part", - "PartIterator": "*PartIterator", - "Reader": "io.Reader", - "StoragerIterator": "*StoragerIterator", - "StorageMeta": "*StorageMeta", - "Writer": "io.Writer", -} - -// TODO: We can remove this convert after all service migrated. -func parseType(v string) string { - s, ok := typeMap[v] - if !ok { - return v - } - log.Warnf("type %s is not supported anymore, please updated to %s.", v, s) - return s -} diff --git a/cmd/definitions/specs/build.go b/cmd/definitions/specs/build.go deleted file mode 100644 index 986309082..000000000 --- a/cmd/definitions/specs/build.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build tools -// +build tools - -package specs - -const ( - featurePath = "definitions/features.toml" - fieldPath = "definitions/fields.toml" - infoObjectMeta = "definitions/info_object_meta.toml" - infoStorageMeta = "definitions/info_storage_meta.toml" - operationPath = "definitions/operations.toml" - pairPath = "definitions/pairs.toml" -) - -var ( - ParsedFeatures Features - ParsedPairs Pairs - ParsedInfos Infos - ParsedOperations Operations -) - -func ParseService(filePath string) (Service, error) { - srv := parseService(filePath) - - // Make sure services has been sorted, so that we can format the specs correctly. - srv.Sort() - - return srv, nil -} - -func init() { - ParsedFeatures = parseFeatures() - ParsedFeatures.Sort() - - ParsedPairs = parsePairs() - ParsedPairs.Sort() - - ParsedInfos = parseInfos() - ParsedInfos.Sort() - - ParsedOperations = parseOperations() - ParsedOperations.Sort() -} diff --git a/cmd/definitions/specs/build_test.go b/cmd/definitions/specs/build_test.go deleted file mode 100644 index 6daadd0d6..000000000 --- a/cmd/definitions/specs/build_test.go +++ /dev/null @@ -1,29 +0,0 @@ -//go:build tools -// +build tools - -package specs - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestImport(t *testing.T) { - assert.NotEmpty(t, ParsedFeatures) - assert.NotEmpty(t, ParsedPairs) - assert.NotEmpty(t, ParsedOperations) - assert.NotEmpty(t, ParsedInfos) -} - -func TestParseService(t *testing.T) { - srv, err := ParseService("testdata/service.toml") - if err != nil { - t.Error("parse service", err) - return - } - - assert.Equal(t, "tests", srv.Name) - assert.Equal(t, "storage", srv.Namespaces[1].Name) - assert.Equal(t, []string{"virtual_dir"}, srv.Namespaces[1].Features) -} diff --git a/cmd/definitions/specs/parse.go b/cmd/definitions/specs/parse.go deleted file mode 100644 index 5b9cb0e95..000000000 --- a/cmd/definitions/specs/parse.go +++ /dev/null @@ -1,291 +0,0 @@ -//go:build tools -// +build tools - -package specs - -import ( - "io/ioutil" - "log" - - "github.com/pelletier/go-toml" - - "github.com/beyondstorage/go-storage/v4/cmd/definitions/bindata" -) - -type tomlFeature struct { - Description string `toml:"description"` -} - -type tomlFeatures map[string]tomlFeature - -type tomlField struct { - Type string -} - -type tomlFields map[string]tomlField - -type tomlInfo struct { - Type string `toml:"type"` - Export bool `toml:"export"` - Description string `toml:"description"` -} - -type tomlInfos map[string]tomlInfo - -type tomlPair struct { - Type string `toml:"type"` - Defaultable bool `toml:"defaultable"` - Description string `toml:"description,optional"` -} - -type tomlPairs map[string]tomlPair - -type tomlOperation struct { - Description string `toml:"description"` - Params []string `toml:"params"` - Pairs []string `toml:"pairs"` - Results []string `toml:"results"` - ObjectMode string `toml:"object_mode"` - Local bool `toml:"local"` -} - -type tomlInterface struct { - Description string `toml:"description"` - Ops map[string]tomlOperation `toml:"op"` -} - -type tomlInterfaces map[string]tomlInterface - -type tomlOp struct { - Required []string `toml:"required"` - Optional []string `toml:"optional"` -} - -type tomlNamespace struct { - Features []string `toml:"features"` - Implement []string `toml:"implement"` - New tomlOp `toml:"new"` - Op map[string]tomlOp `toml:"op"` -} - -type tomlService struct { - Name string `toml:"name"` - Namespace map[string]tomlNamespace `toml:"namespace"` - Pairs map[string]tomlPair `toml:"pairs"` - Infos map[string]map[string]map[string]tomlInfo `toml:"infos"` -} - -func parseTOML(src []byte, in interface{}) (err error) { - return toml.Unmarshal(src, in) -} - -func parseFeatures() Features { - var tp tomlFeatures - - err := parseTOML(bindata.MustAsset(featurePath), &tp) - if err != nil { - log.Fatalf("parse: %v", err) - } - - var ps Features - - for k, v := range tp { - p := Feature{ - Name: k, - Description: v.Description, - } - - ps = append(ps, p) - } - - return ps -} - -func parsePairs() Pairs { - var tp tomlPairs - - err := parseTOML(bindata.MustAsset(pairPath), &tp) - if err != nil { - log.Fatalf("parse: %v", err) - } - - var ps Pairs - - for k, v := range tp { - p := Pair{ - Name: k, - Type: v.Type, - Defaultable: v.Defaultable, - Description: v.Description, - } - - ps = append(ps, p) - } - - return ps -} - -func parseInfos() Infos { - var ti tomlInfos - var ps Infos - - // Parse object meta - err := parseTOML(bindata.MustAsset(infoObjectMeta), &ti) - if err != nil { - log.Fatalf("parse: %v", err) - } - - for k, v := range ti { - p := Info{ - Scope: "object", - Category: "meta", - Name: k, - Type: v.Type, - Export: v.Export, - Description: v.Description, - } - - ps = append(ps, p) - } - - // Parse storage meta - err = parseTOML(bindata.MustAsset(infoStorageMeta), &ti) - if err != nil { - log.Fatalf("parse: %v", err) - } - - for k, v := range ti { - p := Info{ - Scope: "storage", - Category: "meta", - Name: k, - Type: v.Type, - Export: v.Export, - Description: v.Description, - } - - ps = append(ps, p) - } - - return ps -} - -func parseOperations() Operations { - var ( - ti tomlInterfaces - tf tomlFields - ps Operations - ) - - // Parse operations. - err := parseTOML(bindata.MustAsset(operationPath), &ti) - if err != nil { - log.Fatalf("parse: %v", err) - } - - // Parse fields - err = parseTOML(bindata.MustAsset(fieldPath), &tf) - if err != nil { - log.Fatalf("parse: %v", err) - } - - for k, v := range tf { - ps.Fields = append(ps.Fields, Field{ - Name: k, - Type: v.Type, - }) - } - - for k, v := range ti { - it := Interface{ - Name: k, - Description: v.Description, - } - - for k, v := range v.Ops { - it.Ops = append(it.Ops, Operation{ - Name: k, - Description: v.Description, - Params: v.Params, - Pairs: v.Pairs, - Results: v.Results, - ObjectMode: v.ObjectMode, - Local: v.Local, - }) - } - - ps.Interfaces = append(ps.Interfaces, it) - } - - return ps -} - -func parseService(filePath string) Service { - var ( - ts tomlService - ps Service - ) - - content, err := ioutil.ReadFile(filePath) - if err != nil { - log.Fatalf("read file %s: %v", filePath, err) - } - - err = parseTOML(content, &ts) - if err != nil { - log.Fatalf("parse toml %s: %v", filePath, err) - } - - ps.Name = ts.Name - - // Parse pairs - for k, v := range ts.Pairs { - ps.Pairs = append(ps.Pairs, Pair{ - Name: k, - Type: v.Type, - Defaultable: v.Defaultable, - Description: v.Description, - }) - } - - // Parse infos - for scope, v := range ts.Infos { - for category, v := range v { - for name, v := range v { - ps.Infos = append(ps.Infos, Info{ - Scope: scope, - Category: category, - Name: name, - Type: v.Type, - Export: v.Export, - Description: v.Description, - }) - } - } - } - - // Parse namespace. - for name, v := range ts.Namespace { - n := Namespace{ - Name: name, - Implement: v.Implement, - Features: v.Features, - New: New{ - Required: v.New.Required, - Optional: v.New.Optional, - }, - } - - for opName, op := range v.Op { - n.Op = append(n.Op, Op{ - Name: opName, - Required: op.Required, - Optional: op.Optional, - }) - } - - ps.Namespaces = append(ps.Namespaces, n) - } - - return ps -} diff --git a/cmd/definitions/specs/spec.go b/cmd/definitions/specs/spec.go deleted file mode 100644 index 5b555be56..000000000 --- a/cmd/definitions/specs/spec.go +++ /dev/null @@ -1,201 +0,0 @@ -//go:build tools -// +build tools - -package specs - -import ( - "sort" -) - -// Interface is the spec for interface. -type Interface struct { - Name string - Description string - Ops []Operation -} - -// Operations is the spec for operations. -type Operations struct { - Interfaces []Interface - Fields []Field -} - -// Sort will sort the Operations -func (o *Operations) Sort() { - sort.Slice(o.Fields, func(i, j int) bool { - x := o.Fields - return x[i].Name < x[j].Name - }) - - sort.Slice(o.Interfaces, func(i, j int) bool { - x := o.Interfaces - return x[i].Name < x[j].Name - }) -} - -// Operation is the spec for operation. -type Operation struct { - Name string - Description string - Params []string - Pairs []string - Results []string - ObjectMode string - Local bool -} - -// Field is the spec for field. -type Field struct { - Name string - Type string -} - -// Service is the data parsed from TOML. -type Service struct { - Name string - Namespaces []Namespace - Pairs Pairs - Infos Infos -} - -// Sort will sort the service spec. -func (s *Service) Sort() { - s.Pairs.Sort() - s.Infos.Sort() - - sort.Slice(s.Namespaces, func(i, j int) bool { - ns := s.Namespaces - return ns[i].Name < ns[j].Name - }) - - for _, v := range s.Namespaces { - v.Sort() - } -} - -// Features is all global features that available. -// -// Features will be defined in features.toml. -type Features []Feature - -func (p Features) Sort() { - if p == nil || len(p) == 0 { - return - } - - sort.Slice(p, func(i, j int) bool { - return p[i].Description < p[j].Description - }) -} - -type Feature struct { - Name string - Description string -} - -// Infos is the spec for infos. -type Infos []Info - -// Sort will sort the pair spec. -func (p Infos) Sort() { - if p == nil || len(p) == 0 { - return - } - - sort.Slice(p, func(i, j int) bool { - return compareInfoSpec(p[i], p[j]) - }) -} - -// Info is the spec for info. -type Info struct { - Scope string - Category string - Name string - Type string - Export bool - Description string -} - -type Pairs []Pair - -// Sort will sort the pair spec. -func (p Pairs) Sort() { - if p == nil || len(p) == 0 { - return - } - - sort.Slice(p, func(i, j int) bool { - return p[i].Name < p[j].Name - }) -} - -// Pair is the data parsed from TOML. -type Pair struct { - Name string - Type string - Defaultable bool - Description string -} - -// Namespace is the data parsed from TOML. -type Namespace struct { - Name string - Features []string // The feature names that provided by current namespace. - Implement []string - New New - Op []Op -} - -// Sort will sort the Namespace -func (n *Namespace) Sort() { - n.New.Sort() - - sort.Strings(n.Features) - sort.Strings(n.Implement) - - sort.Slice(n.Op, func(i, j int) bool { - x := n.Op - return x[i].Name < x[j].Name - }) - - for _, v := range n.Op { - v.Sort() - } -} - -// Op means an operation definition. -type Op struct { - Name string - - Required []string - Optional []string -} - -// Sort will sort the Op -func (o *Op) Sort() { - sort.Strings(o.Required) - sort.Strings(o.Optional) -} - -// New is the spec for new function. -type New struct { - Required []string - Optional []string -} - -// Sort will sort the New -func (o *New) Sort() { - sort.Strings(o.Required) - sort.Strings(o.Optional) -} - -func compareInfoSpec(x, y Info) bool { - if x.Scope != y.Scope { - return x.Scope < y.Scope - } - if x.Category != y.Category { - return x.Category < y.Category - } - return x.Name < y.Name -} diff --git a/cmd/definitions/specs/testdata/service.toml b/cmd/definitions/testdata/service.toml similarity index 100% rename from cmd/definitions/specs/testdata/service.toml rename to cmd/definitions/testdata/service.toml diff --git a/cmd/definitions/type.go b/cmd/definitions/type.go index b26b220c3..5738ec42c 100644 --- a/cmd/definitions/type.go +++ b/cmd/definitions/type.go @@ -5,650 +5,693 @@ package main import ( "fmt" + "io/ioutil" "sort" - "strings" "github.com/Xuanwo/templateutils" + "github.com/pelletier/go-toml" log "github.com/sirupsen/logrus" - "github.com/beyondstorage/go-storage/v4/cmd/definitions/specs" + "github.com/beyondstorage/go-storage/v4/cmd/definitions/bindata" ) // Data is the biggest container for all definitions. type Data struct { - Pairs map[string]*Pair - Infos []*Info - InfosMap map[string][]*Info - ObjectMeta []*Info FeaturesMap map[string]*Feature + PairsMap map[string]*Pair + // scope -> category -> name -> info + InfosMap map[string]map[string]map[string]*Info + FieldsMap map[string]*Field + OperationsMap map[string]map[string]*Operation + InterfacesMap map[string]*Interface Service *Service +} - Interfaces []*Interface - interfacesMap map[string]*Interface +// NewData will formatGlobal the whole data. +func NewData() *Data { + data := &Data{} - // Store all specs for encoding - featuresSpec specs.Features - pairSpec specs.Pairs - infoSpec specs.Infos - operationsSpec specs.Operations - serviceSpec specs.Service + data.LoadFeatures() + data.LoadPairs() + data.LoadInfos() + data.LoadFields() + data.LoadOperations() + return data } -// Service is the service definition. -type Service struct { - Name string - Namespaces []*Namespace - pairs map[string]*Pair - Infos []*Info +func (d *Data) Interfaces() []*Interface { + var ins []*Interface + for _, v := range d.InterfacesMap { + v := v + ins = append(ins, v) + } + sort.Slice(ins, func(i, j int) bool { + return ins[i].Name < ins[j].Name + }) + return ins } -// Sort will sort the service -func (s *Service) Sort() { - // Make sure namespaces sorted by name. - sort.Slice(s.Namespaces, func(i, j int) bool { - n := s.Namespaces +func (d *Data) StorageMeta() []*Info { + var infos []*Info + for _, v := range d.InfosMap["storage"]["meta"] { + v := v + infos = append(infos, v) + } - return n[i].Name < n[j].Name + sort.Slice(infos, func(i, j int) bool { + return compareInfo(infos[i], infos[j]) }) - - for _, v := range s.Namespaces { - v.Sort() - } + return infos } -// Pairs returns a sorted pair. -func (s *Service) Pairs() []*Pair { - keys := make([]string, 0, len(s.pairs)) - - for k := range s.pairs { - keys = append(keys, k) +func (d *Data) ObjectMeta() []*Info { + var infos []*Info + for _, v := range d.InfosMap["object"]["meta"] { + v := v + infos = append(infos, v) } - sort.Strings(keys) - ps := make([]*Pair, 0, len(s.pairs)) - for _, v := range keys { - ps = append(ps, s.pairs[v]) - } - return ps + sort.Slice(infos, func(i, j int) bool { + return compareInfo(infos[i], infos[j]) + }) + return infos } -// Namespace contains all info about a namespace -type Namespace struct { - Name string - Features []*Feature - New *Function - Funcs []*Function - Interfaces []*Interface - - HasFeatureLoosePair bool // Add a marker to support feature loose_pair +func (d *Data) LoadFeatures() { + err := parseTOML(bindata.MustAsset(featurePath), &d.FeaturesMap) + if err != nil { + log.Fatalf("parse feature: %v", err) + } + for k, v := range d.FeaturesMap { + v.Name = k + } } -// PairFuncs contains pair and the func names that contain it. -type PairFuncs struct { - Pair *Pair - Funcs []string -} +// LoadPairs will formatGlobal PairsMap for pair spec +func (d *Data) LoadPairs() { + err := parseTOML(bindata.MustAsset(pairPath), &d.PairsMap) + if err != nil { + log.Fatalf("parse pair: %v", err) + } -// Defaultable returns sorted PairFuncs slice for defaultable pairs. -func (n *Namespace) Defaultable() []*PairFuncs { - pfm := make(map[*Pair][]string) - pfs := make([]*PairFuncs, 0) + // Inject pairs + d.PairsMap["context"] = &Pair{ + Type: "context.Context", + } + d.PairsMap["http_client_options"] = &Pair{ + Type: "*httpclient.Options", + } - for _, op := range n.Funcs { - ps := make([]*Pair, 0) - ps = append(ps, op.Required...) - ps = append(ps, op.Optional...) + var defaultPairs []*Pair + for k, v := range d.PairsMap { + v.Name = k + // Pairs from bindata must be global. + v.Global = true - for _, pair := range ps { - if pair.Defaultable { - pfm[pair] = append(pfm[pair], op.Name) - } + if v.Defaultable { + defaultPairs = append(defaultPairs, &Pair{ + Name: fmt.Sprintf("default_%s", v.Name), + Type: v.Type, + Description: v.Description, + Global: true, + }) } } - - for pair, ops := range pfm { - pf := &PairFuncs{Pair: pair, Funcs: ops} - pfs = append(pfs, pf) + for _, v := range defaultPairs { + v := v + d.PairsMap[v.Name] = v } - - sort.Slice(pfs, func(i, j int) bool { - return pfs[i].Pair.Name < pfs[j].Pair.Name - }) - - return pfs } -// Sort will sort the namespace -func (n *Namespace) Sort() { - sort.Slice(n.Funcs, func(i, j int) bool { - x := n.Funcs - return x[i].Name < x[j].Name - }) +func (d *Data) LoadInfos() { + d.InfosMap = map[string]map[string]map[string]*Info{ + "object": { + "meta": nil, + }, + "storage": { + "meta": nil, + }, + } - n.New.Sort() - for _, v := range n.Funcs { - v.Sort() + omm := make(map[string]*Info) + err := parseTOML(bindata.MustAsset(infoObjectMeta), &omm) + if err != nil { + log.Fatalf("parse pair: %v", err) } -} + d.InfosMap["object"]["meta"] = omm -type Feature struct { - Name string - Description string -} + smm := make(map[string]*Info) + err = parseTOML(bindata.MustAsset(infoStorageMeta), &smm) + if err != nil { + log.Fatalf("parse pair: %v", err) + } + d.InfosMap["storage"]["meta"] = smm -func (f *Feature) Format(s specs.Feature) { - f.Name = s.Name - f.Description = s.Description + for scope, v := range d.InfosMap { + for category, v := range v { + for name, v := range v { + v.Name = name + v.Category = category + v.Scope = scope + // SortedInfos from bindata must be global. + v.Global = true + } + } + } } -// Pair is the pair definition. -type Pair struct { - Name string - - ptype string - - Defaultable bool - - // Runtime generated - Global bool - Description string +func (d *Data) LoadFields() { + err := parseTOML(bindata.MustAsset(fieldPath), &d.FieldsMap) + if err != nil { + log.Fatalf("parse field: %v", err) + } + for k, v := range d.FieldsMap { + v.Name = k + } } -func (p *Pair) Type() string { - return parseType(p.ptype) -} +func (d *Data) LoadOperations() { + err := parseTOML(bindata.MustAsset(operationPath), &d.InterfacesMap) + if err != nil { + log.Fatalf("parse operations: %v", err) + } -// Format will formatGlobal current pair -func (p *Pair) Format(s specs.Pair, global bool) { - p.Name = s.Name - p.ptype = s.Type - p.Global = global - p.Defaultable = s.Defaultable - p.Description = s.Description -} + d.OperationsMap = map[string]map[string]*Operation{ + "service": make(map[string]*Operation), + "storage": make(map[string]*Operation), + } + for k, v := range d.InterfacesMap { + v.Name = k -// Info is the metadata definition. -type Info struct { - Scope string - Category string - Name string - Export bool - Description string + for name, op := range v.Op { + op.Name = name + op.d = d - itype string + op.Params = append(op.Params, "pairs") + if !op.Local { + op.Results = append(op.Results, "err") + } - Global bool + op := op + if k == "servicer" { + d.OperationsMap["service"][op.Name] = op + } else { + d.OperationsMap["storage"][op.Name] = op + } + } + } } -// Format will formatGlobal info spec into Info -func (i *Info) Format(s specs.Info, global bool) { - i.Scope = s.Scope - i.Category = s.Category - i.Name = s.Name - i.itype = s.Type - i.Export = s.Export - i.Description = s.Description - - i.Global = global -} +// ValidateNamespace will inject a namespace to insert generated PairsMap. +func (d *Data) ValidateNamespace(n *Namespace) { + for _, v := range n.ParsedFunctions() { + // For now, we disallow required Pairs for Storage. + if n.Name == "Storage" && len(v.Required) > 0 { + log.Fatalf("Operation [%s] cannot specify required Pairs.", v.Name) + } -func (i *Info) Type() string { - return parseType(i.itype) -} + existPairs := map[string]bool{} + log.Infof("check function %s", v.Name) + for _, p := range v.Optional { + existPairs[p] = true + } -func (i *Info) TypeName() string { - if i.Export { - return templateutils.ToPascal(i.Name) - } else { - return templateutils.ToCamel(i.Name) + op := v.GetOperation() + for _, ps := range op.Pairs { + if existPairs[ps] { + continue + } + log.Fatalf("Operation [%s] requires Pair [%s] support, please add virtual implementation for this pair.", v.Name, ps) + } } } -func (i *Info) DisplayName() string { - return templateutils.ToPascal(i.Name) -} - -// Interface represents an interface -type Interface struct { - Name string - Description string - Ops map[string]*Operation -} -// NewInterface will create a new interface from spec. -func NewInterface(in specs.Interface, fields map[string]*Field) *Interface { - inter := &Interface{ - Name: in.Name, - Description: in.Description, - Ops: make(map[string]*Operation), - } - for _, v := range in.Ops { - // Update op maps - inter.Ops[v.Name] = NewOperation(v, fields) +func (d *Data) LoadService(filePath string) { + bs, err := ioutil.ReadFile(filePath) + if err != nil { + log.Fatalf("read file %s: %v", filePath, err) } - return inter -} - -func (i *Interface) SortedOps() []*Operation { - var ks []string - for _, v := range i.Ops { - ks = append(ks, v.Name) + d.Service = &Service{ + d: d, } - sort.Strings(ks) - - var ops []*Operation - for _, v := range ks { - ops = append(ops, i.Ops[v]) + err = parseTOML(bs, d.Service) + if err != nil { + log.Fatalf("parse service: %v", err) } - return ops -} -// DisplayName will output interface's display name. -func (i *Interface) DisplayName() string { - return templateutils.ToPascal(i.Name) -} + srv := d.Service -// Operation represents an operation. -type Operation struct { - Name string - Description string - Pairs []string - Params Fields - Results Fields - ObjectMode string - Local bool -} + // Handle pairs + var defaultPairs []*Pair + for k, v := range srv.Pairs { + v.Name = k -// NewOperation will create an new operation from operation spec. -func NewOperation(v specs.Operation, fields map[string]*Field) *Operation { - op := &Operation{ - Name: v.Name, - Local: v.Local, - ObjectMode: v.ObjectMode, - Description: v.Description, + if v.Defaultable { + defaultPairs = append(defaultPairs, &Pair{ + Name: fmt.Sprintf("default_%s", v.Name), + Type: v.Type, + Description: v.Description, + }) + } } - for _, f := range v.Params { - op.Params = append(op.Params, fields[f]) + for _, v := range defaultPairs { + v := v + srv.Pairs[v.Name] = v } - // Inject pairs - op.Params = append(op.Params, fields["pairs"]) - for _, f := range v.Results { - op.Results = append(op.Results, fields[f]) - } - // As long as the function is not local, an error may be occur - if !op.Local { - // Inject error for non-local functions. - op.Results = append(op.Results, fields["err"]) + // Handle all infos + for scope, v := range srv.Infos { + for category, v := range v { + for name, v := range v { + v.Name = name + v.Category = category + v.Scope = scope + } + } } - // Add pairs - op.Pairs = v.Pairs - - return op -} + // Handle namespace. + for name, ns := range srv.Namespaces { + ns.Name = name + ns.srv = srv + // Append namespace itself into implement. + ns.Implement = append(ns.Implement, ns.Name+"r") -// Function represents a function. -type Function struct { - *Operation + // Handle features. + for _, featureName := range ns.Features { + f, ok := d.FeaturesMap[featureName] + if !ok { + log.Fatalf("feature not registered: %s", featureName) + } - Simulated bool // This op is simulated, user can decide whether use the virtual function or not. + if featureName == "loose_pair" { + ns.HasFeatureLoosePair = true + } - Required []*Pair // TODO: other functions could not have required pairs. - Optional []*Pair - Virtual []*Pair // This op's virtual pairs, user can decide whether use the virtual pairs. + // Generate enable feature pairs. + pairName := fmt.Sprintf("enable_%s", featureName) + srv.Pairs[pairName] = &Pair{ + Name: pairName, + Type: "bool", + Description: f.Description, + } + } - Implemented bool // flag for whether this function has been implemented or not. -} + // Handle New function. + if ns.New == nil { + ns.New = &Function{} + } + ns.New.Name = "new" + ns.New.srv = srv + ns.New.ns = ns + ns.New.Implemented = true + for _, v := range d.PairsMap { + if v.Defaultable { + ns.New.Optional = append(ns.New.Optional, "default_"+v.Name) + } + } + for _, v := range srv.Pairs { + if v.Defaultable { + ns.New.Optional = append(ns.New.Optional, "default_"+v.Name) + } + } -// NewFunction will createn a new function. -func NewFunction(o *Operation) *Function { - return &Function{Operation: o} -} + // Handle other functions. + for k, v := range ns.Op { + v.srv = srv + v.ns = ns + v.Name = k + v.Implemented = true + } -// Format will formatGlobal a function with Op. -func (f *Function) Format(s specs.Op, p map[string]*Pair) { - for _, v := range s.Required { - pair, ok := p[v] - if !ok { - log.Fatalf("pair %s is not exist", v) + // Service could not declare all ops, so we need to fill them instead. + for _, implement := range ns.Implement { + in := d.InterfacesMap[implement] + + for _, op := range in.Op { + if _, ok := ns.Op[op.Name]; ok { + continue + } + ns.Op[op.Name] = &Function{ + srv: srv, + ns: ns, + Name: op.Name, + Implemented: false, + } + } } - f.Required = append(f.Required, pair) - } - for _, v := range s.Optional { - pair, ok := p[v] - if !ok { - log.Fatalf("pair %s is not exist", v) + + implemented := parseFunc(ns.Name) + for k := range implemented { + sk := templateutils.ToSnack(k) + if _, ok := ns.Op[sk]; !ok { + continue + } + ns.Op[sk].Implemented = true } - f.Optional = append(f.Optional, pair) + + //d.ValidateNamespace(ns) } } -// Sort will sort this function. -func (f *Function) Sort() { - sort.Slice(f.Required, func(i, j int) bool { - x := f.Required - return x[i].Name < x[j].Name - }) - sort.Slice(f.Optional, func(i, j int) bool { - x := f.Optional - return x[i].Name < x[j].Name - }) -} +// Service is the service definition. +type Service struct { + d *Data -// Fields is a slice for field. -type Fields []*Field + Name string `toml:"name"` + Namespaces map[string]*Namespace `toml:"namespace"` + Pairs map[string]*Pair `toml:"pairs"` + // scope -> category -> name -> info + Infos map[string]map[string]map[string]*Info `toml:"infos"` +} -// String implements the stringer interface. -func (f Fields) String() string { - x := make([]string, 0) - for _, v := range f { - x = append(x, v.String()) +func (s *Service) SortedNamespaces() []*Namespace { + var ns []*Namespace + for _, v := range s.Namespaces { + v := v + ns = append(ns, v) } - return strings.Join(x, ",") -} -// Field represent a field. -type Field struct { - Name string - ftype string + sort.Slice(ns, func(i, j int) bool { + return ns[i].Name < ns[j].Name + }) + return ns } -// String will print field in string formatGlobal. -func (f *Field) String() string { - if f.Name == "" { - return f.Type() +// SortedPairs returns a sorted pair. +func (s *Service) SortedPairs() []*Pair { + var ps []*Pair + + for _, v := range s.d.PairsMap { + v := v + ps = append(ps, v) } - return fmt.Sprintf("%s %s", f.Name, f.Type()) + for _, v := range s.Pairs { + v := v + ps = append(ps, v) + } + sort.Slice(ps, func(i, j int) bool { + return ps[i].Name < ps[j].Name + }) + return ps } -func (f *Field) Type() string { - return f.ftype -} +func (s *Service) SortedInfos() []*Info { + var infos []*Info + for _, v := range s.Infos { + for _, v := range v { + for _, v := range v { + infos = append(infos, v) + } + } + } -// Format will create a new field. -func (f *Field) Format(s specs.Field) { - f.ftype = s.Type - f.Name = s.Name + sort.Slice(infos, func(i, j int) bool { + return compareInfo(infos[i], infos[j]) + }) + return infos } -func (d *Data) FormatFeatures(p specs.Features) map[string]*Feature { - m := make(map[string]*Feature) - for _, v := range p { - f := &Feature{} - f.Format(v) +func (s *Service) GetPair(name string) *Pair { + p, ok := s.d.PairsMap[name] + if ok { + return p + } - m[f.Name] = f + p, ok = s.Pairs[name] + if ok { + return p } - return m + + log.Fatalf("pair %s is not registered", name) + return nil } -// FormatPairs will formatGlobal pairs for pair spec -func (d *Data) FormatPairs(p specs.Pairs, global bool) map[string]*Pair { - m := make(map[string]*Pair) - for _, v := range p { - pair := &Pair{} - pair.Format(v, global) +// Namespace contains all info about a namespace +type Namespace struct { + Features []string `toml:"features"` + Implement []string `toml:"implement"` + New *Function `toml:"new"` + Op map[string]*Function `toml:"op"` - m[pair.Name] = pair - } - return m + // Runtime generated + srv *Service + Name string + HasFeatureLoosePair bool // Add a marker to support feature loose_pair } -// FormatInfos will formatGlobal metas for meta spec -func (d *Data) FormatInfos(m specs.Infos, global bool) []*Info { - is := make([]*Info, 0, len(m)) - for _, v := range m { - i := &Info{} - i.Format(v, global) +func (ns *Namespace) ParsedFeatures() []*Feature { + var ps []*Feature - is = append(is, i) + for _, v := range ns.Features { + f, ok := ns.srv.d.FeaturesMap[v] + if !ok { + log.Fatalf("feature not registered: %s", v) + } + ps = append(ps, f) } + sort.Slice(ps, func(i, j int) bool { + return ps[i].Name < ps[j].Name + }) + return ps +} - d.InfosMap = make(map[string][]*Info) - for _, v := range is { - v := v - - typeName := fmt.Sprintf("%s-%s", v.Scope, v.Category) - if typeName == "object-meta" { - d.ObjectMeta = append(d.ObjectMeta, v) - continue +func (ns *Namespace) ParsedInterfaces() []*Interface { + var is []*Interface + for _, name := range ns.Implement { + i, ok := ns.srv.d.InterfacesMap[name] + if !ok { + log.Fatalf("interface %s is not registered", name) } - d.InfosMap[typeName] = append(d.InfosMap[typeName], v) + is = append(is, i) } + sort.Slice(is, func(i, j int) bool { + return is[i].Name < is[j].Name + }) return is } -// FormatOperations will formatGlobal operations from operation spec -func (d *Data) FormatOperations(o specs.Operations) (ins []*Interface, inm map[string]*Interface) { - fileds := make(map[string]*Field) - for _, v := range o.Fields { - f := &Field{} - f.Format(v) +func (ns *Namespace) ParsedFunctions() []*Function { + var fns []*Function - fileds[v.Name] = f - } - - // Build all interfaces. - inm = make(map[string]*Interface) - for _, in := range o.Interfaces { - inter := NewInterface(in, fileds) - - ins = append(ins, inter) - inm[inter.Name] = inter + for _, v := range ns.Op { + v := v + fns = append(fns, v) } - return + sort.Slice(fns, func(i, j int) bool { + return fns[i].Name < fns[j].Name + }) + return fns } -// FormatNamespace will formatGlobal a namespace. -func (d *Data) FormatNamespace(srv *Service, n specs.Namespace) *Namespace { - ns := &Namespace{Name: n.Name} +type pairFunc struct { + Pair *Pair + Funcs []*Function +} - nsInterface := n.Name + "r" +func (ns *Namespace) ParsedDefaultable() []*pairFunc { + m := make(map[*Pair][]*Function) - // Handle features - for _, featureName := range n.Features { - f, ok := d.FeaturesMap[featureName] - if !ok { - log.Fatalf("feature not registered: %s", featureName) + for _, v := range ns.ParsedFunctions() { + v := v + for _, name := range v.Optional { + p := ns.srv.GetPair(name) + if p.Defaultable { + m[p] = append(m[p], v) + } } + } - if f.Name == "loose_pair" { - ns.HasFeatureLoosePair = true + var ps []*pairFunc + for p, fn := range m { + p, fn := p, fn + pfn := &pairFunc{ + Pair: p, + Funcs: fn, } + sort.Slice(pfn.Funcs, func(i, j int) bool { + return pfn.Funcs[i].Name < pfn.Funcs[j].Name + }) + ps = append(ps, pfn) + } - ns.Features = append(ns.Features, f) + sort.Slice(ps, func(i, j int) bool { + return ps[i].Pair.Name < ps[j].Pair.Name + }) + return ps +} - // Generate feature pairs. - name := fmt.Sprintf("enable_%s", featureName) - featurePair := &Pair{ - Name: name, - ptype: "bool", - Global: false, - Defaultable: false, - Description: f.Description, - } - srv.pairs[featurePair.Name] = featurePair - } +// Feature is all global features that available. +// +// Feature will be defined in features.toml. +type Feature struct { + Description string `toml:"description"` - // Handle New function - ns.New = NewFunction(&Operation{Name: "new"}) - ns.New.Format(specs.Op{ - Required: n.New.Required, - Optional: n.New.Optional, - }, srv.pairs) + // Runtime generated. + Name string +} - // Handle other interfaces. - fns := make(map[string]*Function) +// Pair is the pair definition. +type Pair struct { + Name string + Type string `toml:"type"` + Defaultable bool `toml:"defaultable"` + Description string `toml:"description"` - // Add namespace itself into implements. - implements := append(n.Implement[:], nsInterface) - for _, interfaceName := range implements { - inter := d.interfacesMap[interfaceName] + // Runtime generated + Global bool +} - // Add interface into namespace's interface list. - ns.Interfaces = append(ns.Interfaces, inter) +// Info is the metadata definition. +type Info struct { + Export bool `toml:"export"` + Description string `toml:"description"` + Type string `toml:"type"` - // Add all functions under interface into namespace's func list. - for k, v := range inter.Ops { - v := v + // Runtime generated. + Scope string + Category string + Name string + Global bool +} - f := NewFunction(v) - ns.Funcs = append(ns.Funcs, f) - fns[k] = f - } +func (i *Info) TypeName() string { + if i.Export { + return templateutils.ToPascal(i.Name) + } else { + return templateutils.ToCamel(i.Name) } +} - for _, v := range n.Op { - fns[v.Name].Format(v, srv.pairs) - } +func (i *Info) DisplayName() string { + return templateutils.ToPascal(i.Name) +} - implemented := parseFunc(n.Name) - for _, fn := range fns { - x := templateutils.ToCamel(fn.Name) - if _, ok := implemented[x]; ok { - fn.Implemented = true - } +// Interface represents an interface +type Interface struct { + Description string `toml:"description"` + Op map[string]*Operation `toml:"op"` + + // Runtime generated + Name string +} + +func (i *Interface) SortedOps() []*Operation { + var ops []*Operation + + for _, v := range i.Op { + v := v + ops = append(ops, v) } - d.ValidateNamespace(srv, ns) - return ns + sort.Slice(ops, func(i, j int) bool { + return ops[i].Name < ops[j].Name + }) + return ops } -// ValidateNamespace will inject a namespace to insert generated pairs. -func (d *Data) ValidateNamespace(srv *Service, n *Namespace) { - for _, v := range n.Funcs { - // For now, we disallow required pairs for Storage. - if n.Name == "Storage" && len(v.Required) > 0 { - log.Fatalf("Operation [%s] cannot specify required pairs.", v.Name) - } +// DisplayName will output interface's display name. +func (i *Interface) DisplayName() string { + return templateutils.ToPascal(i.Name) +} - existPairs := map[string]bool{} - for _, p := range v.Optional { - existPairs[p.Name] = true - } - for _, p := range v.Virtual { - existPairs[p.Name] = true - } +// Operation represents an operation. +type Operation struct { + Description string `toml:"description"` + Pairs []string `toml:"pairs"` + Params []string `toml:"params"` + Results []string `toml:"results"` + ObjectMode string `toml:"object_mode"` + Local bool `toml:"local"` + + // Runtime generated. + d *Data + Name string +} - for _, ps := range v.Pairs { - if existPairs[ps] { - continue - } - log.Fatalf("Operation [%s] requires Pair [%s] support, please add virtual implementation for this pair.", v.Name, ps) - } +func (op *Operation) ParsedParams() []*Field { + var fs []*Field + for _, f := range op.Params { + fs = append(fs, op.d.FieldsMap[f]) } + return fs } -// FormatService will formatGlobal services from service spec -func (d *Data) FormatService(s specs.Service) *Service { - d.serviceSpec = s - - srv := &Service{ - Name: s.Name, - pairs: mergePairs(d.Pairs, d.FormatPairs(s.Pairs, false)), - Infos: mergeInfos(d.Infos, d.FormatInfos(s.Infos, false)), +func (op *Operation) ParsedResults() []*Field { + var fs []*Field + for _, f := range op.Results { + fs = append(fs, op.d.FieldsMap[f]) } + return fs +} - for _, v := range s.Namespaces { - ns := d.FormatNamespace(srv, v) +// Function represents a function. +type Function struct { + Required []string `toml:"required"` + Optional []string `toml:"optional"` + + // Runtime generated. + srv *Service + ns *Namespace + Name string + Implemented bool // flag for whether this function has been implemented or not. +} - srv.Namespaces = append(srv.Namespaces, ns) +func (f *Function) ParsedRequired() []*Pair { + var ps []*Pair + for _, v := range f.Required { + ps = append(ps, f.srv.GetPair(v)) } - return srv + sort.Slice(ps, func(i, j int) bool { + return ps[i].Name < ps[j].Name + }) + return ps } -// Sort will sort the data. -func (d *Data) Sort() { - // Sort all specs. - d.pairSpec.Sort() - d.infoSpec.Sort() - d.operationsSpec.Sort() +func (f *Function) ParsedOptional() []*Pair { + var ps []*Pair + for _, v := range f.Optional { + ps = append(ps, f.srv.GetPair(v)) + } - d.serviceSpec.Sort() + sort.Slice(ps, func(i, j int) bool { + return ps[i].Name < ps[j].Name + }) + return ps +} - if d.Service != nil { - d.Service.Sort() +func (f *Function) GetOperation() *Operation { + op, ok := f.srv.d.OperationsMap[f.ns.Name][f.Name] + if !ok { + log.Fatalf("operation %s in namespace %s is not registered", f.Name, f.ns.Name) } + return op } -// NewData will formatGlobal the whole data. -func NewData() *Data { - f := specs.ParsedFeatures - p := specs.ParsedPairs - m := specs.ParsedInfos - o := specs.ParsedOperations - - data := &Data{ - pairSpec: p, - infoSpec: m, - operationsSpec: o, - featuresSpec: f, - } - data.FeaturesMap = data.FormatFeatures(f) - data.Pairs = data.FormatPairs(p, true) - data.Infos = data.FormatInfos(m, true) - data.Interfaces, data.interfacesMap = data.FormatOperations(o) +// Field represents a field. +type Field struct { + Type string `toml:"type"` - return data + // Runtime generated. + Name string } -func mergePairs(global, service map[string]*Pair) map[string]*Pair { - ans := make(map[string]*Pair) - for k, v := range global { - v := v - ans[k] = v - // Handle global defaultable pairs. - if v.Defaultable { - name := fmt.Sprintf("default_%s", v.Name) - pair := &Pair{ - Name: name, - ptype: v.ptype, - Global: true, - Defaultable: false, - Description: v.Description, - } - ans[name] = pair - } - } - for k, v := range service { - if _, ok := ans[k]; ok { - log.Fatalf("pair conflict: %s", k) - } - v := v - ans[k] = v - // Handle system defaultable pairs. - if v.Defaultable { - name := fmt.Sprintf("default_%s", v.Name) - pair := &Pair{ - Name: name, - ptype: v.ptype, - Global: false, - Defaultable: false, - Description: v.Description, - } - ans[name] = pair - } - } - return ans +func parseTOML(src []byte, in interface{}) (err error) { + return toml.Unmarshal(src, in) } -func mergeInfos(a, b []*Info) []*Info { - fn := func(ms ...[]*Info) []*Info { - ans := make([]*Info, 0) - for _, m := range ms { - for _, v := range m { - v := v - ans = append(ans, v) - } - } - return ans +func compareInfo(x, y *Info) bool { + if x.Scope != y.Scope { + return x.Scope < y.Scope } - - return fn(a, b) + if x.Category != y.Category { + return x.Category < y.Category + } + return x.Name < y.Name } diff --git a/doc.go b/doc.go index db0acfc65..35508f6ce 100644 --- a/doc.go +++ b/doc.go @@ -34,4 +34,3 @@ package storage //go:generate go run github.com/kevinburke/go-bindata/go-bindata -nometadata -o ./cmd/definitions/bindata/bindata.go -pkg bindata -tags tools ./definitions //go:generate go run -tags tools ./cmd/definitions -//go:generate go run -tags tools ./internal/cmd/iterator diff --git a/go.mod b/go.mod index b08169b1d..4fd1fe8ff 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/beyondstorage/go-storage/v4 go 1.15 require ( - github.com/Xuanwo/gg v0.1.0 + github.com/Xuanwo/gg v0.2.0 github.com/Xuanwo/templateutils v0.1.0 github.com/dave/dst v0.26.2 github.com/golang/mock v1.6.0 @@ -12,4 +12,5 @@ require ( github.com/pelletier/go-toml v1.9.4 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 + github.com/urfave/cli/v2 v2.3.0 ) diff --git a/go.sum b/go.sum index ca473e70d..f82647bf6 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,12 @@ -github.com/Xuanwo/gg v0.1.0 h1:VGP9kLM71YCZUU73YN9XVQjd1K4hO6Ka3pR6r4zjYOg= -github.com/Xuanwo/gg v0.1.0/go.mod h1:0fLiiSxR87u2UA0ZNZiKZXuz3jnJdbDHWtU2xpdcH3s= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Xuanwo/gg v0.2.0 h1:axbZmA0qmidh3s9PA86GqvBXVQ3o7Bbpf0aImGtlimA= +github.com/Xuanwo/gg v0.2.0/go.mod h1:0fLiiSxR87u2UA0ZNZiKZXuz3jnJdbDHWtU2xpdcH3s= github.com/Xuanwo/go-bufferpool v0.2.0 h1:DXzqJD9lJufXbT/03GrcEvYOs4gXYUj9/g5yi6Q9rUw= github.com/Xuanwo/go-bufferpool v0.2.0/go.mod h1:Mle++9GGouhOwGj52i9PJLNAPmW2nb8PWBP7JJzNCzk= github.com/Xuanwo/templateutils v0.1.0 h1:WpkWOqQtIQ2vAIpJLa727DdN8WtxhUkkbDGa6UhntJY= github.com/Xuanwo/templateutils v0.1.0/go.mod h1:OdE0DJ+CJxDBq6psX5DPV+gOZi8bhuHuVUpPCG++Wb8= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/dave/dst v0.26.2 h1:lnxLAKI3tx7MgLNVDirFCsDTlTG9nKTk7GcptKcWSwY= github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU= github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= @@ -30,14 +33,20 @@ github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhEC github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= @@ -79,5 +88,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek= gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cmd/iterator/main.go b/internal/cmd/iterator/main.go deleted file mode 100644 index ac9400001..000000000 --- a/internal/cmd/iterator/main.go +++ /dev/null @@ -1,124 +0,0 @@ -//go:build tools -// +build tools - -package main - -import ( - "fmt" - "os" - "text/template" - - log "github.com/sirupsen/logrus" -) - -func main() { - data := map[string]string{ - "Object": "*Object", - "Storager": "Storager", - "Part": "*Part", - "Block": "*Block", - } - - generateT(tmpl, "types/iterator.generated.go", data) -} - -func generateT(tmpl *template.Template, filePath string, data interface{}) { - errorMsg := fmt.Sprintf("generate template %s to %s", tmpl.Name(), filePath) + ": %v" - - file, err := os.Create(filePath) - if err != nil { - log.Fatalf(errorMsg, err) - } - err = tmpl.Execute(file, data) - if err != nil { - log.Fatalf(errorMsg, err) - } -} - -var tmpl = template.Must(template.New("iterator").Parse(` -// Code generated by go generate internal/cmd; DO NOT EDIT. -package types - -import ( - "context" - "errors" - "fmt" -) - -type Continuable interface { - ContinuationToken() string -} - -{{- range $k, $v := . }} -/* -NextObjectFunc is the func used in iterator. - -Notes -- ErrDone should be return while there are no items any more. -- Input objects slice should be set every time. -*/ -type Next{{$k}}Func func(ctx context.Context, page *{{$k}}Page) error - -type {{$k}}Page struct { - Status Continuable - Data []{{$v}} -} - -type {{$k}}Iterator struct { - ctx context.Context - next Next{{$k}}Func - - index int - done bool - - o {{$k}}Page -} - -func New{{$k}}Iterator(ctx context.Context, next Next{{$k}}Func, status Continuable) *{{$k}}Iterator { - return &{{$k}}Iterator{ - ctx: ctx, - next: next, - index: 0, - done: false, - o: {{$k}}Page{ - Status: status, - }, - } -} - -func (it *{{$k}}Iterator) ContinuationToken() string { - return it.o.Status.ContinuationToken() -} - -func (it *{{$k}}Iterator) Next() (object {{$v}}, err error) { - // Consume Data via index. - if it.index < len(it.o.Data) { - it.index++ - return it.o.Data[it.index-1], nil - } - // Return IterateDone if iterator is already done. - if it.done { - return nil, IterateDone - } - - // Reset buf before call next. - it.o.Data = it.o.Data[:0] - - err = it.next(it.ctx ,&it.o) - if err != nil && !errors.Is(err, IterateDone) { - return nil, fmt.Errorf("iterator next failed: %w", err) - } - // Make iterator to done so that we will not fetch from upstream anymore. - if err != nil { - it.done = true - } - // Return IterateDone directly if we don't have any data. - if len(it.o.Data) == 0 { - return nil, IterateDone - } - // Return the first object. - it.index = 1 - return it.o.Data[0], nil -} -{{- end }} -`)) diff --git a/pairs/generated.go b/pairs/generated.go index 77e1c2f25..c586dc24e 100644 --- a/pairs/generated.go +++ b/pairs/generated.go @@ -23,13 +23,6 @@ func WithContentType(v string) (p Pair) { return Pair{Key: "content_type", Value: v} } -// WithDefaultContentType will apply default_content_type value to Options. -// -// DefaultContentType -func WithDefaultContentType(v string) (p Pair) { - return Pair{Key: "default_content_type", Value: v} -} - // WithContext will apply context value to Options. // // Context @@ -51,6 +44,20 @@ func WithCredential(v string) (p Pair) { return Pair{Key: "credential", Value: v} } +// WithDefaultContentType will apply default_content_type value to Options. +// +// DefaultContentType +func WithDefaultContentType(v string) (p Pair) { + return Pair{Key: "default_content_type", Value: v} +} + +// WithDefaultIoCallback will apply default_io_callback value to Options. +// +// DefaultIoCallback specify what todo every time we read data from source +func WithDefaultIoCallback(v func([]byte)) (p Pair) { + return Pair{Key: "default_io_callback", Value: v} +} + // WithEndpoint will apply endpoint value to Options. // // Endpoint specify how to provide endpoint for service or storage @@ -88,13 +95,6 @@ func WithIoCallback(v func([]byte)) (p Pair) { return Pair{Key: "io_callback", Value: v} } -// WithDefaultIoCallback will apply default_io_callback value to Options. -// -// DefaultIoCallback specify what todo every time we read data from source -func WithDefaultIoCallback(v func([]byte)) (p Pair) { - return Pair{Key: "default_io_callback", Value: v} -} - // WithListMode will apply list_mode value to Options. // // ListMode diff --git a/tests/doc.go b/tests/doc.go index 6de9b1477..ec89949c9 100644 --- a/tests/doc.go +++ b/tests/doc.go @@ -6,4 +6,4 @@ If the test failed, the generator SHOULD NOT be used in specific service. package tests -//go:generate go run -tags tools github.com/beyondstorage/go-storage/v4/cmd/definitions service.toml +//go:generate go run -tags tools github.com/beyondstorage/go-storage/v4/cmd/definitions --debug service.toml diff --git a/tests/generated.go b/tests/generated.go index f556a4ed2..b5b038daa 100644 --- a/tests/generated.go +++ b/tests/generated.go @@ -163,8 +163,14 @@ type pairServiceNew struct { HasCredential bool Credential string // Optional pairs + HasDefaultContentType bool + DefaultContentType string + HasDefaultIoCallback bool + DefaultIoCallback func([]byte) HasDefaultServicePairs bool DefaultServicePairs DefaultServicePairs + HasDefaultStorageClass bool + DefaultStorageClass string HasEndpoint bool Endpoint string HasHTTPClientOptions bool @@ -172,7 +178,6 @@ type pairServiceNew struct { HasServiceFeatures bool ServiceFeatures ServiceFeatures // Enable features - // Default pairs } // parsePairServiceNew will parse Pair slice into *pairServiceNew @@ -188,12 +193,30 @@ func parsePairServiceNew(opts []Pair) (pairServiceNew, error) { } result.HasCredential = true result.Credential = v.Value.(string) + case "default_content_type": + if result.HasDefaultContentType { + continue + } + result.HasDefaultContentType = true + result.DefaultContentType = v.Value.(string) + case "default_io_callback": + if result.HasDefaultIoCallback { + continue + } + result.HasDefaultIoCallback = true + result.DefaultIoCallback = v.Value.(func([]byte)) case "default_service_pairs": if result.HasDefaultServicePairs { continue } result.HasDefaultServicePairs = true result.DefaultServicePairs = v.Value.(DefaultServicePairs) + case "default_storage_class": + if result.HasDefaultStorageClass { + continue + } + result.HasDefaultStorageClass = true + result.DefaultStorageClass = v.Value.(string) case "endpoint": if result.HasEndpoint { continue @@ -455,6 +478,12 @@ type pairStorageNew struct { HasName bool Name string // Optional pairs + HasDefaultContentType bool + DefaultContentType string + HasDefaultIoCallback bool + DefaultIoCallback func([]byte) + HasDefaultStorageClass bool + DefaultStorageClass string HasDefaultStoragePairs bool DefaultStoragePairs DefaultStoragePairs HasDisableURICleaning bool @@ -472,13 +501,6 @@ type pairStorageNew struct { EnableLoosePair bool hasEnableVirtualDir bool EnableVirtualDir bool - // Default pairs - hasDefaultContentType bool - DefaultContentType string - hasDefaultIoCallback bool - DefaultIoCallback func([]byte) - hasDefaultStorageClass bool - DefaultStorageClass string } // parsePairStorageNew will parse Pair slice into *pairStorageNew @@ -494,6 +516,24 @@ func parsePairStorageNew(opts []Pair) (pairStorageNew, error) { } result.HasName = true result.Name = v.Value.(string) + case "default_content_type": + if result.HasDefaultContentType { + continue + } + result.HasDefaultContentType = true + result.DefaultContentType = v.Value.(string) + case "default_io_callback": + if result.HasDefaultIoCallback { + continue + } + result.HasDefaultIoCallback = true + result.DefaultIoCallback = v.Value.(func([]byte)) + case "default_storage_class": + if result.HasDefaultStorageClass { + continue + } + result.HasDefaultStorageClass = true + result.DefaultStorageClass = v.Value.(string) case "default_storage_pairs": if result.HasDefaultStoragePairs { continue @@ -542,38 +582,20 @@ func parsePairStorageNew(opts []Pair) (pairStorageNew, error) { } result.hasEnableVirtualDir = true result.EnableVirtualDir = true - case "default_content_type": - if result.hasDefaultContentType { - continue - } - result.hasDefaultContentType = true - result.DefaultContentType = v.Value.(string) - case "default_io_callback": - if result.hasDefaultIoCallback { - continue - } - result.hasDefaultIoCallback = true - result.DefaultIoCallback = v.Value.(func([]byte)) - case "default_storage_class": - if result.hasDefaultStorageClass { - continue - } - result.hasDefaultStorageClass = true - result.DefaultStorageClass = v.Value.(string) } } // Enable features - if result.hasDefaultContentType { + if result.HasDefaultContentType { result.HasDefaultStoragePairs = true result.DefaultStoragePairs.Write = append(result.DefaultStoragePairs.Write, WithContentType(result.DefaultContentType)) } - if result.hasDefaultIoCallback { + if result.HasDefaultIoCallback { result.HasDefaultStoragePairs = true result.DefaultStoragePairs.Read = append(result.DefaultStoragePairs.Read, WithIoCallback(result.DefaultIoCallback)) result.DefaultStoragePairs.Write = append(result.DefaultStoragePairs.Write, WithIoCallback(result.DefaultIoCallback)) } - if result.hasDefaultStorageClass { + if result.HasDefaultStorageClass { result.HasDefaultStoragePairs = true result.DefaultStoragePairs.Write = append(result.DefaultStoragePairs.Write, WithStorageClass(result.DefaultStorageClass)) } diff --git a/types/iterator.generated.go b/types/iterator.generated.go index 941edc99d..7eeb8049d 100644 --- a/types/iterator.generated.go +++ b/types/iterator.generated.go @@ -1,4 +1,4 @@ -// Code generated by go generate internal/cmd; DO NOT EDIT. +// Code generated by go generate cmd/definitions; DO NOT EDIT. package types import ( @@ -11,20 +11,16 @@ type Continuable interface { ContinuationToken() string } -/* -NextObjectFunc is the func used in iterator. - -Notes -- ErrDone should be return while there are no items any more. -- Input objects slice should be set every time. -*/ +// NextBlockFunc is the func used in iterator. +// +// Notes +// - ErrDone should be return while there are no items any more. +// - Input Block slice should be set every time. type NextBlockFunc func(ctx context.Context, page *BlockPage) error - type BlockPage struct { Status Continuable Data []*Block } - type BlockIterator struct { ctx context.Context next NextBlockFunc @@ -36,22 +32,13 @@ type BlockIterator struct { } func NewBlockIterator(ctx context.Context, next NextBlockFunc, status Continuable) *BlockIterator { - return &BlockIterator{ - ctx: ctx, - next: next, - index: 0, - done: false, - o: BlockPage{ - Status: status, - }, - } + return &BlockIterator{ctx: ctx, next: next, o: BlockPage{Status: status}} } - func (it *BlockIterator) ContinuationToken() string { return it.o.Status.ContinuationToken() } - func (it *BlockIterator) Next() (object *Block, err error) { + // Consume Data via index. // Consume Data via index. if it.index < len(it.o.Data) { it.index++ @@ -82,20 +69,16 @@ func (it *BlockIterator) Next() (object *Block, err error) { return it.o.Data[0], nil } -/* -NextObjectFunc is the func used in iterator. - -Notes -- ErrDone should be return while there are no items any more. -- Input objects slice should be set every time. -*/ +// NextObjectFunc is the func used in iterator. +// +// Notes +// - ErrDone should be return while there are no items any more. +// - Input Object slice should be set every time. type NextObjectFunc func(ctx context.Context, page *ObjectPage) error - type ObjectPage struct { Status Continuable Data []*Object } - type ObjectIterator struct { ctx context.Context next NextObjectFunc @@ -107,22 +90,13 @@ type ObjectIterator struct { } func NewObjectIterator(ctx context.Context, next NextObjectFunc, status Continuable) *ObjectIterator { - return &ObjectIterator{ - ctx: ctx, - next: next, - index: 0, - done: false, - o: ObjectPage{ - Status: status, - }, - } + return &ObjectIterator{ctx: ctx, next: next, o: ObjectPage{Status: status}} } - func (it *ObjectIterator) ContinuationToken() string { return it.o.Status.ContinuationToken() } - func (it *ObjectIterator) Next() (object *Object, err error) { + // Consume Data via index. // Consume Data via index. if it.index < len(it.o.Data) { it.index++ @@ -153,20 +127,16 @@ func (it *ObjectIterator) Next() (object *Object, err error) { return it.o.Data[0], nil } -/* -NextObjectFunc is the func used in iterator. - -Notes -- ErrDone should be return while there are no items any more. -- Input objects slice should be set every time. -*/ +// NextPartFunc is the func used in iterator. +// +// Notes +// - ErrDone should be return while there are no items any more. +// - Input Part slice should be set every time. type NextPartFunc func(ctx context.Context, page *PartPage) error - type PartPage struct { Status Continuable Data []*Part } - type PartIterator struct { ctx context.Context next NextPartFunc @@ -178,22 +148,13 @@ type PartIterator struct { } func NewPartIterator(ctx context.Context, next NextPartFunc, status Continuable) *PartIterator { - return &PartIterator{ - ctx: ctx, - next: next, - index: 0, - done: false, - o: PartPage{ - Status: status, - }, - } + return &PartIterator{ctx: ctx, next: next, o: PartPage{Status: status}} } - func (it *PartIterator) ContinuationToken() string { return it.o.Status.ContinuationToken() } - func (it *PartIterator) Next() (object *Part, err error) { + // Consume Data via index. // Consume Data via index. if it.index < len(it.o.Data) { it.index++ @@ -224,20 +185,16 @@ func (it *PartIterator) Next() (object *Part, err error) { return it.o.Data[0], nil } -/* -NextObjectFunc is the func used in iterator. - -Notes -- ErrDone should be return while there are no items any more. -- Input objects slice should be set every time. -*/ +// NextStoragerFunc is the func used in iterator. +// +// Notes +// - ErrDone should be return while there are no items any more. +// - Input Storager slice should be set every time. type NextStoragerFunc func(ctx context.Context, page *StoragerPage) error - type StoragerPage struct { Status Continuable Data []Storager } - type StoragerIterator struct { ctx context.Context next NextStoragerFunc @@ -249,22 +206,13 @@ type StoragerIterator struct { } func NewStoragerIterator(ctx context.Context, next NextStoragerFunc, status Continuable) *StoragerIterator { - return &StoragerIterator{ - ctx: ctx, - next: next, - index: 0, - done: false, - o: StoragerPage{ - Status: status, - }, - } + return &StoragerIterator{ctx: ctx, next: next, o: StoragerPage{Status: status}} } - func (it *StoragerIterator) ContinuationToken() string { return it.o.Status.ContinuationToken() } - func (it *StoragerIterator) Next() (object Storager, err error) { + // Consume Data via index. // Consume Data via index. if it.index < len(it.o.Data) { it.index++ diff --git a/types/operation.generated.go b/types/operation.generated.go index b6729929b..d8e0cedb4 100644 --- a/types/operation.generated.go +++ b/types/operation.generated.go @@ -45,6 +45,7 @@ type UnimplementedAppender struct { } func (s UnimplementedAppender) mustEmbedUnimplementedAppender() { + } func (s UnimplementedAppender) String() string { return "UnimplementedAppender" @@ -116,6 +117,7 @@ type UnimplementedBlocker struct { } func (s UnimplementedBlocker) mustEmbedUnimplementedBlocker() { + } func (s UnimplementedBlocker) String() string { return "UnimplementedBlocker" @@ -196,6 +198,7 @@ type UnimplementedCopier struct { } func (s UnimplementedCopier) mustEmbedUnimplementedCopier() { + } func (s UnimplementedCopier) String() string { return "UnimplementedCopier" @@ -224,6 +227,7 @@ type UnimplementedDirer struct { } func (s UnimplementedDirer) mustEmbedUnimplementedDirer() { + } func (s UnimplementedDirer) String() string { return "UnimplementedDirer" @@ -264,6 +268,7 @@ type UnimplementedFetcher struct { } func (s UnimplementedFetcher) mustEmbedUnimplementedFetcher() { + } func (s UnimplementedFetcher) String() string { return "UnimplementedFetcher" @@ -318,6 +323,7 @@ type UnimplementedLinker struct { } func (s UnimplementedLinker) mustEmbedUnimplementedLinker() { + } func (s UnimplementedLinker) String() string { return "UnimplementedLinker" @@ -374,6 +380,7 @@ type UnimplementedMover struct { } func (s UnimplementedMover) mustEmbedUnimplementedMover() { + } func (s UnimplementedMover) String() string { return "UnimplementedMover" @@ -425,6 +432,7 @@ type UnimplementedMultiparter struct { } func (s UnimplementedMultiparter) mustEmbedUnimplementedMultiparter() { + } func (s UnimplementedMultiparter) String() string { return "UnimplementedMultiparter" @@ -490,6 +498,7 @@ type UnimplementedPager struct { } func (s UnimplementedPager) mustEmbedUnimplementedPager() { + } func (s UnimplementedPager) String() string { return "UnimplementedPager" @@ -532,6 +541,7 @@ type UnimplementedReacher struct { } func (s UnimplementedReacher) mustEmbedUnimplementedReacher() { + } func (s UnimplementedReacher) String() string { return "UnimplementedReacher" @@ -576,6 +586,7 @@ type UnimplementedServicer struct { } func (s UnimplementedServicer) mustEmbedUnimplementedServicer() { + } func (s UnimplementedServicer) String() string { return "UnimplementedServicer" @@ -635,6 +646,7 @@ type UnimplementedStorageHTTPSigner struct { } func (s UnimplementedStorageHTTPSigner) mustEmbedUnimplementedStorageHTTPSigner() { + } func (s UnimplementedStorageHTTPSigner) String() string { return "UnimplementedStorageHTTPSigner" @@ -765,6 +777,7 @@ type UnimplementedStorager struct { } func (s UnimplementedStorager) mustEmbedUnimplementedStorager() { + } func (s UnimplementedStorager) String() string { return "UnimplementedStorager"