diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 86fb55226..d4ca188d2 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -67,5 +67,7 @@ jobs: run: GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic - name: Report Coverage - run: bash <(curl -s https://codecov.io/bash) + uses: codecov/codecov-action@v2 + with: + flags: go-${{ matrix.go }} diff --git a/README.MD b/README.MD index dda9ad1a5..8e00bb4fa 100644 --- a/README.MD +++ b/README.MD @@ -75,7 +75,7 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec # Contributors This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)]. - + # Donators diff --git a/README_ZH.MD b/README_ZH.MD index 79b22cfde..172ad307f 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -91,7 +91,7 @@ golang版本 >= 1.11 # 贡献 感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)]. - + # 捐赠 diff --git a/container/gtype/gtype.go b/container/gtype/gtype.go index b43e7f7d6..d6711f2a1 100644 --- a/container/gtype/gtype.go +++ b/container/gtype/gtype.go @@ -4,14 +4,11 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gtype provides kinds of high performance and concurrent-safe basic variable types. +// Package gtype provides high performance and concurrent-safe basic variable types. package gtype -// Type is alias of Interface. -type Type = Interface - // New is alias of NewInterface. // See NewInterface. -func New(value ...interface{}) *Type { +func New(value ...interface{}) *Interface { return NewInterface(value...) } diff --git a/container/gtype/gtype_bool.go b/container/gtype/gtype_bool.go index 4b7e45909..e24b5cd21 100644 --- a/container/gtype/gtype_bool.go +++ b/container/gtype/gtype_bool.go @@ -51,7 +51,7 @@ func (v *Bool) Set(value bool) (old bool) { return } -// Val atomically loads and returns t.valueue. +// Val atomically loads and returns t.value. func (v *Bool) Val() bool { return atomic.LoadInt32(&v.value) > 0 } diff --git a/container/gtype/gtype_float32.go b/container/gtype/gtype_float32.go index 7da59ce87..364a979f0 100644 --- a/container/gtype/gtype_float32.go +++ b/container/gtype/gtype_float32.go @@ -11,7 +11,6 @@ import ( "math" "strconv" "sync/atomic" - "unsafe" ) // Float32 is a struct for concurrent-safe operation for type float32. @@ -51,7 +50,7 @@ func (v *Float32) Add(delta float32) (new float32) { old := math.Float32frombits(v.value) new = old + delta if atomic.CompareAndSwapUint32( - (*uint32)(unsafe.Pointer(&v.value)), + &v.value, math.Float32bits(old), math.Float32bits(new), ) { diff --git a/container/gtype/gtype_float64.go b/container/gtype/gtype_float64.go index 340bdef00..dcf68aa7d 100644 --- a/container/gtype/gtype_float64.go +++ b/container/gtype/gtype_float64.go @@ -11,7 +11,6 @@ import ( "math" "strconv" "sync/atomic" - "unsafe" ) // Float64 is a struct for concurrent-safe operation for type float64. @@ -51,7 +50,7 @@ func (v *Float64) Add(delta float64) (new float64) { old := math.Float64frombits(v.value) new = old + delta if atomic.CompareAndSwapUint64( - (*uint64)(unsafe.Pointer(&v.value)), + &v.value, math.Float64bits(old), math.Float64bits(new), ) { diff --git a/container/gtype/gtype_z_unit_bytes_test.go b/container/gtype/gtype_z_unit_bytes_test.go index 49145d290..468673eb1 100644 --- a/container/gtype/gtype_z_unit_bytes_test.go +++ b/container/gtype/gtype_z_unit_bytes_test.go @@ -22,7 +22,7 @@ func Test_Bytes(t *testing.T) { t.AssertEQ(iClone.Set([]byte("123")), []byte("abc")) t.AssertEQ(iClone.Val(), []byte("123")) - //空参测试 + // 空参测试 i1 := gtype.NewBytes() t.AssertEQ(i1.Val(), nil) }) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 35e392caa..d9a7813ec 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -265,6 +265,7 @@ type ( ) const ( + defaultModelSafe = false queryTypeNormal = 0 queryTypeCount = 1 unionTypeNormal = 0 diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 717ab6e01..1398c3454 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -132,7 +132,7 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { tableStr = c.QuotePrefixTableName(tableNames[0]) } } - return &Model{ + m := &Model{ db: c.db, tablesInit: tableStr, tables: tableStr, @@ -142,6 +142,10 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { filter: true, extraArgs: extraArgs, } + if defaultModelSafe { + m.safe = true + } + return m } // Raw creates and returns a model based on a raw sql not a table. @@ -154,7 +158,7 @@ func (c *Core) Raw(rawSql string, args ...interface{}) *Model { return model } -// Raw creates and returns a model based on a raw sql not a table. +// Raw sets current model as a raw sql model. // Example: // db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result) // See Core.Raw. @@ -169,7 +173,7 @@ func (tx *TX) Raw(rawSql string, args ...interface{}) *Model { return tx.Model().Raw(rawSql, args...) } -// With creates and returns an ORM model based on meta data of given object. +// With creates and returns an ORM model based on metadata of given object. func (c *Core) With(objects ...interface{}) *Model { return c.db.Model().With(objects...) } diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 9a6d4ac50..bcf73bb9c 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -236,16 +236,6 @@ func (m *Model) WhereOrNotNull(columns ...string) *Model { return model } -// Group sets the "GROUP BY" statement for the model. -func (m *Model) Group(groupBy ...string) *Model { - if len(groupBy) > 0 { - model := m.getModel() - model.groupBy = m.db.GetCore().QuoteString(gstr.Join(groupBy, ",")) - return model - } - return m -} - // And adds "AND" condition to the where statement. // Deprecated, use Where instead. func (m *Model) And(where interface{}, args ...interface{}) *Model { @@ -267,11 +257,17 @@ func (m *Model) Or(where interface{}, args ...interface{}) *Model { return m.WhereOr(where, args...) } -// GroupBy is alias of Model.Group. -// See Model.Group. -// Deprecated, use Group instead. -func (m *Model) GroupBy(groupBy string) *Model { - return m.Group(groupBy) +// Group sets the "GROUP BY" statement for the model. +func (m *Model) Group(groupBy ...string) *Model { + if len(groupBy) == 0 { + return m + } + model := m.getModel() + if model.groupBy != "" { + model.groupBy += "," + } + model.groupBy = model.db.GetCore().QuoteString(strings.Join(groupBy, ",")) + return model } // Order sets the "ORDER BY" statement for the model. @@ -283,7 +279,7 @@ func (m *Model) Order(orderBy ...string) *Model { if model.orderBy != "" { model.orderBy += "," } - model.orderBy = m.db.GetCore().QuoteString(strings.Join(orderBy, " ")) + model.orderBy = model.db.GetCore().QuoteString(strings.Join(orderBy, " ")) return model } @@ -292,12 +288,7 @@ func (m *Model) OrderAsc(column string) *Model { if len(column) == 0 { return m } - model := m.getModel() - if model.orderBy != "" { - model.orderBy += "," - } - model.orderBy = m.db.GetCore().QuoteWord(column) + " ASC" - return model + return m.Order(column + " ASC") } // OrderDesc sets the "ORDER BY xxx DESC" statement for the model. @@ -305,12 +296,7 @@ func (m *Model) OrderDesc(column string) *Model { if len(column) == 0 { return m } - model := m.getModel() - if model.orderBy != "" { - model.orderBy += "," - } - model.orderBy = m.db.GetCore().QuoteWord(column) + " DESC" - return model + return m.Order(column + " DESC") } // OrderRandom sets the "ORDER BY RANDOM()" statement for the model. @@ -320,13 +306,6 @@ func (m *Model) OrderRandom() *Model { return model } -// OrderBy is alias of Model.Order. -// See Model.Order. -// Deprecated, use Order instead. -func (m *Model) OrderBy(orderBy string) *Model { - return m.Order(orderBy) -} - // Limit sets the "LIMIT" statement for the model. // The parameter `limit` can be either one or two number, if passed two number is passed, // it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]" diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 014df31a5..d780d1f30 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -7,6 +7,7 @@ package gdb import ( + "database/sql" "fmt" "github.com/gogf/gf/errors/gcode" "reflect" @@ -156,7 +157,8 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { model = model.Order(parsedTagOutput.Order) } err = model.Fields(fieldKeys).Where(relatedSourceName, relatedTargetValue).Scan(bindToReflectValue) - if err != nil { + // It ignores sql.ErrNoRows in with feature. + if err != nil && err != sql.ErrNoRows { return err } } @@ -265,7 +267,8 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { err = model.Fields(fieldKeys). Where(relatedSourceName, relatedTargetValue). ScanList(pointer, fieldName, parsedTagOutput.With) - if err != nil { + // It ignores sql.ErrNoRows in with feature. + if err != nil && err != sql.ErrNoRows { return err } } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index aef619da9..c9e7414e2 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -1992,6 +1992,7 @@ PRIMARY KEY (id) }) } +// https://github.com/gogf/gf/issues/1401 func Test_With_Feature_Issue1401(t *testing.T) { var ( table1 = "parcels" @@ -2032,3 +2033,65 @@ func Test_With_Feature_Issue1401(t *testing.T) { t.Assert(parcelDetail.Items[0].ParcelId, 3) }) } + +// https://github.com/gogf/gf/issues/1412 +func Test_With_Feature_Issue1412(t *testing.T) { + var ( + table1 = "parcels" + table2 = "items" + ) + array := gstr.SplitAndTrim(gtest.TestDataContent(`issue1412.sql`), ";") + for _, v := range array { + if _, err := db.Exec(v); err != nil { + gtest.Error(err) + } + } + defer dropTable(table1) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + type Items struct { + gmeta.Meta `orm:"table:items"` + Id int `json:"id"` + Name string `json:"name"` + } + + type ParcelRsp struct { + gmeta.Meta `orm:"table:parcels"` + Id int `json:"id"` + ItemId int `json:"item_id"` + Items Items `json:"items" orm:"with:Id=ItemId"` + } + + entity := &ParcelRsp{} + err := db.Model("parcels").With(Items{}).Where("id=3").Scan(&entity) + t.AssertNil(err) + t.Assert(entity.Id, 3) + t.Assert(entity.ItemId, 0) + t.Assert(entity.Items.Id, 0) + t.Assert(entity.Items.Name, "") + }) + + gtest.C(t, func(t *gtest.T) { + type Items struct { + gmeta.Meta `orm:"table:items"` + Id int `json:"id"` + Name string `json:"name"` + } + + type ParcelRsp struct { + gmeta.Meta `orm:"table:parcels"` + Id int `json:"id"` + ItemId int `json:"item_id"` + Items Items `json:"items" orm:"with:Id=ItemId"` + } + + entity := &ParcelRsp{} + err := db.Model("parcels").With(Items{}).Where("id=30000").Scan(&entity) + t.AssertNE(err, nil) + t.Assert(entity.Id, 0) + t.Assert(entity.ItemId, 0) + t.Assert(entity.Items.Id, 0) + t.Assert(entity.Items.Name, "") + }) +} diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index b571a1da3..c3e7f7aa5 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -1313,14 +1313,14 @@ func Test_Model_InnerJoin(t *testing.T) { t.Assert(n, 5) - result, err := db.Model(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").OrderBy("u1.id").Select() + result, err := db.Model(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").Order("u1.id").Select() if err != nil { t.Fatal(err) } t.Assert(len(result), 5) - result, err = db.Model(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ?", 1).OrderBy("u1.id").Select() + result, err = db.Model(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ?", 1).Order("u1.id").Select() if err != nil { t.Fatal(err) } diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index f9f95fcef..b113d771e 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -1225,7 +1225,7 @@ func Test_Model_GroupBy(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Model(table).GroupBy("id").Select() + result, err := db.Model(table).Group("id").All() t.AssertNil(err) t.Assert(len(result), TableSize) t.Assert(result[0]["nickname"].String(), "name_1") diff --git a/database/gdb/testdata/issue1412.sql b/database/gdb/testdata/issue1412.sql new file mode 100644 index 000000000..453fca783 --- /dev/null +++ b/database/gdb/testdata/issue1412.sql @@ -0,0 +1,30 @@ +-- ---------------------------- +-- Table structure for items +-- ---------------------------- +CREATE TABLE `items` ( + `id` int(11) NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of items +-- ---------------------------- +INSERT INTO `items` VALUES (1, '金秋产品1'); +INSERT INTO `items` VALUES (2, '金秋产品2'); + +-- ---------------------------- +-- Table structure for parcels +-- ---------------------------- +CREATE TABLE `parcels` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `item_id` int(11) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of parcels +-- ---------------------------- +INSERT INTO `parcels` VALUES (1, 1); +INSERT INTO `parcels` VALUES (2, 2); +INSERT INTO `parcels` VALUES (3, 0); \ No newline at end of file diff --git a/encoding/gjson/gjson_z_example_conversion_test.go b/encoding/gjson/gjson_z_example_conversion_test.go index d2f4bdd9f..f7b01f48c 100644 --- a/encoding/gjson/gjson_z_example_conversion_test.go +++ b/encoding/gjson/gjson_z_example_conversion_test.go @@ -48,10 +48,10 @@ func Example_conversionNormalFormats() { // ====================== // YAML: // users: - // array: - // - John - // - Ming - // count: 1 + // array: + // - John + // - Ming + // count: 1 // // ====================== // TOML: diff --git a/encoding/gurl/url_test.go b/encoding/gurl/url_test.go index c992caea3..1ba6ab154 100644 --- a/encoding/gurl/url_test.go +++ b/encoding/gurl/url_test.go @@ -13,9 +13,11 @@ import ( "github.com/gogf/gf/test/gtest" ) -var urlStr string = `https://golang.org/x/crypto?go-get=1 +` -var urlEncode string = `https%3A%2F%2Fgolang.org%2Fx%2Fcrypto%3Fgo-get%3D1+%2B` -var rawUrlEncode string = `https%3A%2F%2Fgolang.org%2Fx%2Fcrypto%3Fgo-get%3D1%20%2B` +var ( + urlStr = `https://golang.org/x/crypto?go-get=1 +` + urlEncode = `https%3A%2F%2Fgolang.org%2Fx%2Fcrypto%3Fgo-get%3D1+%2B` + rawUrlEncode = `https%3A%2F%2Fgolang.org%2Fx%2Fcrypto%3Fgo-get%3D1%20%2B` +) func TestEncodeAndDecode(t *testing.T) { gtest.C(t, func(t *gtest.T) { diff --git a/encoding/gxml/gxml.go b/encoding/gxml/gxml.go index cf831bd28..57f393336 100644 --- a/encoding/gxml/gxml.go +++ b/encoding/gxml/gxml.go @@ -10,7 +10,7 @@ package gxml import ( "strings" - "github.com/clbanning/mxj" + "github.com/clbanning/mxj/v2" "github.com/gogf/gf/encoding/gcharset" "github.com/gogf/gf/text/gregex" ) @@ -48,7 +48,7 @@ func Encode(m map[string]interface{}, rootTag ...string) ([]byte, error) { return mxj.Map(m).Xml(rootTag...) } -// Encode encodes map to a XML format content as bytes with indent. +// EncodeWithIndent encodes map to an XML format content as bytes with indent. // The optional parameter is used to specify the XML root tag. func EncodeWithIndent(m map[string]interface{}, rootTag ...string) ([]byte, error) { return mxj.Map(m).XmlIndent("", "\t", rootTag...) @@ -68,7 +68,7 @@ func ToJson(content []byte) ([]byte, error) { } } -// convert converts the encoding of given XML content from XML root tag into UTF-8 encoding content. +// convert does convert the encoding of given XML content from XML root tag into UTF-8 encoding content. func convert(xml []byte) (res []byte, err error) { patten := `<\?xml.*encoding\s*=\s*['|"](.*?)['|"].*\?>` matchStr, err := gregex.MatchString(patten, string(xml)) diff --git a/encoding/gxml/gxml_test.go b/encoding/gxml/gxml_test.go index 8d9989a6d..cf901cd12 100644 --- a/encoding/gxml/gxml_test.go +++ b/encoding/gxml/gxml_test.go @@ -55,7 +55,7 @@ func buildXml(charset string, str string) (string, string) { return srcXml, dstXml } -//测试XML中字符集的转换 +// 测试XML中字符集的转换 func Test_XmlToJson(t *testing.T) { for _, v := range testData { srcXml, dstXml := buildXml(v.otherEncoding, v.utf8) diff --git a/encoding/gyaml/gyaml.go b/encoding/gyaml/gyaml.go index 95c531b57..255ccfed7 100644 --- a/encoding/gyaml/gyaml.go +++ b/encoding/gyaml/gyaml.go @@ -9,7 +9,7 @@ package gyaml import ( "github.com/gogf/gf/internal/json" - "gopkg.in/yaml.v3" + "gopkg.in/yaml.v2" "github.com/gogf/gf/util/gconv" ) diff --git a/frame/gins/gins.go b/frame/gins/gins.go index 719cf9bba..ca9843967 100644 --- a/frame/gins/gins.go +++ b/frame/gins/gins.go @@ -12,31 +12,31 @@ import ( ) var ( - // instances is the instance map for common used components. - instances = gmap.NewStrAnyMap(true) + // localInstances is the instance map for common used components. + localInstances = gmap.NewStrAnyMap(true) ) // Get returns the instance by given name. func Get(name string) interface{} { - return instances.Get(name) + return localInstances.Get(name) } // Set sets a instance object to the instance manager with given name. func Set(name string, instance interface{}) { - instances.Set(name, instance) + localInstances.Set(name, instance) } // GetOrSet returns the instance by name, // or set instance to the instance manager if it does not exist and returns this instance. func GetOrSet(name string, instance interface{}) interface{} { - return instances.GetOrSet(name, instance) + return localInstances.GetOrSet(name, instance) } // GetOrSetFunc returns the instance by name, // or sets instance with returned value of callback function `f` if it does not exist // and then returns this instance. func GetOrSetFunc(name string, f func() interface{}) interface{} { - return instances.GetOrSetFunc(name, f) + return localInstances.GetOrSetFunc(name, f) } // GetOrSetFuncLock returns the instance by name, @@ -46,11 +46,11 @@ func GetOrSetFunc(name string, f func() interface{}) interface{} { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f` // with mutex.Lock of the hash map. func GetOrSetFuncLock(name string, f func() interface{}) interface{} { - return instances.GetOrSetFuncLock(name, f) + return localInstances.GetOrSetFuncLock(name, f) } // SetIfNotExist sets to the map if the `name` does not exist, then returns true. // It returns false if exists, and `instance` would be ignored. func SetIfNotExist(name string, instance interface{}) bool { - return instances.SetIfNotExist(name, instance) + return localInstances.SetIfNotExist(name, instance) } diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index 8d722a10d..a6143dd76 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -12,6 +12,7 @@ import ( "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/os/gcfg" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gutil" @@ -25,59 +26,79 @@ const ( configNodeNameDatabase = "database" ) -// Database returns an instance of database ORM object -// with specified configuration group name. +// Database returns an instance of database ORM object with specified configuration group name. +// Note that it panics if any error occurs duration instance creating. func Database(name ...string) gdb.DB { - group := gdb.DefaultGroupName + var ( + ctx = context.Background() + group = gdb.DefaultGroupName + ) + if len(name) > 0 && name[0] != "" { group = name[0] } instanceKey := fmt.Sprintf("%s.%s", frameCoreComponentNameDatabase, group) - db := instances.GetOrSetFuncLock(instanceKey, func() interface{} { + db := localInstances.GetOrSetFuncLock(instanceKey, func() interface{} { + // It ignores returned error to avoid file no found error while it's not necessary. var ( configMap map[string]interface{} - configNodeKey string + configNodeKey = configNodeNameDatabase ) // It firstly searches the configuration of the instance name. - if Config().Available() { - configNodeKey, _ = gutil.MapPossibleItemByKey( - Config().GetMap("."), - configNodeNameDatabase, - ) - if configNodeKey == "" { - configNodeKey = configNodeNameDatabase + if configData, _ := Config().Data(ctx); len(configData) > 0 { + if v, _ := gutil.MapPossibleItemByKey(configData, configNodeNameDatabase); v != "" { + configNodeKey = v } - configMap = Config().GetMap(configNodeKey) + } + if v, _ := Config().Get(ctx, configNodeKey); !v.IsEmpty() { + configMap = v.Map() } if len(configMap) == 0 && !gdb.IsConfigured() { - configFilePath, _ := Config().GetFilePath() - if configFilePath == "" { - exampleFileName := "config.example.toml" - if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" { - panic(gerror.NewCodef( - gcode.CodeMissingConfiguration, - `configuration file "%s" not found, but found "%s", did you miss renaming the example configuration file?`, - Config().GetFileName(), - exampleFileName, - )) - } else { - panic(gerror.NewCodef( - gcode.CodeMissingConfiguration, - `configuration file "%s" not found, did you miss the configuration file or the misspell the configuration file name?`, - Config().GetFileName(), - )) + // File configuration object checks. + var ( + err error + configFilePath string + ) + if fileConfig, ok := Config().GetAdapter().(*gcfg.AdapterFile); ok { + if configFilePath, err = fileConfig.GetFilePath(); configFilePath == "" { + exampleFileName := "config.example.toml" + if exampleConfigFilePath, _ := fileConfig.GetFilePath(exampleFileName); exampleConfigFilePath != "" { + err = gerror.WrapCodef( + gcode.CodeMissingConfiguration, + err, + `configuration file "%s" not found, but found "%s", did you miss renaming the example configuration file?`, + fileConfig.GetFileName(), + exampleFileName, + ) + } else { + err = gerror.WrapCodef( + gcode.CodeMissingConfiguration, + err, + `configuration file "%s" not found, did you miss the configuration file or the misspell the configuration file name?`, + fileConfig.GetFileName(), + ) + } + if err != nil { + panic(err) + } } } - panic(gerror.NewCodef( - gcode.CodeMissingConfiguration, - `database initialization failed: "%s" node not found, is configuration file or configuration node missing?`, - configNodeNameDatabase, - )) + // Panic if nothing found in Config object or in gdb configuration. + if len(configMap) == 0 && !gdb.IsConfigured() { + err = gerror.WrapCodef( + gcode.CodeMissingConfiguration, + err, + `database initialization failed: "%s" node not found, is configuration file or configuration node missing?`, + configNodeNameDatabase, + ) + panic(err) + } } + if len(configMap) == 0 { configMap = make(map[string]interface{}) } - // Parse `m` as map-slice and adds it to gdb's global configurations. + // Parse `m` as map-slice and adds it to global configurations for package gdb. for g, groupConfig := range configMap { cg := gdb.ConfigGroup{} switch value := groupConfig.(type) { @@ -94,11 +115,11 @@ func Database(name ...string) gdb.DB { } if len(cg) > 0 { if gdb.GetConfig(group) == nil { - intlog.Printf(context.TODO(), "add configuration for group: %s, %#v", g, cg) + intlog.Printf(ctx, "add configuration for group: %s, %#v", g, cg) gdb.SetConfigGroup(g, cg) } else { - intlog.Printf(context.TODO(), "ignore configuration as it already exists for group: %s, %#v", g, cg) - intlog.Printf(context.TODO(), "%s, %#v", g, cg) + intlog.Printf(ctx, "ignore configuration as it already exists for group: %s, %#v", g, cg) + intlog.Printf(ctx, "%s, %#v", g, cg) } } } @@ -112,27 +133,33 @@ func Database(name ...string) gdb.DB { if len(cg) > 0 { if gdb.GetConfig(group) == nil { - intlog.Printf(context.TODO(), "add configuration for group: %s, %#v", gdb.DefaultGroupName, cg) + intlog.Printf(ctx, "add configuration for group: %s, %#v", gdb.DefaultGroupName, cg) gdb.SetConfigGroup(gdb.DefaultGroupName, cg) } else { - intlog.Printf(context.TODO(), "ignore configuration as it already exists for group: %s, %#v", gdb.DefaultGroupName, cg) - intlog.Printf(context.TODO(), "%s, %#v", gdb.DefaultGroupName, cg) + intlog.Printf(ctx, "ignore configuration as it already exists for group: %s, %#v", gdb.DefaultGroupName, cg) + intlog.Printf(ctx, "%s, %#v", gdb.DefaultGroupName, cg) } } } + // Create a new ORM object with given configurations. if db, err := gdb.New(name...); err == nil { - if Config().Available() { - // Initialize logger for ORM. - var loggerConfigMap map[string]interface{} - loggerConfigMap = Config().GetMap(fmt.Sprintf("%s.%s", configNodeKey, configNodeNameLogger)) - if len(loggerConfigMap) == 0 { - loggerConfigMap = Config().GetMap(configNodeKey) + // Initialize logger for ORM. + var ( + loggerConfigMap map[string]interface{} + loggerNodeName = fmt.Sprintf("%s.%s", configNodeKey, configNodeNameLogger) + ) + if v, _ := Config().Get(ctx, loggerNodeName); !v.IsEmpty() { + loggerConfigMap = v.Map() + } + if len(loggerConfigMap) == 0 { + if v, _ := Config().Get(ctx, configNodeKey); !v.IsEmpty() { + loggerConfigMap = v.Map() } - if len(loggerConfigMap) > 0 { - if err := db.GetLogger().SetConfigWithMap(loggerConfigMap); err != nil { - panic(err) - } + } + if len(loggerConfigMap) > 0 { + if err = db.GetLogger().SetConfigWithMap(loggerConfigMap); err != nil { + panic(err) } } return db @@ -158,7 +185,7 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode { if err != nil { panic(err) } - // To be compatible with old version. + // Be compatible with old version. if _, v := gutil.MapPossibleItemByKey(nodeMap, "LinkInfo"); v != nil { node.Link = gconv.String(v) } diff --git a/frame/gins/gins_log.go b/frame/gins/gins_log.go index 30018f031..dfd87b6b5 100644 --- a/frame/gins/gins_log.go +++ b/frame/gins/gins_log.go @@ -7,6 +7,7 @@ package gins import ( + "context" "fmt" "github.com/gogf/gf/os/glog" "github.com/gogf/gf/util/gutil" @@ -19,29 +20,44 @@ const ( // Log returns an instance of glog.Logger. // The parameter `name` is the name for the instance. +// Note that it panics if any error occurs duration instance creating. func Log(name ...string) *glog.Logger { - instanceName := glog.DefaultName + var ( + ctx = context.Background() + instanceName = glog.DefaultName + ) if len(name) > 0 && name[0] != "" { instanceName = name[0] } instanceKey := fmt.Sprintf("%s.%s", frameCoreComponentNameLogger, instanceName) - return instances.GetOrSetFuncLock(instanceKey, func() interface{} { + return localInstances.GetOrSetFuncLock(instanceKey, func() interface{} { logger := glog.Instance(instanceName) // To avoid file no found error while it's not necessary. - if Config().Available() { - var m map[string]interface{} - nodeKey, _ := gutil.MapPossibleItemByKey(Config().GetMap("."), configNodeNameLogger) - if nodeKey == "" { - nodeKey = configNodeNameLogger + var ( + configMap map[string]interface{} + loggerNodeName = configNodeNameLogger + ) + // Try to find possible `loggerNodeName` in case-insensitive way. + if configData, _ := Config().Data(ctx); len(configData) > 0 { + if v, _ := gutil.MapPossibleItemByKey(configData, configNodeNameLogger); v != "" { + loggerNodeName = v } - m = Config().GetMap(fmt.Sprintf(`%s.%s`, nodeKey, instanceName)) - if len(m) == 0 { - m = Config().GetMap(nodeKey) + } + // Retrieve certain logger configuration by logger name. + certainLoggerNodeName := fmt.Sprintf(`%s.%s`, loggerNodeName, instanceName) + if v, _ := Config().Get(ctx, certainLoggerNodeName); !v.IsEmpty() { + configMap = v.Map() + } + // Retrieve global logger configuration if configuration for certain logger name does not exist. + if len(configMap) == 0 { + if v, _ := Config().Get(ctx, loggerNodeName); !v.IsEmpty() { + configMap = v.Map() } - if len(m) > 0 { - if err := logger.SetConfigWithMap(m); err != nil { - panic(err) - } + } + // Set logger config if config map is not empty. + if len(configMap) > 0 { + if err := logger.SetConfigWithMap(configMap); err != nil { + panic(err) } } return logger diff --git a/frame/gins/gins_redis.go b/frame/gins/gins_redis.go index ef0ad47f4..bf3ab73b7 100644 --- a/frame/gins/gins_redis.go +++ b/frame/gins/gins_redis.go @@ -7,8 +7,11 @@ package gins import ( + "context" "fmt" "github.com/gogf/gf/database/gredis" + "github.com/gogf/gf/errors/gcode" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" ) @@ -19,42 +22,46 @@ const ( ) // Redis returns an instance of redis client with specified configuration group name. +// Note that it panics if any error occurs duration instance creating. func Redis(name ...string) *gredis.Redis { - config := Config() - group := gredis.DefaultGroupName + var ( + ctx = context.Background() + group = gredis.DefaultGroupName + ) if len(name) > 0 && name[0] != "" { group = name[0] } instanceKey := fmt.Sprintf("%s.%s", frameCoreComponentNameRedis, group) - result := instances.GetOrSetFuncLock(instanceKey, func() interface{} { + result := localInstances.GetOrSetFuncLock(instanceKey, func() interface{} { // If already configured, it returns the redis instance. if _, ok := gredis.GetConfig(group); ok { return gredis.Instance(group) } // Or else, it parses the default configuration file and returns a new redis instance. - var m map[string]interface{} - if _, v := gutil.MapPossibleItemByKey(Config().GetMap("."), configNodeNameRedis); v != nil { - m = gconv.Map(v) + var ( + configMap map[string]interface{} + ) + + if configData, err := Config().Data(ctx); err != nil { + panic(gerror.WrapCode(gcode.CodeOperationFailed, err, `retrieving redis configuration failed`)) + } else { + if _, v := gutil.MapPossibleItemByKey(configData, configNodeNameRedis); v != nil { + configMap = gconv.Map(v) + } } - if len(m) > 0 { - if v, ok := m[group]; ok { + + if len(configMap) > 0 { + if v, ok := configMap[group]; ok { redisConfig, err := gredis.ConfigFromStr(gconv.String(v)) if err != nil { panic(err) } return gredis.New(redisConfig) } else { - panic(fmt.Sprintf(`configuration for redis not found for group "%s"`, group)) + panic(fmt.Sprintf(`missing configuration for redis group "%s"`, group)) } } else { - filepath, err := config.GetFilePath() - if err != nil { - panic(err) - } - panic(fmt.Sprintf( - `incomplete configuration for redis: "redis" node not found in config file "%s"`, - filepath, - )) + panic(`missing configuration for redis: "redis" node not found`) } return nil }) diff --git a/frame/gins/gins_server.go b/frame/gins/gins_server.go index e73eadaa8..3dcdd65c4 100644 --- a/frame/gins/gins_server.go +++ b/frame/gins/gins_server.go @@ -7,8 +7,11 @@ package gins import ( + "context" "fmt" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" ) @@ -18,43 +21,61 @@ const ( ) // Server returns an instance of http server with specified name. +// Note that it panics if any error occurs duration instance creating. func Server(name ...interface{}) *ghttp.Server { - instanceKey := fmt.Sprintf("%s.%v", frameCoreComponentNameServer, name) - return instances.GetOrSetFuncLock(instanceKey, func() interface{} { - s := ghttp.GetServer(name...) - // To avoid file no found error while it's not necessary. - if Config().Available() { - var ( - serverConfigMap map[string]interface{} - serverLoggerConfigMap map[string]interface{} - ) - nodeKey, _ := gutil.MapPossibleItemByKey(Config().GetMap("."), configNodeNameServer) - if nodeKey == "" { - nodeKey = configNodeNameServer + var ( + ctx = context.Background() + instanceName = ghttp.DefaultServerName + instanceKey = fmt.Sprintf("%s.%v", frameCoreComponentNameServer, name) + ) + if len(name) > 0 && name[0] != "" { + instanceName = gconv.String(name[0]) + } + return localInstances.GetOrSetFuncLock(instanceKey, func() interface{} { + s := ghttp.GetServer(instanceName) + // It ignores returned error to avoid file no found error while it's not necessary. + var ( + serverConfigMap map[string]interface{} + serverLoggerConfigMap map[string]interface{} + configNodeName = configNodeNameServer + ) + if configData, _ := Config().Data(ctx); len(configData) > 0 { + if v, _ := gutil.MapPossibleItemByKey(configData, configNodeNameServer); v != "" { + configNodeName = v } - // Server configuration. - serverConfigMap = Config().GetMap(fmt.Sprintf(`%s.%s`, nodeKey, s.GetName())) - if len(serverConfigMap) == 0 { - serverConfigMap = Config().GetMap(nodeKey) - } - if len(serverConfigMap) > 0 { - if err := s.SetConfigWithMap(serverConfigMap); err != nil { - panic(err) - } - } - // Server logger configuration. - serverLoggerConfigMap = Config().GetMap( - fmt.Sprintf(`%s.%s.%s`, nodeKey, s.GetName(), configNodeNameLogger), - ) - if len(serverLoggerConfigMap) > 0 { - if err := s.Logger().SetConfigWithMap(serverLoggerConfigMap); err != nil { - panic(err) - } - } - // As it might use template feature, - // it initialize the view instance as well. - _ = getViewInstance() } + // Server configuration. + certainConfigNodeName := fmt.Sprintf(`%s.%s`, configNodeName, s.GetName()) + if v, _ := Config().Get(ctx, certainConfigNodeName); !v.IsEmpty() { + serverConfigMap = v.Map() + } + if len(serverConfigMap) == 0 { + if v, _ := Config().Get(ctx, configNodeName); !v.IsEmpty() { + serverConfigMap = v.Map() + } + } + if len(serverConfigMap) > 0 { + if err := s.SetConfigWithMap(serverConfigMap); err != nil { + panic(err) + } + } else { + // The configuration is not necessary, so it just prints internal logs. + intlog.Printf(ctx, `missing configuration for HTTP server "%s"`, instanceName) + } + + // Server logger configuration checks. + serverLoggerNodeName := fmt.Sprintf(`%s.%s.%s`, configNodeName, s.GetName(), configNodeNameLogger) + if v, _ := Config().Get(ctx, serverLoggerNodeName); !v.IsEmpty() { + serverLoggerConfigMap = v.Map() + } + if len(serverLoggerConfigMap) > 0 { + if err := s.Logger().SetConfigWithMap(serverLoggerConfigMap); err != nil { + panic(err) + } + } + // As it might use template feature, + // it initializes the view instance as well. + _ = getViewInstance() return s }).(*ghttp.Server) } diff --git a/frame/gins/gins_view.go b/frame/gins/gins_view.go index c67cef1ac..83914a12a 100644 --- a/frame/gins/gins_view.go +++ b/frame/gins/gins_view.go @@ -7,6 +7,7 @@ package gins import ( + "context" "fmt" "github.com/gogf/gf/os/gview" "github.com/gogf/gf/util/gutil" @@ -19,38 +20,48 @@ const ( // View returns an instance of View with default settings. // The parameter `name` is the name for the instance. +// Note that it panics if any error occurs duration instance creating. func View(name ...string) *gview.View { instanceName := gview.DefaultName if len(name) > 0 && name[0] != "" { instanceName = name[0] } instanceKey := fmt.Sprintf("%s.%s", frameCoreComponentNameViewer, instanceName) - return instances.GetOrSetFuncLock(instanceKey, func() interface{} { + return localInstances.GetOrSetFuncLock(instanceKey, func() interface{} { return getViewInstance(instanceName) }).(*gview.View) } func getViewInstance(name ...string) *gview.View { - instanceName := gview.DefaultName + var ( + ctx = context.Background() + instanceName = gview.DefaultName + ) if len(name) > 0 && name[0] != "" { instanceName = name[0] } view := gview.Instance(instanceName) // To avoid file no found error while it's not necessary. - if Config().Available() { - var m map[string]interface{} - nodeKey, _ := gutil.MapPossibleItemByKey(Config().GetMap("."), configNodeNameViewer) - if nodeKey == "" { - nodeKey = configNodeNameViewer + var ( + configMap map[string]interface{} + configNodeName = configNodeNameViewer + ) + if configData, _ := Config().Data(ctx); len(configData) > 0 { + if v, _ := gutil.MapPossibleItemByKey(configData, configNodeNameViewer); v != "" { + configNodeName = v } - m = Config().GetMap(fmt.Sprintf(`%s.%s`, nodeKey, instanceName)) - if len(m) == 0 { - m = Config().GetMap(nodeKey) + } + if v, _ := Config().Get(ctx, fmt.Sprintf(`%s.%s`, configNodeName, instanceName)); !v.IsEmpty() { + configMap = v.Map() + } + if len(configMap) == 0 { + if v, _ := Config().Get(ctx, configNodeName); !v.IsEmpty() { + configMap = v.Map() } - if len(m) > 0 { - if err := view.SetConfigWithMap(m); err != nil { - panic(err) - } + } + if len(configMap) > 0 { + if err := view.SetConfigWithMap(configMap); err != nil { + panic(err) } } return view diff --git a/frame/gins/gins_z_unit_config_test.go b/frame/gins/gins_z_unit_config_test.go index 0057b347b..2893939d4 100644 --- a/frame/gins/gins_z_unit_config_test.go +++ b/frame/gins/gins_z_unit_config_test.go @@ -7,6 +7,7 @@ package gins_test import ( + "context" "fmt" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/frame/gins" @@ -21,6 +22,7 @@ import ( ) var ( + ctx = context.Background() configContent = gfile.GetContents( gdebug.TestDataPath("config", "config.toml"), ) @@ -48,14 +50,14 @@ func Test_Config2(t *testing.T) { err = gfile.PutContents(gfile.Join(dirPath, name), configContent) t.Assert(err, nil) - err = gins.Config().AddPath(dirPath) + err = gins.Config().GetAdapter().(*gcfg.AdapterFile).AddPath(dirPath) t.Assert(err, nil) - defer gins.Config().Clear() + defer gins.Config().GetAdapter().(*gcfg.AdapterFile).Clear() - t.Assert(gins.Config().Get("test"), "v=1") - t.Assert(gins.Config().Get("database.default.1.host"), "127.0.0.1") - t.Assert(gins.Config().Get("redis.disk"), "127.0.0.1:6379,0") + t.Assert(gins.Config().MustGet(ctx, "test"), "v=1") + t.Assert(gins.Config().MustGet(ctx, "database.default.1.host"), "127.0.0.1") + t.Assert(gins.Config().MustGet(ctx, "redis.disk"), "127.0.0.1:6379,0") }) // for gfsnotify callbacks to refresh cache of config file time.Sleep(500 * time.Millisecond) @@ -72,14 +74,14 @@ func Test_Config2(t *testing.T) { err = gfile.PutContents(gfile.Join(dirPath, name), configContent) t.Assert(err, nil) - err = gins.Config().AddPath(dirPath) + err = gins.Config().GetAdapter().(*gcfg.AdapterFile).AddPath(dirPath) t.Assert(err, nil) - defer gins.Config().Clear() + defer gins.Config().GetAdapter().(*gcfg.AdapterFile).Clear() - t.Assert(gins.Config().Get("test"), "v=1") - t.Assert(gins.Config().Get("database.default.1.host"), "127.0.0.1") - t.Assert(gins.Config().Get("redis.disk"), "127.0.0.1:6379,0") + t.Assert(gins.Config().MustGet(ctx, "test"), "v=1") + t.Assert(gins.Config().MustGet(ctx, "database.default.1.host"), "127.0.0.1") + t.Assert(gins.Config().MustGet(ctx, "redis.disk"), "127.0.0.1:6379,0") // for gfsnotify callbacks to refresh cache of config file time.Sleep(500 * time.Millisecond) @@ -98,15 +100,15 @@ func Test_Config3(t *testing.T) { err = gfile.PutContents(gfile.Join(dirPath, name), configContent) t.Assert(err, nil) - err = gins.Config("test").AddPath(dirPath) + err = gins.Config("test").GetAdapter().(*gcfg.AdapterFile).AddPath(dirPath) t.Assert(err, nil) - defer gins.Config("test").Clear() - gins.Config("test").SetFileName("test.toml") + defer gins.Config("test").GetAdapter().(*gcfg.AdapterFile).Clear() + gins.Config("test").GetAdapter().(*gcfg.AdapterFile).SetFileName("test.toml") - t.Assert(gins.Config("test").Get("test"), "v=1") - t.Assert(gins.Config("test").Get("database.default.1.host"), "127.0.0.1") - t.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0") + t.Assert(gins.Config("test").MustGet(ctx, "test"), "v=1") + t.Assert(gins.Config("test").MustGet(ctx, "database.default.1.host"), "127.0.0.1") + t.Assert(gins.Config("test").MustGet(ctx, "redis.disk"), "127.0.0.1:6379,0") }) // for gfsnotify callbacks to refresh cache of config file time.Sleep(500 * time.Millisecond) @@ -122,15 +124,15 @@ func Test_Config3(t *testing.T) { err = gfile.PutContents(gfile.Join(dirPath, name), configContent) t.Assert(err, nil) - err = gins.Config("test").AddPath(dirPath) + err = gins.Config("test").GetAdapter().(*gcfg.AdapterFile).AddPath(dirPath) t.Assert(err, nil) - defer gins.Config("test").Clear() - gins.Config("test").SetFileName("test.toml") + defer gins.Config("test").GetAdapter().(*gcfg.AdapterFile).Clear() + gins.Config("test").GetAdapter().(*gcfg.AdapterFile).SetFileName("test.toml") - t.Assert(gins.Config("test").Get("test"), "v=1") - t.Assert(gins.Config("test").Get("database.default.1.host"), "127.0.0.1") - t.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0") + t.Assert(gins.Config("test").MustGet(ctx, "test"), "v=1") + t.Assert(gins.Config("test").MustGet(ctx, "database.default.1.host"), "127.0.0.1") + t.Assert(gins.Config("test").MustGet(ctx, "redis.disk"), "127.0.0.1:6379,0") }) // for gfsnotify callbacks to refresh cache of config file for next unit testing case. time.Sleep(500 * time.Millisecond) @@ -144,12 +146,12 @@ func Test_Config4(t *testing.T) { err := gfile.PutContents(file, configContent) t.Assert(err, nil) defer gfile.Remove(file) - defer gins.Config().Clear() + defer gins.Config().GetAdapter().(*gcfg.AdapterFile).Clear() - t.Assert(gins.Config().AddPath(path), nil) - t.Assert(gins.Config().Get("test"), "v=1") - t.Assert(gins.Config().Get("database.default.1.host"), "127.0.0.1") - t.Assert(gins.Config().Get("redis.disk"), "127.0.0.1:6379,0") + t.Assert(gins.Config().GetAdapter().(*gcfg.AdapterFile).AddPath(path), nil) + t.Assert(gins.Config().MustGet(ctx, "test"), "v=1") + t.Assert(gins.Config().MustGet(ctx, "database.default.1.host"), "127.0.0.1") + t.Assert(gins.Config().MustGet(ctx, "redis.disk"), "127.0.0.1:6379,0") }) time.Sleep(500 * time.Millisecond) @@ -159,11 +161,11 @@ func Test_Config4(t *testing.T) { err := gfile.PutContents(file, configContent) t.Assert(err, nil) defer gfile.Remove(file) - defer gins.Config().Clear() - t.Assert(gins.Config().AddPath(path), nil) - t.Assert(gins.Config().Get("test"), "v=1") - t.Assert(gins.Config().Get("database.default.1.host"), "127.0.0.1") - t.Assert(gins.Config().Get("redis.disk"), "127.0.0.1:6379,0") + defer gins.Config().GetAdapter().(*gcfg.AdapterFile).Clear() + t.Assert(gins.Config().GetAdapter().(*gcfg.AdapterFile).AddPath(path), nil) + t.Assert(gins.Config().MustGet(ctx, "test"), "v=1") + t.Assert(gins.Config().MustGet(ctx, "database.default.1.host"), "127.0.0.1") + t.Assert(gins.Config().MustGet(ctx, "redis.disk"), "127.0.0.1:6379,0") }) time.Sleep(500 * time.Millisecond) @@ -173,12 +175,12 @@ func Test_Config4(t *testing.T) { err := gfile.PutContents(file, configContent) t.Assert(err, nil) defer gfile.Remove(file) - defer gins.Config("test").Clear() - gins.Config("test").SetFileName("test.toml") - t.Assert(gins.Config("test").AddPath(path), nil) - t.Assert(gins.Config("test").Get("test"), "v=1") - t.Assert(gins.Config("test").Get("database.default.1.host"), "127.0.0.1") - t.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0") + defer gins.Config("test").GetAdapter().(*gcfg.AdapterFile).Clear() + gins.Config("test").GetAdapter().(*gcfg.AdapterFile).SetFileName("test.toml") + t.Assert(gins.Config("test").GetAdapter().(*gcfg.AdapterFile).AddPath(path), nil) + t.Assert(gins.Config("test").MustGet(ctx, "test"), "v=1") + t.Assert(gins.Config("test").MustGet(ctx, "database.default.1.host"), "127.0.0.1") + t.Assert(gins.Config("test").MustGet(ctx, "redis.disk"), "127.0.0.1:6379,0") }) time.Sleep(500 * time.Millisecond) @@ -188,12 +190,12 @@ func Test_Config4(t *testing.T) { err := gfile.PutContents(file, configContent) t.Assert(err, nil) defer gfile.Remove(file) - defer gins.Config().Clear() - gins.Config("test").SetFileName("test.toml") - t.Assert(gins.Config("test").AddPath(path), nil) - t.Assert(gins.Config("test").Get("test"), "v=1") - t.Assert(gins.Config("test").Get("database.default.1.host"), "127.0.0.1") - t.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0") + defer gins.Config().GetAdapter().(*gcfg.AdapterFile).Clear() + gins.Config("test").GetAdapter().(*gcfg.AdapterFile).SetFileName("test.toml") + t.Assert(gins.Config("test").GetAdapter().(*gcfg.AdapterFile).AddPath(path), nil) + t.Assert(gins.Config("test").MustGet(ctx, "test"), "v=1") + t.Assert(gins.Config("test").MustGet(ctx, "database.default.1.host"), "127.0.0.1") + t.Assert(gins.Config("test").MustGet(ctx, "redis.disk"), "127.0.0.1:6379,0") }) } func Test_Basic2(t *testing.T) { @@ -206,6 +208,6 @@ func Test_Basic2(t *testing.T) { _ = gfile.Remove(path) }() - t.Assert(gins.Config().Get("log-path"), "logs") + t.Assert(gins.Config().MustGet(ctx, "log-path"), "logs") }) } diff --git a/frame/gins/gins_z_unit_database_test.go b/frame/gins/gins_z_unit_database_test.go index 2693a27e7..9ffb016ff 100644 --- a/frame/gins/gins_z_unit_database_test.go +++ b/frame/gins/gins_z_unit_database_test.go @@ -9,6 +9,7 @@ package gins_test import ( "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/frame/gins" + "github.com/gogf/gf/os/gcfg" "github.com/gogf/gf/os/gtime" "testing" "time" @@ -32,10 +33,10 @@ func Test_Database(t *testing.T) { err = gfile.PutContents(gfile.Join(dirPath, name), databaseContent) t.Assert(err, nil) - err = gins.Config().AddPath(dirPath) + err = gins.Config().GetAdapter().(*gcfg.AdapterFile).AddPath(dirPath) t.Assert(err, nil) - defer gins.Config().Clear() + defer gins.Config().GetAdapter().(*gcfg.AdapterFile).Clear() // for gfsnotify callbacks to refresh cache of config file time.Sleep(500 * time.Millisecond) diff --git a/frame/gins/gins_z_unit_redis_test.go b/frame/gins/gins_z_unit_redis_test.go index 6ac44f85f..06a30129a 100644 --- a/frame/gins/gins_z_unit_redis_test.go +++ b/frame/gins/gins_z_unit_redis_test.go @@ -9,6 +9,7 @@ package gins_test import ( "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/frame/gins" + "github.com/gogf/gf/os/gcfg" "github.com/gogf/gf/os/gtime" "testing" "time" @@ -33,10 +34,10 @@ func Test_Redis(t *testing.T) { err = gfile.PutContents(gfile.Join(dirPath, name), redisContent) t.Assert(err, nil) - err = gins.Config().AddPath(dirPath) + err = gins.Config().GetAdapter().(*gcfg.AdapterFile).AddPath(dirPath) t.Assert(err, nil) - defer gins.Config().Clear() + defer gins.Config().GetAdapter().(*gcfg.AdapterFile).Clear() // for gfsnotify callbacks to refresh cache of config file time.Sleep(500 * time.Millisecond) diff --git a/frame/gins/gins_z_unit_view_test.go b/frame/gins/gins_z_unit_view_test.go index 084c09705..b8c3bbb83 100644 --- a/frame/gins/gins_z_unit_view_test.go +++ b/frame/gins/gins_z_unit_view_test.go @@ -54,9 +54,9 @@ func Test_View_Config(t *testing.T) { // view1 test1 gtest.C(t, func(t *gtest.T) { dirPath := gdebug.TestDataPath("view1") - gcfg.SetContent(gfile.GetContents(gfile.Join(dirPath, "config.toml"))) - defer gcfg.ClearContent() - defer instances.Clear() + Config().GetAdapter().(*gcfg.AdapterFile).SetContent(gfile.GetContents(gfile.Join(dirPath, "config.toml"))) + defer Config().GetAdapter().(*gcfg.AdapterFile).ClearContent() + defer localInstances.Clear() view := View("test1") t.AssertNE(view, nil) @@ -76,9 +76,9 @@ func Test_View_Config(t *testing.T) { // view1 test2 gtest.C(t, func(t *gtest.T) { dirPath := gdebug.TestDataPath("view1") - gcfg.SetContent(gfile.GetContents(gfile.Join(dirPath, "config.toml"))) - defer gcfg.ClearContent() - defer instances.Clear() + Config().GetAdapter().(*gcfg.AdapterFile).SetContent(gfile.GetContents(gfile.Join(dirPath, "config.toml"))) + defer Config().GetAdapter().(*gcfg.AdapterFile).ClearContent() + defer localInstances.Clear() view := View("test2") t.AssertNE(view, nil) @@ -98,9 +98,9 @@ func Test_View_Config(t *testing.T) { // view2 gtest.C(t, func(t *gtest.T) { dirPath := gdebug.TestDataPath("view2") - gcfg.SetContent(gfile.GetContents(gfile.Join(dirPath, "config.toml"))) - defer gcfg.ClearContent() - defer instances.Clear() + Config().GetAdapter().(*gcfg.AdapterFile).SetContent(gfile.GetContents(gfile.Join(dirPath, "config.toml"))) + defer Config().GetAdapter().(*gcfg.AdapterFile).ClearContent() + defer localInstances.Clear() view := View() t.AssertNE(view, nil) @@ -120,9 +120,9 @@ func Test_View_Config(t *testing.T) { // view2 gtest.C(t, func(t *gtest.T) { dirPath := gdebug.TestDataPath("view2") - gcfg.SetContent(gfile.GetContents(gfile.Join(dirPath, "config.toml"))) - defer gcfg.ClearContent() - defer instances.Clear() + Config().GetAdapter().(*gcfg.AdapterFile).SetContent(gfile.GetContents(gfile.Join(dirPath, "config.toml"))) + defer Config().GetAdapter().(*gcfg.AdapterFile).ClearContent() + defer localInstances.Clear() view := View("test100") t.AssertNE(view, nil) diff --git a/go.mod b/go.mod index fc660eac6..1343ded26 100644 --- a/go.mod +++ b/go.mod @@ -4,18 +4,17 @@ go 1.14 require ( github.com/BurntSushi/toml v0.4.1 - github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 + github.com/clbanning/mxj/v2 v2.5.5 github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.5.1 github.com/go-sql-driver/mysql v1.6.0 github.com/gomodule/redigo v1.8.5 github.com/gorilla/websocket v1.4.2 - github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf + github.com/grokify/html-strip-tags-go v0.0.1 github.com/olekukonko/tablewriter v0.0.5 - go.opentelemetry.io/otel v1.0.0-RC3 - go.opentelemetry.io/otel/oteltest v1.0.0-RC3 - go.opentelemetry.io/otel/trace v1.0.0-RC3 + go.opentelemetry.io/otel v1.0.0 + go.opentelemetry.io/otel/trace v1.0.0 golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 golang.org/x/text v0.3.6 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 269310b56..e3486a097 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= -github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= @@ -16,8 +16,8 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= -github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= +github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -32,12 +32,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v1.0.0-RC3 h1:kvwiyEkiUT/JaadXzVLI/R1wDO934A7r3Bs2wEe6wqA= -go.opentelemetry.io/otel v1.0.0-RC3/go.mod h1:Ka5j3ua8tZs4Rkq4Ex3hwgBgOchyPVq5S6P2lz//nKQ= -go.opentelemetry.io/otel/oteltest v1.0.0-RC3 h1:MjaeegZTaX0Bv9uB9CrdVjOFM/8slRjReoWoV9xDCpY= -go.opentelemetry.io/otel/oteltest v1.0.0-RC3/go.mod h1:xpzajI9JBRr7gX63nO6kAmImmYIAtuQblZ36Z+LfCjE= -go.opentelemetry.io/otel/trace v1.0.0-RC3 h1:9F0ayEvlxv8BmNmPbU005WK7hC+7KbOazCPZjNa1yME= -go.opentelemetry.io/otel/trace v1.0.0-RC3/go.mod h1:VUt2TUYd8S2/ZRX09ZDFZQwn2RqfMB5MzO17jBojGxo= +go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI= +go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg= +go.opentelemetry.io/otel/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4= +go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -55,6 +53,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 7e7febfe3..b9354f3f9 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -107,9 +107,9 @@ const ( HookAfterOutput = "HOOK_AFTER_OUTPUT" ServerStatusStopped = 0 ServerStatusRunning = 1 + DefaultServerName = "default" + DefaultDomainName = "default" supportedHttpMethods = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE" - defaultServerName = "default" - defaultDomainName = "default" defaultMethod = "ALL" handlerTypeHandler = 1 handlerTypeObject = 2 diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 052b4ee7e..aa28222ad 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -8,19 +8,21 @@ package ghttp import ( "fmt" + "io/ioutil" + "net/http" + "github.com/gogf/gf" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/ghttp/internal/client" "github.com/gogf/gf/net/ghttp/internal/httputil" "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/util/gconv" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" - "io/ioutil" - "net/http" ) const ( @@ -56,8 +58,8 @@ func MiddlewareServerTracing(r *Request) { r.Body = utils.NewReadCloser(reqBodyContentBytes, false) span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( - attribute.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)), - attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)), + attribute.String(tracingEventHttpRequestHeaders, gconv.String(httputil.HeaderToMap(r.Header))), + attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx).String()), attribute.String(tracingEventHttpRequestBody, gstr.StrLimit( string(reqBodyContentBytes), gtrace.MaxContentLogSize(), @@ -82,7 +84,7 @@ func MiddlewareServerTracing(r *Request) { ) span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( - attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())), + attribute.String(tracingEventHttpResponseHeaders, gconv.String(httputil.HeaderToMap(r.Response.Header()))), attribute.String(tracingEventHttpResponseBody, resBodyContent), )) return diff --git a/net/ghttp/ghttp_request_param_post.go b/net/ghttp/ghttp_request_param_post.go deleted file mode 100644 index 8ddfa17ab..000000000 --- a/net/ghttp/ghttp_request_param_post.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package ghttp - -import ( - "github.com/gogf/gf/container/gvar" - "github.com/gogf/gf/util/gconv" -) - -// GetPost retrieves and returns parameter from form and body. -// It returns if does not exist in neither form nor body. -// It returns nil if is not passed. -// -// Note that if there're multiple parameters with the same name, the parameters are retrieved -// and overwrote in order of priority: form > body. -// -// Deprecated, use GetForm instead. -func (r *Request) GetPost(key string, def ...interface{}) interface{} { - r.parseForm() - if len(r.formMap) > 0 { - if v, ok := r.formMap[key]; ok { - return v - } - } - r.parseBody() - if len(r.bodyMap) > 0 { - if v, ok := r.bodyMap[key]; ok { - return v - } - } - if len(def) > 0 { - return def[0] - } - return nil -} - -// Deprecated, use GetFormVar instead. -func (r *Request) GetPostVar(key string, def ...interface{}) *gvar.Var { - return gvar.New(r.GetPost(key, def...)) -} - -// Deprecated, use GetFormString instead. -func (r *Request) GetPostString(key string, def ...interface{}) string { - return r.GetPostVar(key, def...).String() -} - -// Deprecated, use GetFormBool instead. -func (r *Request) GetPostBool(key string, def ...interface{}) bool { - return r.GetPostVar(key, def...).Bool() -} - -// Deprecated, use GetFormInt instead. -func (r *Request) GetPostInt(key string, def ...interface{}) int { - return r.GetPostVar(key, def...).Int() -} - -// Deprecated, use GetFormInt32 instead. -func (r *Request) GetPostInt32(key string, def ...interface{}) int32 { - return r.GetPostVar(key, def...).Int32() -} - -// Deprecated, use GetFormInt64 instead. -func (r *Request) GetPostInt64(key string, def ...interface{}) int64 { - return r.GetPostVar(key, def...).Int64() -} - -// Deprecated, use GetFormInts instead. -func (r *Request) GetPostInts(key string, def ...interface{}) []int { - return r.GetPostVar(key, def...).Ints() -} - -// Deprecated, use GetFormUint instead. -func (r *Request) GetPostUint(key string, def ...interface{}) uint { - return r.GetPostVar(key, def...).Uint() -} - -// Deprecated, use GetFormUint32 instead. -func (r *Request) GetPostUint32(key string, def ...interface{}) uint32 { - return r.GetPostVar(key, def...).Uint32() -} - -// Deprecated, use GetFormUint64 instead. -func (r *Request) GetPostUint64(key string, def ...interface{}) uint64 { - return r.GetPostVar(key, def...).Uint64() -} - -// Deprecated, use GetFormFloat32 instead. -func (r *Request) GetPostFloat32(key string, def ...interface{}) float32 { - return r.GetPostVar(key, def...).Float32() -} - -// Deprecated, use GetFormFloat64 instead. -func (r *Request) GetPostFloat64(key string, def ...interface{}) float64 { - return r.GetPostVar(key, def...).Float64() -} - -// Deprecated, use GetFormFloats instead. -func (r *Request) GetPostFloats(key string, def ...interface{}) []float64 { - return r.GetPostVar(key, def...).Floats() -} - -// Deprecated, use GetFormArray instead. -func (r *Request) GetPostArray(key string, def ...interface{}) []string { - return r.GetPostVar(key, def...).Strings() -} - -// Deprecated, use GetFormStrings instead. -func (r *Request) GetPostStrings(key string, def ...interface{}) []string { - return r.GetPostVar(key, def...).Strings() -} - -// Deprecated, use GetFormInterfaces instead. -func (r *Request) GetPostInterfaces(key string, def ...interface{}) []interface{} { - return r.GetPostVar(key, def...).Interfaces() -} - -// GetPostMap retrieves and returns all parameters in the form and body passed from client -// as map. The parameter specifies the keys retrieving from client parameters, -// the associated values are the default values if the client does not pass. -// -// Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote -// in order of priority: form > body. -// -// Deprecated. -func (r *Request) GetPostMap(kvMap ...map[string]interface{}) map[string]interface{} { - r.parseForm() - r.parseBody() - var ok, filter bool - if len(kvMap) > 0 && kvMap[0] != nil { - filter = true - } - m := make(map[string]interface{}, len(r.formMap)+len(r.bodyMap)) - for k, v := range r.bodyMap { - if filter { - if _, ok = kvMap[0][k]; !ok { - continue - } - } - m[k] = v - } - for k, v := range r.formMap { - if filter { - if _, ok = kvMap[0][k]; !ok { - continue - } - } - m[k] = v - } - // Check none exist parameters and assign it with default value. - if filter { - for k, v := range kvMap[0] { - if _, ok = m[k]; !ok { - m[k] = v - } - } - } - return m -} - -// GetPostMapStrStr retrieves and returns all parameters in the form and body passed from client -// as map[string]string. The parameter specifies the keys -// retrieving from client parameters, the associated values are the default values if the client -// does not pass. -// -// Deprecated. -func (r *Request) GetPostMapStrStr(kvMap ...map[string]interface{}) map[string]string { - postMap := r.GetPostMap(kvMap...) - if len(postMap) > 0 { - m := make(map[string]string, len(postMap)) - for k, v := range postMap { - m[k] = gconv.String(v) - } - return m - } - return nil -} - -// GetPostMapStrVar retrieves and returns all parameters in the form and body passed from client -// as map[string]*gvar.Var. The parameter specifies the keys -// retrieving from client parameters, the associated values are the default values if the client -// does not pass. -// -// Deprecated. -func (r *Request) GetPostMapStrVar(kvMap ...map[string]interface{}) map[string]*gvar.Var { - postMap := r.GetPostMap(kvMap...) - if len(postMap) > 0 { - m := make(map[string]*gvar.Var, len(postMap)) - for k, v := range postMap { - m[k] = gvar.New(v) - } - return m - } - return nil -} - -// GetPostStruct retrieves all parameters in the form and body passed from client -// and converts them to given struct object. Note that the parameter is a pointer -// to the struct object. The optional parameter is used to specify the key to -// attribute mapping. -// -// Deprecated. -func (r *Request) GetPostStruct(pointer interface{}, mapping ...map[string]string) error { - return gconv.Struct(r.GetPostMap(), pointer, mapping...) -} - -// GetPostToStruct is alias of GetQueryStruct. See GetPostStruct. -// -// Deprecated. -func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]string) error { - return r.GetPostStruct(pointer, mapping...) -} diff --git a/net/ghttp/ghttp_request_param_query.go b/net/ghttp/ghttp_request_param_query.go index b108b4b3c..13c8624dc 100644 --- a/net/ghttp/ghttp_request_param_query.go +++ b/net/ghttp/ghttp_request_param_query.go @@ -25,7 +25,7 @@ func (r *Request) SetQuery(key string, value interface{}) { // and request body. It returns if does not exist in the query and is given, // or else it returns nil. // -// Note that if there're multiple parameters with the same name, the parameters are retrieved +// Note that if there are multiple parameters with the same name, the parameters are retrieved // and overwrote in order of priority: query > body. func (r *Request) GetQuery(key string, def ...interface{}) interface{} { r.parseQuery() @@ -116,7 +116,7 @@ func (r *Request) GetQueryInterfaces(key string, def ...interface{}) []interface // as map. The parameter specifies the keys retrieving from client parameters, // the associated values are the default values if the client does not pass. // -// Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote +// Note that if there are multiple parameters with the same name, the parameters are retrieved and overwrote // in order of priority: query > body. func (r *Request) GetQueryMap(kvMap ...map[string]interface{}) map[string]interface{} { r.parseQuery() diff --git a/net/ghttp/ghttp_request_param_request.go b/net/ghttp/ghttp_request_param_request.go index 2e62a1992..8b4cc13d8 100644 --- a/net/ghttp/ghttp_request_param_request.go +++ b/net/ghttp/ghttp_request_param_request.go @@ -20,7 +20,7 @@ import ( // // GetRequest is one of the most commonly used functions for retrieving parameters. // -// Note that if there're multiple parameters with the same name, the parameters are +// Note that if there are multiple parameters with the same name, the parameters are // retrieved and overwrote in order of priority: router < query < body < form < custom. func (r *Request) GetRequest(key string, def ...interface{}) interface{} { value := r.GetParam(key) @@ -167,7 +167,7 @@ func (r *Request) GetRequestInterfaces(key string, def ...interface{}) []interfa // // GetRequestMap is one of the most commonly used functions for retrieving parameters. // -// Note that if there're multiple parameters with the same name, the parameters are retrieved +// Note that if there are multiple parameters with the same name, the parameters are retrieved // and overwrote in order of priority: router < query < body < form < custom. func (r *Request) GetRequestMap(kvMap ...map[string]interface{}) map[string]interface{} { r.parseQuery() diff --git a/net/ghttp/ghttp_response_view.go b/net/ghttp/ghttp_response_view.go index 47da241dc..d8892b9a1 100644 --- a/net/ghttp/ghttp_response_view.go +++ b/net/ghttp/ghttp_response_view.go @@ -92,8 +92,8 @@ func (r *Response) buildInVars(params ...map[string]interface{}) map[string]inte }) // Note that it should assign no Config variable to template // if there's no configuration file. - if c := gcfg.Instance(); c.Available() { - m["Config"] = c.Map() + if v, _ := gcfg.Instance().Data(r.Request.Context()); len(v) > 0 { + m["Config"] = v } return m } diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 1b2eef058..7442eecd7 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -88,7 +88,7 @@ func serverProcessInit() { // Note that the parameter should be unique for different servers. It returns an existing // server instance if given is already existing in the server mapping. func GetServer(name ...interface{}) *Server { - serverName := defaultServerName + serverName := DefaultServerName if len(name) > 0 && name[0] != "" { serverName = gconv.String(name[0]) } diff --git a/net/ghttp/ghttp_server_pprof.go b/net/ghttp/ghttp_server_pprof.go index cef4f2d35..dcc0e93b3 100644 --- a/net/ghttp/ghttp_server_pprof.go +++ b/net/ghttp/ghttp_server_pprof.go @@ -32,7 +32,7 @@ func StartPProfServer(port int, pattern ...string) { // EnablePProf enables PProf feature for server. func (s *Server) EnablePProf(pattern ...string) { - s.Domain(defaultDomainName).EnablePProf(pattern...) + s.Domain(DefaultDomainName).EnablePProf(pattern...) } // EnablePProf enables PProf feature for server of specified domain. diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index 83d061b5e..ac0261f82 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -39,7 +39,7 @@ func (s *Server) routerMapKey(hook, method, path, domain string) string { // parsePattern parses the given pattern to domain, method and path variable. func (s *Server) parsePattern(pattern string) (domain, method, path string, err error) { path = strings.TrimSpace(pattern) - domain = defaultDomainName + domain = DefaultDomainName method = defaultMethod if array, err := gregex.MatchString(`([a-zA-Z]+):(.+)`, pattern); len(array) > 1 && err == nil { path = strings.TrimSpace(array[2]) diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index 17fd011c1..9ca95702d 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -106,7 +106,7 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han ) // Default domain has the most priority when iteration. - for _, domain := range []string{defaultDomainName, domain} { + for _, domain := range []string{DefaultDomainName, domain} { p, ok := s.serveTree[domain] if !ok { continue diff --git a/net/ghttp/ghttp_server_status.go b/net/ghttp/ghttp_server_status.go index 7cc627abd..a41776b40 100644 --- a/net/ghttp/ghttp_server_status.go +++ b/net/ghttp/ghttp_server_status.go @@ -12,7 +12,7 @@ import ( // getStatusHandler retrieves and returns the handler for given status code. func (s *Server) getStatusHandler(status int, r *Request) []HandlerFunc { - domains := []string{r.GetHost(), defaultDomainName} + domains := []string{r.GetHost(), DefaultDomainName} for _, domain := range domains { if f, ok := s.statusHandlerMap[s.statusHandlerKey(status, domain)]; ok { return f @@ -37,7 +37,7 @@ func (s *Server) statusHandlerKey(status int, domain string) string { // BindStatusHandler registers handler for given status code. func (s *Server) BindStatusHandler(status int, handler HandlerFunc) { - s.addStatusHandler(s.statusHandlerKey(status, defaultDomainName), handler) + s.addStatusHandler(s.statusHandlerKey(status, DefaultDomainName), handler) } // BindStatusHandlerByMap registers handler for given status code using map. diff --git a/net/ghttp/ghttp_unit_request_test.go b/net/ghttp/ghttp_unit_request_test.go index 96ba9a375..cc8de827c 100644 --- a/net/ghttp/ghttp_unit_request_test.go +++ b/net/ghttp/ghttp_unit_request_test.go @@ -95,39 +95,7 @@ func Test_Params_Basic(t *testing.T) { r.Response.Write(r.GetMapStrStr()["a"]) } }) - // POST - s.BindHandler("/post", func(r *ghttp.Request) { - if r.GetPost("array") != nil { - r.Response.Write(r.GetPost("array")) - } - if r.GetPost("slice") != nil { - r.Response.Write(r.GetPost("slice")) - } - if r.GetPost("bool") != nil { - r.Response.Write(r.GetPostBool("bool")) - } - if r.GetPost("float32") != nil { - r.Response.Write(r.GetPostFloat32("float32")) - } - if r.GetPost("float64") != nil { - r.Response.Write(r.GetPostFloat64("float64")) - } - if r.GetPost("int") != nil { - r.Response.Write(r.GetPostInt("int")) - } - if r.GetPost("uint") != nil { - r.Response.Write(r.GetPostUint("uint")) - } - if r.GetPost("string") != nil { - r.Response.Write(r.GetPostString("string")) - } - if r.GetPost("map") != nil { - r.Response.Write(r.GetPostMap()["map"].(map[string]interface{})["b"]) - } - if r.GetPost("a") != nil { - r.Response.Write(r.GetPostMapStrStr()["a"]) - } - }) + // DELETE s.BindHandler("/delete", func(r *ghttp.Request) { if r.Get("array") != nil { @@ -330,21 +298,6 @@ func Test_Params_Basic(t *testing.T) { t.Assert(client.PutContent("/put", "map[a]=1&map[b]=2"), `2`) t.Assert(client.PutContent("/put", "a=1&b=2"), `1`) - // POST - t.Assert(client.PostContent("/post", "array[]=1&array[]=2"), `["1","2"]`) - t.Assert(client.PostContent("/post", "slice=1&slice=2"), `2`) - t.Assert(client.PostContent("/post", "bool=1"), `true`) - t.Assert(client.PostContent("/post", "bool=0"), `false`) - t.Assert(client.PostContent("/post", "float32=0.11"), `0.11`) - t.Assert(client.PostContent("/post", "float64=0.22"), `0.22`) - t.Assert(client.PostContent("/post", "int=-10000"), `-10000`) - t.Assert(client.PostContent("/post", "int=10000"), `10000`) - t.Assert(client.PostContent("/post", "uint=10000"), `10000`) - t.Assert(client.PostContent("/post", "uint=9"), `9`) - t.Assert(client.PostContent("/post", "string=key"), `key`) - t.Assert(client.PostContent("/post", "map[a]=1&map[b]=2"), `2`) - t.Assert(client.PostContent("/post", "a=1&b=2"), `1`) - // DELETE t.Assert(client.DeleteContent("/delete", "array[]=1&array[]=2"), `["1","2"]`) t.Assert(client.DeleteContent("/delete", "slice=1&slice=2"), `2`) @@ -458,9 +411,6 @@ func Test_Params_Priority(t *testing.T) { s.BindHandler("/query", func(r *ghttp.Request) { r.Response.Write(r.GetQuery("a")) }) - s.BindHandler("/post", func(r *ghttp.Request) { - r.Response.Write(r.GetPost("a")) - }) s.BindHandler("/form", func(r *ghttp.Request) { r.Response.Write(r.GetForm("a")) }) @@ -485,7 +435,6 @@ func Test_Params_Priority(t *testing.T) { client.SetPrefix(prefix) t.Assert(client.GetContent("/query?a=1", "a=100"), "100") - t.Assert(client.PostContent("/post?a=1", "a=100"), "100") t.Assert(client.PostContent("/form?a=1", "a=100"), "100") t.Assert(client.PutContent("/form?a=1", "a=100"), "100") t.Assert(client.GetContent("/request?a=1", "a=100"), "100") diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index 567ea6c14..138a71966 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -8,19 +8,21 @@ package client import ( "fmt" + "io/ioutil" + "net/http" + "net/http/httptrace" + "github.com/gogf/gf" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/ghttp/internal/httputil" "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/util/gconv" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" - "io/ioutil" - "net/http" - "net/http/httptrace" ) const ( @@ -70,7 +72,7 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro response.Body = utils.NewReadCloser(reqBodyContentBytes, false) span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( - attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(response.Header)), + attribute.String(tracingEventHttpResponseHeaders, gconv.String(httputil.HeaderToMap(response.Header))), attribute.String(tracingEventHttpResponseBody, gstr.StrLimit( string(reqBodyContentBytes), gtrace.MaxContentLogSize(), diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go index ef4733f45..4cfcfd27b 100644 --- a/net/ghttp/internal/client/client_tracing_tracer.go +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -10,18 +10,20 @@ import ( "context" "crypto/tls" "fmt" - "github.com/gogf/gf/internal/utils" - "github.com/gogf/gf/net/gtrace" - "github.com/gogf/gf/text/gstr" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" "net/http/httptrace" "net/textproto" "strings" "sync" + + "github.com/gogf/gf/internal/utils" + "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/util/gconv" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" ) type clientTracer struct { @@ -147,8 +149,8 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { } ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( - attribute.Any(tracingEventHttpRequestHeaders, ct.headers), - attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)), + attribute.String(tracingEventHttpRequestHeaders, gconv.String(ct.headers)), + attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context).String()), attribute.String(tracingEventHttpRequestBody, gstr.StrLimit( string(ct.requestBody), gtrace.MaxContentLogSize(), diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index 6d847474b..1325ddd32 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -9,6 +9,9 @@ package gtrace import ( "context" + "os" + "strings" + "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" @@ -17,8 +20,6 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" - "os" - "strings" ) const ( diff --git a/net/gtrace/gtrace_baggage.go b/net/gtrace/gtrace_baggage.go index d04834645..1e9a89606 100644 --- a/net/gtrace/gtrace_baggage.go +++ b/net/gtrace/gtrace_baggage.go @@ -8,6 +8,7 @@ package gtrace import ( "context" + "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/util/gconv" diff --git a/net/gtrace/gtrace_carrier.go b/net/gtrace/gtrace_carrier.go index 997f2ea91..4cce90236 100644 --- a/net/gtrace/gtrace_carrier.go +++ b/net/gtrace/gtrace_carrier.go @@ -14,12 +14,12 @@ import ( // Carrier is the storage medium used by a TextMapPropagator. type Carrier map[string]interface{} +// NewCarrier creates and returns a Carrier. func NewCarrier(data ...map[string]interface{}) Carrier { if len(data) > 0 && data[0] != nil { return data[0] - } else { - return make(map[string]interface{}) } + return make(map[string]interface{}) } // Get returns the value associated with the passed key. @@ -41,6 +41,7 @@ func (c Carrier) Keys() []string { return keys } +// MustMarshal .returns the JSON encoding of c func (c Carrier) MustMarshal() []byte { b, err := json.Marshal(c) if err != nil { @@ -49,10 +50,12 @@ func (c Carrier) MustMarshal() []byte { return b } +// String converts and returns current Carrier as string. func (c Carrier) String() string { return string(c.MustMarshal()) } +// UnmarshalJSON implements interface UnmarshalJSON for package json. func (c Carrier) UnmarshalJSON(b []byte) error { carrier := NewCarrier(nil) return json.UnmarshalUseNumber(b, carrier) diff --git a/net/gtrace/gtrace_span.go b/net/gtrace/gtrace_span.go index 72f88c418..0d7fb240b 100644 --- a/net/gtrace/gtrace_span.go +++ b/net/gtrace/gtrace_span.go @@ -8,9 +8,11 @@ package gtrace import ( "context" + "go.opentelemetry.io/otel/trace" ) +// Span warps trace.Span for compatibility and extension. type Span struct { trace.Span } diff --git a/net/gtrace/gtrace_tracer.go b/net/gtrace/gtrace_tracer.go index 8394fcebf..47d2baa72 100644 --- a/net/gtrace/gtrace_tracer.go +++ b/net/gtrace/gtrace_tracer.go @@ -11,11 +11,12 @@ import ( "go.opentelemetry.io/otel/trace" ) +// Tracer warps trace.Tracer for compatibility and extension. type Tracer struct { trace.Tracer } -// Tracer is a short function for retrieving Tracer. +// NewTracer Tracer is a short function for retrieving Tracer. func NewTracer(name ...string) *Tracer { tracerName := "" if len(name) > 0 { diff --git a/net/gtrace/gtrace_unit_carrier_test.go b/net/gtrace/gtrace_unit_carrier_test.go index 095250c92..f5beceec9 100644 --- a/net/gtrace/gtrace_unit_carrier_test.go +++ b/net/gtrace/gtrace_unit_carrier_test.go @@ -8,12 +8,12 @@ package gtrace_test import ( "context" + "testing" + "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/test/gtest" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/oteltest" "go.opentelemetry.io/otel/trace" - "testing" ) const ( @@ -51,14 +51,15 @@ func TestNewCarrier(t *testing.T) { SpanID: spanID, TraceFlags: trace.FlagsSampled, })) - ctx, _ = oteltest.DefaultTracer().Start(ctx, "inject") + + ctx, _ = otel.Tracer("").Start(ctx, "inject") carrier1 := gtrace.NewCarrier() otel.GetTextMapPropagator().Inject(ctx, carrier1) - t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01"}`) + t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}`) ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1) gotSc := trace.SpanContextFromContext(ctx) t.Assert(gotSc.TraceID().String(), traceID.String()) - t.Assert(gotSc.SpanID().String(), "0000000000000002") + t.Assert(gotSc.SpanID().String(), "00f067aa0ba902b7") }) } diff --git a/os/gbuild/gbuild.go b/os/gbuild/gbuild.go index c44cd57fa..01aca70cf 100644 --- a/os/gbuild/gbuild.go +++ b/os/gbuild/gbuild.go @@ -14,16 +14,16 @@ import ( "github.com/gogf/gf/encoding/gbase64" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" - "github.com/gogf/gf/util/gconv" "runtime" ) var ( - builtInVarStr = "" // Raw variable base64 string. + builtInVarStr = "" // Raw variable base64 string, which is injected by go build flags. builtInVarMap = map[string]interface{}{} // Binary custom variable map decoded. ) func init() { + // The `builtInVarStr` is injected by go build flags. if builtInVarStr != "" { err := json.UnmarshalUseNumber(gbase64.MustDecodeString(builtInVarStr), &builtInVarMap) if err != nil { @@ -39,37 +39,27 @@ func init() { // Info returns the basic built information of the binary as map. // Note that it should be used with gf-cli tool "gf build", -// which injects necessary information into the binary. +// which automatically injects necessary information into the binary. func Info() map[string]string { return map[string]string{ - "gf": GetString("gfVersion"), - "go": GetString("goVersion"), - "git": GetString("builtGit"), - "time": GetString("builtTime"), + "gf": Get("gfVersion").String(), + "go": Get("goVersion").String(), + "git": Get("builtGit").String(), + "time": Get("builtTime").String(), } } // Get retrieves and returns the build-in binary variable with given name. -func Get(name string, def ...interface{}) interface{} { +func Get(name string, def ...interface{}) *gvar.Var { if v, ok := builtInVarMap[name]; ok { - return v + return gvar.New(v) } if len(def) > 0 { - return def[0] + return gvar.New(def[0]) } return nil } -// GetVar retrieves and returns the build-in binary variable of given name as gvar.Var. -func GetVar(name string, def ...interface{}) *gvar.Var { - return gvar.New(Get(name, def...)) -} - -// GetString retrieves and returns the build-in binary variable of given name as string. -func GetString(name string, def ...interface{}) string { - return gconv.String(Get(name, def...)) -} - // Map returns the custom build-in variable map. func Map() map[string]interface{} { return builtInVarMap diff --git a/os/gbuild/gbuild_test.go b/os/gbuild/gbuild_test.go new file mode 100644 index 000000000..e53bf3a21 --- /dev/null +++ b/os/gbuild/gbuild_test.go @@ -0,0 +1,39 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gbuild_test + +import ( + "github.com/gogf/gf/os/gbuild" + "github.com/gogf/gf/test/gtest" + "testing" +) + +func Test_Info(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gbuild.Info(), map[string]string{ + "gf": "", + "go": "", + "git": "", + "time": "", + }) + }) +} + +func Test_Get(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gbuild.Get(`none`), nil) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(gbuild.Get(`none`, 1), 1) + }) +} + +func Test_Map(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gbuild.Map(), map[string]interface{}{}) + }) +} diff --git a/os/gcache/gcache_adapter.go b/os/gcache/gcache_adapter.go index af2e23b17..e6553fed7 100644 --- a/os/gcache/gcache_adapter.go +++ b/os/gcache/gcache_adapter.go @@ -14,9 +14,7 @@ import ( // Adapter is the core adapter for cache features implements. // -// Note that the implements should guarantee the concurrent safety calling its functions. -// You can implement one or more functions if necessary, it is suggested returning gcode.CodeNotImplemented error -// for those unimplemented functions. +// Note that the implementer itself should guarantee the concurrent safety of these functions. type Adapter interface { // Set sets cache with `key`-`value` pair, which is expired after `duration`. // diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index 47ac9fae5..5d2e570a1 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -54,7 +54,7 @@ const ( ) // NewAdapterMemory creates and returns a new memory cache object. -func NewAdapterMemory(lruCap ...int) Adapter { +func NewAdapterMemory(lruCap ...int) *AdapterMemory { c := &AdapterMemory{ data: newAdapterMemoryData(), lruGetList: glist.New(true), diff --git a/os/gcache/gcache_cache.go b/os/gcache/gcache_cache.go index 66bc7a9f9..37f431224 100644 --- a/os/gcache/gcache_cache.go +++ b/os/gcache/gcache_cache.go @@ -30,7 +30,7 @@ func New(lruCap ...int) *Cache { } // Here may be a "timer leak" if adapter is manually changed from memory adapter. // Do not worry about this, as adapter is less changed, and it does nothing if it's not used. - gtimer.AddSingleton(time.Second, memAdapter.(*AdapterMemory).syncEventAndClearExpired) + gtimer.AddSingleton(time.Second, memAdapter.syncEventAndClearExpired) return c } diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index ec8bf5153..e522512a3 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -9,97 +9,141 @@ package gcfg import ( "context" - "github.com/gogf/gf/container/garray" + "fmt" "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/intlog" - "github.com/gogf/gf/os/gcmd" ) -// Config is the configuration manager. type Config struct { - defaultName string // Default configuration file name. - searchPaths *garray.StrArray // Searching path array. - jsonMap *gmap.StrAnyMap // The pared JSON objects for configuration files. - violenceCheck bool // Whether do violence check in value index searching. It affects the performance when set true(false in default). + adapter Adapter + dataMap *gmap.StrAnyMap } const ( - DefaultName = "config" // DefaultName is the default group name for instance usage. - DefaultConfigFile = "config.toml" // DefaultConfigFile is the default configuration file name. - commandEnvKeyForFile = "gf.gcfg.file" // commandEnvKeyForFile is the configuration key for command argument or environment configuring file name. - commandEnvKeyForPath = "gf.gcfg.path" // commandEnvKeyForPath is the configuration key for command argument or environment configuring directory path. - commandEnvKeyForErrorPrint = "gf.gcfg.errorprint" // commandEnvKeyForErrorPrint is used to specify the key controlling error printing to stdout. + DefaultName = "config" // DefaultName is the default group name for instance usage. ) -var ( - supportedFileTypes = []string{"toml", "yaml", "yml", "json", "ini", "xml"} // All supported file types suffixes. - resourceTryFiles = []string{"", "/", "config/", "config", "/config", "/config/"} // Prefix array for trying searching in resource manager. - instances = gmap.NewStrAnyMap(true) // Instances map containing configuration instances. - customConfigContentMap = gmap.NewStrStrMap(true) // Customized configuration content. -) - -// SetContent sets customized configuration content for specified `file`. -// The `file` is unnecessary param, default is DefaultConfigFile. -func SetContent(content string, file ...string) { - name := DefaultConfigFile - if len(file) > 0 { - name = file[0] +// New creates and returns a Config object with default adapter of AdapterFile. +func New() (*Config, error) { + adapterFile, err := NewAdapterFile() + if err != nil { + return nil, err } - // Clear file cache for instances which cached `name`. - instances.LockFunc(func(m map[string]interface{}) { - if customConfigContentMap.Contains(name) { - for _, v := range m { - v.(*Config).jsonMap.Remove(name) + return &Config{ + adapter: adapterFile, + dataMap: gmap.NewStrAnyMap(true), + }, nil +} + +// NewWithAdapter creates and returns a Config object with given adapter. +func NewWithAdapter(adapter Adapter) *Config { + return &Config{ + adapter: adapter, + dataMap: gmap.NewStrAnyMap(true), + } +} + +// Instance returns an instance of Config with default settings. +// The parameter `name` is the name for the instance. But very note that, if the file "name.toml" +// exists in the configuration directory, it then sets it as the default configuration file. The +// toml file type is the default configuration file type. +func Instance(name ...string) *Config { + key := DefaultName + if len(name) > 0 && name[0] != "" { + key = name[0] + } + return localInstances.GetOrSetFuncLock(key, func() interface{} { + adapter, err := NewAdapterFile() + if err != nil { + intlog.Error(context.Background(), err) + return nil + } + // If it's not using default configuration or its configuration file is not available, + // it searches the possible configuration file according to the name and all supported + // file types. + if key != DefaultName || !adapter.Available() { + for _, fileType := range supportedFileTypes { + if file := fmt.Sprintf(`%s.%s`, key, fileType); adapter.Available(file) { + adapter.SetFileName(file) + break + } } } - customConfigContentMap.Set(name, content) - }) + return NewWithAdapter(adapter) + }).(*Config) } -// GetContent returns customized configuration content for specified `file`. -// The `file` is unnecessary param, default is DefaultConfigFile. -func GetContent(file ...string) string { - name := DefaultConfigFile - if len(file) > 0 { - name = file[0] - } - return customConfigContentMap.Get(name) +// SetAdapter sets the adapter of current Config object. +func (c *Config) SetAdapter(adapter Adapter) { + c.adapter = adapter } -// RemoveContent removes the global configuration with specified `file`. -// If `name` is not passed, it removes configuration of the default group name. -func RemoveContent(file ...string) { - name := DefaultConfigFile - if len(file) > 0 { - name = file[0] - } - // Clear file cache for instances which cached `name`. - instances.LockFunc(func(m map[string]interface{}) { - if customConfigContentMap.Contains(name) { - for _, v := range m { - v.(*Config).jsonMap.Remove(name) - } - customConfigContentMap.Remove(name) +// GetAdapter returns the adapter of current Config object. +func (c *Config) GetAdapter() Adapter { + return c.adapter +} + +// Set sets value with specified `pattern`. +// It supports hierarchical data access by char separator, which is '.' in default. +// It is commonly used for updates certain configuration value in runtime. +func (c *Config) Set(ctx context.Context, pattern string, value interface{}) { + c.dataMap.Set(pattern, value) +} + +// Get retrieves and returns value by specified `pattern`. +// It returns all values of current Json object if `pattern` is given empty or string ".". +// It returns nil if no value found by `pattern`. +// +// It returns a default value specified by `def` if value for `pattern` is not found. +func (c *Config) Get(ctx context.Context, pattern string, def ...interface{}) (*gvar.Var, error) { + var ( + err error + value interface{} + ) + if value = c.dataMap.Get(pattern); value == nil { + value, err = c.adapter.Get(ctx, pattern) + if err != nil { + return nil, err } - }) - - intlog.Printf(context.TODO(), `RemoveContent: %s`, name) -} - -// ClearContent removes all global configuration contents. -func ClearContent() { - customConfigContentMap.Clear() - // Clear cache for all instances. - instances.LockFunc(func(m map[string]interface{}) { - for _, v := range m { - v.(*Config).jsonMap.Clear() + if value == nil && len(def) > 0 { + return gvar.New(def[0]), nil } + } + return gvar.New(value), nil +} + +// Data retrieves and returns all configuration data as map type. +func (c *Config) Data(ctx context.Context) (data map[string]interface{}, err error) { + adapterData, err := c.adapter.Data(ctx) + if err != nil { + return nil, err + } + data = make(map[string]interface{}) + for k, v := range adapterData { + data[k] = v + } + c.dataMap.Iterator(func(k string, v interface{}) bool { + data[k] = v + return true }) - - intlog.Print(context.TODO(), `RemoveConfig`) + return } -// errorPrint checks whether printing error to stdout. -func errorPrint() bool { - return gcmd.GetOptWithEnv(commandEnvKeyForErrorPrint, true).Bool() +// MustGet acts as function Get, but it panics if error occurs. +func (c *Config) MustGet(ctx context.Context, pattern string, def ...interface{}) *gvar.Var { + v, err := c.Get(ctx, pattern, def...) + if err != nil { + panic(err) + } + return gvar.New(v) +} + +// MustData acts as function Data, but it panics if error occurs. +func (c *Config) MustData(ctx context.Context) map[string]interface{} { + v, err := c.Data(ctx) + if err != nil { + panic(err) + } + return v } diff --git a/os/gcfg/gcfg_adaper.go b/os/gcfg/gcfg_adaper.go new file mode 100644 index 000000000..3d37d3edd --- /dev/null +++ b/os/gcfg/gcfg_adaper.go @@ -0,0 +1,20 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcfg + +import "context" + +// Adapter is the interface for configuration retrieving. +type Adapter interface { + // Get retrieves and returns value by specified `pattern`. + Get(ctx context.Context, pattern string) (value interface{}, err error) + + // Data retrieves and returns all configuration data as map type. + // Note that this function may lead lots of memory usage if configuration data is too large, + // you can implement this function if necessary. + Data(ctx context.Context) (data map[string]interface{}, err error) +} diff --git a/os/gcfg/gcfg_adapter_file.go b/os/gcfg/gcfg_adapter_file.go new file mode 100644 index 000000000..96ba02f76 --- /dev/null +++ b/os/gcfg/gcfg_adapter_file.go @@ -0,0 +1,275 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcfg + +import ( + "context" + "github.com/gogf/gf/container/garray" + "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/encoding/gjson" + "github.com/gogf/gf/errors/gcode" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gfsnotify" + "github.com/gogf/gf/os/gres" + "github.com/gogf/gf/util/gmode" +) + +type AdapterFile struct { + defaultName string // Default configuration file name. + searchPaths *garray.StrArray // Searching path array. + jsonMap *gmap.StrAnyMap // The pared JSON objects for configuration files. + violenceCheck bool // Whether it does violence check in value index searching. It affects the performance when set true(false in default). +} + +const ( + DefaultConfigFile = "config.toml" // DefaultConfigFile is the default configuration file name. + commandEnvKeyForFile = "gf.gcfg.file" // commandEnvKeyForFile is the configuration key for command argument or environment configuring file name. + commandEnvKeyForPath = "gf.gcfg.path" // commandEnvKeyForPath is the configuration key for command argument or environment configuring directory path. +) + +var ( + supportedFileTypes = []string{"toml", "yaml", "yml", "json", "ini", "xml"} // All supported file types suffixes. + resourceTryFiles = []string{"", "/", "config/", "config", "/config", "/config/"} // Prefix array for trying searching in resource manager. + localInstances = gmap.NewStrAnyMap(true) // Instances map containing configuration instances. + customConfigContentMap = gmap.NewStrStrMap(true) // Customized configuration content. +) + +// NewAdapterFile returns a new configuration management object. +// The parameter `file` specifies the default configuration file name for reading. +func NewAdapterFile(file ...string) (*AdapterFile, error) { + var ( + err error + name = DefaultConfigFile + ) + if len(file) > 0 { + name = file[0] + } else { + // Custom default configuration file name from command line or environment. + if customFile := gcmd.GetOptWithEnv(commandEnvKeyForFile).String(); customFile != "" { + name = customFile + } + } + c := &AdapterFile{ + defaultName: name, + searchPaths: garray.NewStrArray(true), + jsonMap: gmap.NewStrAnyMap(true), + } + // Customized dir path from env/cmd. + if customPath := gcmd.GetOptWithEnv(commandEnvKeyForPath).String(); customPath != "" { + if gfile.Exists(customPath) { + if err = c.SetPath(customPath); err != nil { + return nil, err + } + } else { + return nil, gerror.Newf(`configuration directory path "%s" does not exist`, customPath) + } + } else { + // ================================================================================ + // Automatic searching directories. + // It does not affect adapter object cresting if these directories do not exist. + // ================================================================================ + + // Dir path of working dir. + if err := c.AddPath(gfile.Pwd()); err != nil { + intlog.Error(context.TODO(), err) + } + + // Dir path of main package. + if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { + if err := c.AddPath(mainPath); err != nil { + intlog.Error(context.TODO(), err) + } + } + + // Dir path of binary. + if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { + if err := c.AddPath(selfPath); err != nil { + intlog.Error(context.TODO(), err) + } + } + } + return c, nil +} + +// SetViolenceCheck sets whether to perform hierarchical conflict checking. +// This feature needs to be enabled when there is a level symbol in the key name. +// It is off in default. +// +// Note that, turning on this feature is quite expensive, and it is not recommended +// allowing separators in the key names. It is best to avoid this on the application side. +func (c *AdapterFile) SetViolenceCheck(check bool) { + c.violenceCheck = check + c.Clear() +} + +// SetFileName sets the default configuration file name. +func (c *AdapterFile) SetFileName(name string) { + c.defaultName = name +} + +// GetFileName returns the default configuration file name. +func (c *AdapterFile) GetFileName() string { + return c.defaultName +} + +// Get retrieves and returns value by specified `pattern`. +// It returns all values of current Json object if `pattern` is given empty or string ".". +// It returns nil if no value found by `pattern`. +// +// We can also access slice item by its index number in `pattern` like: +// "list.10", "array.0.name", "array.0.1.id". +// +// It returns a default value specified by `def` if value for `pattern` is not found. +func (c *AdapterFile) Get(ctx context.Context, pattern string) (value interface{}, err error) { + j, err := c.getJson() + if err != nil { + return nil, err + } + if j != nil { + return j.Get(pattern), nil + } + return nil, nil +} + +// Data retrieves and returns all configuration data as map type. +func (c *AdapterFile) Data(ctx context.Context) (data map[string]interface{}, err error) { + j, err := c.getJson() + if err != nil { + return nil, err + } + if j != nil { + return j.GetVar(".").Map(), nil + } + return nil, nil +} + +// MustGet acts as function Get, but it panics if error occurs. +func (c *AdapterFile) MustGet(ctx context.Context, pattern string) *gvar.Var { + v, err := c.Get(ctx, pattern) + if err != nil { + panic(err) + } + return gvar.New(v) +} + +// Clear removes all parsed configuration files content cache, +// which will force reload configuration content from file. +func (c *AdapterFile) Clear() { + c.jsonMap.Clear() +} + +// Dump prints current Json object with more manually readable. +func (c *AdapterFile) Dump() { + if j, _ := c.getJson(); j != nil { + j.Dump() + } +} + +// Available checks and returns whether configuration of given `file` is available. +func (c *AdapterFile) Available(fileName ...string) bool { + var ( + usedFileName string + ) + if len(fileName) > 0 && fileName[0] != "" { + usedFileName = fileName[0] + } else { + usedFileName = c.defaultName + } + if path, _ := c.GetFilePath(usedFileName); path != "" { + return true + } + if c.GetContent(usedFileName) != "" { + return true + } + return false +} + +// autoCheckAndAddMainPkgPathToSearchPaths automatically checks and adds directory path of package main +// to the searching path list if it's currently in development environment. +func (c *AdapterFile) autoCheckAndAddMainPkgPathToSearchPaths() { + if gmode.IsDevelop() { + mainPkgPath := gfile.MainPkgPath() + if mainPkgPath != "" { + if !c.searchPaths.Contains(mainPkgPath) { + c.searchPaths.Append(mainPkgPath) + } + } + } +} + +// getJson returns a *gjson.Json object for the specified `file` content. +// It would print error if file reading fails. It returns nil if any error occurs. +func (c *AdapterFile) getJson(fileName ...string) (configJson *gjson.Json, err error) { + var ( + usedFileName = c.defaultName + ) + if len(fileName) > 0 && fileName[0] != "" { + usedFileName = fileName[0] + } else { + usedFileName = c.defaultName + } + result := c.jsonMap.GetOrSetFuncLock(usedFileName, func() interface{} { + var ( + content string + filePath string + ) + // The configured content can be any kind of data type different from its file type. + isFromConfigContent := true + if content = c.GetContent(usedFileName); content == "" { + isFromConfigContent = false + filePath, err = c.GetFilePath(usedFileName) + if err != nil { + return nil + } + if filePath == "" { + return nil + } + if file := gres.Get(filePath); file != nil { + content = string(file.Content()) + } else { + content = gfile.GetContents(filePath) + } + } + // Note that the underlying configuration json object operations are concurrent safe. + dataType := gfile.ExtName(usedFileName) + if gjson.IsValidDataType(dataType) && !isFromConfigContent { + configJson, err = gjson.LoadContentType(dataType, content, true) + } else { + configJson, err = gjson.LoadContent(content, true) + } + if err == nil { + configJson.SetViolenceCheck(c.violenceCheck) + // Add monitor for this configuration file, + // any changes of this file will refresh its cache in Config object. + if filePath != "" && !gres.Contains(filePath) { + _, err = gfsnotify.Add(filePath, func(event *gfsnotify.Event) { + c.jsonMap.Remove(usedFileName) + }) + if err != nil { + return nil + } + } + return configJson + } + if err != nil { + if filePath != "" { + err = gerror.WrapCodef(gcode.CodeOperationFailed, err, `load config file "%s" failed`, filePath) + } else { + err = gerror.WrapCode(gcode.CodeOperationFailed, err, `load configuration failed`) + } + } + return nil + }) + if result != nil { + return result.(*gjson.Json), err + } + return +} diff --git a/os/gcfg/gcfg_adapter_file_content.go b/os/gcfg/gcfg_adapter_file_content.go new file mode 100644 index 000000000..2d6d4fd5c --- /dev/null +++ b/os/gcfg/gcfg_adapter_file_content.go @@ -0,0 +1,84 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcfg + +import ( + "context" + "github.com/gogf/gf/internal/intlog" +) + +// SetContent sets customized configuration content for specified `file`. +// The `file` is unnecessary param, default is DefaultConfigFile. +func (c *AdapterFile) SetContent(content string, file ...string) { + name := DefaultConfigFile + if len(file) > 0 { + name = file[0] + } + // Clear file cache for instances which cached `name`. + localInstances.LockFunc(func(m map[string]interface{}) { + if customConfigContentMap.Contains(name) { + for _, v := range m { + if configInstance, ok := v.(*Config); ok { + if fileConfig, ok := configInstance.GetAdapter().(*AdapterFile); ok { + fileConfig.jsonMap.Remove(name) + } + } + } + } + customConfigContentMap.Set(name, content) + }) +} + +// GetContent returns customized configuration content for specified `file`. +// The `file` is unnecessary param, default is DefaultConfigFile. +func (c *AdapterFile) GetContent(file ...string) string { + name := DefaultConfigFile + if len(file) > 0 { + name = file[0] + } + return customConfigContentMap.Get(name) +} + +// RemoveContent removes the global configuration with specified `file`. +// If `name` is not passed, it removes configuration of the default group name. +func (c *AdapterFile) RemoveContent(file ...string) { + name := DefaultConfigFile + if len(file) > 0 { + name = file[0] + } + // Clear file cache for instances which cached `name`. + localInstances.LockFunc(func(m map[string]interface{}) { + if customConfigContentMap.Contains(name) { + for _, v := range m { + if configInstance, ok := v.(*Config); ok { + if fileConfig, ok := configInstance.GetAdapter().(*AdapterFile); ok { + fileConfig.jsonMap.Remove(name) + } + } + } + customConfigContentMap.Remove(name) + } + }) + + intlog.Printf(context.TODO(), `RemoveContent: %s`, name) +} + +// ClearContent removes all global configuration contents. +func (c *AdapterFile) ClearContent() { + customConfigContentMap.Clear() + // Clear cache for all instances. + localInstances.LockFunc(func(m map[string]interface{}) { + for _, v := range m { + if configInstance, ok := v.(*Config); ok { + if fileConfig, ok := configInstance.GetAdapter().(*AdapterFile); ok { + fileConfig.jsonMap.Clear() + } + } + } + }) + intlog.Print(context.TODO(), `RemoveConfig`) +} diff --git a/os/gcfg/gcfg_adapter_file_path.go b/os/gcfg/gcfg_adapter_file_path.go new file mode 100644 index 000000000..486a27a78 --- /dev/null +++ b/os/gcfg/gcfg_adapter_file_path.go @@ -0,0 +1,208 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcfg + +import ( + "bytes" + "context" + "fmt" + "github.com/gogf/gf/errors/gcode" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gres" + "github.com/gogf/gf/os/gspath" + "github.com/gogf/gf/text/gstr" +) + +// SetPath sets the configuration directory path for file search. +// The parameter `path` can be absolute or relative path, +// but absolute path is strongly recommended. +func (c *AdapterFile) SetPath(path string) (err error) { + var ( + isDir = false + realPath = "" + ) + if file := gres.Get(path); file != nil { + realPath = path + isDir = file.FileInfo().IsDir() + } else { + // Absolute path. + realPath = gfile.RealPath(path) + if realPath == "" { + // Relative path. + c.searchPaths.RLockFunc(func(array []string) { + for _, v := range array { + if path, _ := gspath.Search(v, path); path != "" { + realPath = path + break + } + } + }) + } + if realPath != "" { + isDir = gfile.IsDir(realPath) + } + } + // Path not exist. + if realPath == "" { + buffer := bytes.NewBuffer(nil) + if c.searchPaths.Len() > 0 { + buffer.WriteString(fmt.Sprintf(`SetPath failed: cannot find directory "%s" in following paths:`, path)) + c.searchPaths.RLockFunc(func(array []string) { + for k, v := range array { + buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) + } + }) + } else { + buffer.WriteString(fmt.Sprintf(`SetPath failed: path "%s" does not exist`, path)) + } + return gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) + } + // Should be a directory. + if !isDir { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `SetPath failed: path "%s" should be directory type`, + path, + ) + } + // Repeated path check. + if c.searchPaths.Search(realPath) != -1 { + return nil + } + c.jsonMap.Clear() + c.searchPaths.Clear() + c.searchPaths.Append(realPath) + intlog.Print(context.TODO(), "SetPath:", realPath) + return nil +} + +// AddPath adds an absolute or relative path to the search paths. +func (c *AdapterFile) AddPath(path string) (err error) { + var ( + isDir = false + realPath = "" + ) + // It firstly checks the resource manager, + // and then checks the filesystem for the path. + if file := gres.Get(path); file != nil { + realPath = path + isDir = file.FileInfo().IsDir() + } else { + // Absolute path. + realPath = gfile.RealPath(path) + if realPath == "" { + // Relative path. + c.searchPaths.RLockFunc(func(array []string) { + for _, v := range array { + if path, _ := gspath.Search(v, path); path != "" { + realPath = path + break + } + } + }) + } + if realPath != "" { + isDir = gfile.IsDir(realPath) + } + } + if realPath == "" { + buffer := bytes.NewBuffer(nil) + if c.searchPaths.Len() > 0 { + buffer.WriteString(fmt.Sprintf(`AddPath failed: cannot find directory "%s" in following paths:`, path)) + c.searchPaths.RLockFunc(func(array []string) { + for k, v := range array { + buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) + } + }) + } else { + buffer.WriteString(fmt.Sprintf(`AddPath failed: path "%s" does not exist`, path)) + } + return gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) + } + if !isDir { + return gerror.NewCodef(gcode.CodeInvalidParameter, `AddPath failed: path "%s" should be directory type`, path) + } + // Repeated path check. + if c.searchPaths.Search(realPath) != -1 { + return nil + } + c.searchPaths.Append(realPath) + intlog.Print(context.TODO(), "AddPath:", realPath) + return nil +} + +// GetFilePath returns the absolute configuration file path for the given filename by `file`. +// If `file` is not passed, it returns the configuration file path of the default name. +// It returns an empty `path` string and an error if the given `file` does not exist. +func (c *AdapterFile) GetFilePath(fileName ...string) (path string, err error) { + var ( + usedFileName = c.defaultName + ) + if len(fileName) > 0 { + usedFileName = fileName[0] + } + // Searching resource manager. + if !gres.IsEmpty() { + for _, v := range resourceTryFiles { + if file := gres.Get(v + usedFileName); file != nil { + path = file.Name() + return + } + } + c.searchPaths.RLockFunc(func(array []string) { + for _, prefix := range array { + for _, v := range resourceTryFiles { + if file := gres.Get(prefix + v + usedFileName); file != nil { + path = file.Name() + return + } + } + } + }) + } + c.autoCheckAndAddMainPkgPathToSearchPaths() + // Searching the file system. + c.searchPaths.RLockFunc(func(array []string) { + for _, prefix := range array { + prefix = gstr.TrimRight(prefix, `\/`) + if path, _ = gspath.Search(prefix, usedFileName); path != "" { + return + } + if path, _ = gspath.Search(prefix+gfile.Separator+"config", usedFileName); path != "" { + return + } + } + }) + // If it cannot find the path of `file`, it formats and returns a detailed error. + if path == "" { + var ( + buffer = bytes.NewBuffer(nil) + ) + if c.searchPaths.Len() > 0 { + buffer.WriteString(fmt.Sprintf( + `config file "%s" not found in resource manager or the following system searching paths:`, + usedFileName, + )) + c.searchPaths.RLockFunc(func(array []string) { + index := 1 + for _, v := range array { + v = gstr.TrimRight(v, `\/`) + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) + index++ + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) + index++ + } + }) + } else { + buffer.WriteString(fmt.Sprintf(`cannot find config file "%s" with no path configured`, usedFileName)) + } + err = gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) + } + return +} diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go deleted file mode 100644 index 14fc71f4a..000000000 --- a/os/gcfg/gcfg_config.go +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gcfg - -import ( - "bytes" - "context" - "fmt" - "github.com/gogf/gf/container/garray" - "github.com/gogf/gf/container/gmap" - "github.com/gogf/gf/encoding/gjson" - "github.com/gogf/gf/errors/gcode" - "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/internal/intlog" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gfsnotify" - "github.com/gogf/gf/os/glog" - "github.com/gogf/gf/os/gres" - "github.com/gogf/gf/os/gspath" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/util/gmode" -) - -// New returns a new configuration management object. -// The parameter `file` specifies the default configuration file name for reading. -func New(file ...string) *Config { - name := DefaultConfigFile - if len(file) > 0 { - name = file[0] - } else { - // Custom default configuration file name from command line or environment. - if customFile := gcmd.GetOptWithEnv(commandEnvKeyForFile).String(); customFile != "" { - name = customFile - } - } - c := &Config{ - defaultName: name, - searchPaths: garray.NewStrArray(true), - jsonMap: gmap.NewStrAnyMap(true), - } - // Customized dir path from env/cmd. - if customPath := gcmd.GetOptWithEnv(commandEnvKeyForPath).String(); customPath != "" { - if gfile.Exists(customPath) { - _ = c.SetPath(customPath) - } else { - if errorPrint() { - glog.Errorf("[gcfg] Configuration directory path does not exist: %s", customPath) - } - } - } else { - // Dir path of working dir. - if err := c.AddPath(gfile.Pwd()); err != nil { - intlog.Error(context.TODO(), err) - } - - // Dir path of main package. - if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { - if err := c.AddPath(mainPath); err != nil { - intlog.Error(context.TODO(), err) - } - } - - // Dir path of binary. - if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { - if err := c.AddPath(selfPath); err != nil { - intlog.Error(context.TODO(), err) - } - } - } - return c -} - -// Instance returns an instance of Config with default settings. -// The parameter `name` is the name for the instance. But very note that, if the file "name.toml" -// exists in the configuration directory, it then sets it as the default configuration file. The -// toml file type is the default configuration file type. -func Instance(name ...string) *Config { - key := DefaultName - if len(name) > 0 && name[0] != "" { - key = name[0] - } - return instances.GetOrSetFuncLock(key, func() interface{} { - c := New() - // If it's not using default configuration or its configuration file is not available, - // it searches the possible configuration file according to the name and all supported - // file types. - if key != DefaultName || !c.Available() { - for _, fileType := range supportedFileTypes { - if file := fmt.Sprintf(`%s.%s`, key, fileType); c.Available(file) { - c.SetFileName(file) - break - } - } - } - return c - }).(*Config) -} - -// SetPath sets the configuration directory path for file search. -// The parameter `path` can be absolute or relative path, -// but absolute path is strongly recommended. -func (c *Config) SetPath(path string) error { - var ( - isDir = false - realPath = "" - ) - if file := gres.Get(path); file != nil { - realPath = path - isDir = file.FileInfo().IsDir() - } else { - // Absolute path. - realPath = gfile.RealPath(path) - if realPath == "" { - // Relative path. - c.searchPaths.RLockFunc(func(array []string) { - for _, v := range array { - if path, _ := gspath.Search(v, path); path != "" { - realPath = path - break - } - } - }) - } - if realPath != "" { - isDir = gfile.IsDir(realPath) - } - } - // Path not exist. - if realPath == "" { - buffer := bytes.NewBuffer(nil) - if c.searchPaths.Len() > 0 { - buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path)) - c.searchPaths.RLockFunc(func(array []string) { - for k, v := range array { - buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) - } - }) - } else { - buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path)) - } - err := gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) - if errorPrint() { - glog.Error(err) - } - return err - } - // Should be a directory. - if !isDir { - err := gerror.NewCodef( - gcode.CodeInvalidParameter, - `[gcfg] SetPath failed: path "%s" should be directory type`, - path, - ) - if errorPrint() { - glog.Error(err) - } - return err - } - // Repeated path check. - if c.searchPaths.Search(realPath) != -1 { - return nil - } - c.jsonMap.Clear() - c.searchPaths.Clear() - c.searchPaths.Append(realPath) - intlog.Print(context.TODO(), "SetPath:", realPath) - return nil -} - -// SetViolenceCheck sets whether to perform hierarchical conflict checking. -// This feature needs to be enabled when there is a level symbol in the key name. -// It is off in default. -// -// Note that, turning on this feature is quite expensive, and it is not recommended -// allowing separators in the key names. It is best to avoid this on the application side. -func (c *Config) SetViolenceCheck(check bool) { - c.violenceCheck = check - c.Clear() -} - -// AddPath adds an absolute or relative path to the search paths. -func (c *Config) AddPath(path string) error { - var ( - isDir = false - realPath = "" - ) - // It firstly checks the resource manager, - // and then checks the filesystem for the path. - if file := gres.Get(path); file != nil { - realPath = path - isDir = file.FileInfo().IsDir() - } else { - // Absolute path. - realPath = gfile.RealPath(path) - if realPath == "" { - // Relative path. - c.searchPaths.RLockFunc(func(array []string) { - for _, v := range array { - if path, _ := gspath.Search(v, path); path != "" { - realPath = path - break - } - } - }) - } - if realPath != "" { - isDir = gfile.IsDir(realPath) - } - } - if realPath == "" { - buffer := bytes.NewBuffer(nil) - if c.searchPaths.Len() > 0 { - buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path)) - c.searchPaths.RLockFunc(func(array []string) { - for k, v := range array { - buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) - } - }) - } else { - buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path)) - } - err := gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) - if errorPrint() { - glog.Error(err) - } - return err - } - if !isDir { - err := gerror.NewCodef(gcode.CodeInvalidParameter, `[gcfg] AddPath failed: path "%s" should be directory type`, path) - if errorPrint() { - glog.Error(err) - } - return err - } - // Repeated path check. - if c.searchPaths.Search(realPath) != -1 { - return nil - } - c.searchPaths.Append(realPath) - intlog.Print(context.TODO(), "AddPath:", realPath) - return nil -} - -// SetFileName sets the default configuration file name. -func (c *Config) SetFileName(name string) *Config { - c.defaultName = name - return c -} - -// GetFileName returns the default configuration file name. -func (c *Config) GetFileName() string { - return c.defaultName -} - -// Available checks and returns whether configuration of given `file` is available. -func (c *Config) Available(file ...string) bool { - var name string - if len(file) > 0 && file[0] != "" { - name = file[0] - } else { - name = c.defaultName - } - if path, _ := c.GetFilePath(name); path != "" { - return true - } - if GetContent(name) != "" { - return true - } - return false -} - -// GetFilePath returns the absolute configuration file path for the given filename by `file`. -// If `file` is not passed, it returns the configuration file path of the default name. -// It returns an empty `path` string and an error if the given `file` does not exist. -func (c *Config) GetFilePath(file ...string) (path string, err error) { - name := c.defaultName - if len(file) > 0 { - name = file[0] - } - // Searching resource manager. - if !gres.IsEmpty() { - for _, v := range resourceTryFiles { - if file := gres.Get(v + name); file != nil { - path = file.Name() - return - } - } - c.searchPaths.RLockFunc(func(array []string) { - for _, prefix := range array { - for _, v := range resourceTryFiles { - if file := gres.Get(prefix + v + name); file != nil { - path = file.Name() - return - } - } - } - }) - } - c.autoCheckAndAddMainPkgPathToSearchPaths() - // Searching the file system. - c.searchPaths.RLockFunc(func(array []string) { - for _, prefix := range array { - prefix = gstr.TrimRight(prefix, `\/`) - if path, _ = gspath.Search(prefix, name); path != "" { - return - } - if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" { - return - } - } - }) - // If it cannot find the path of `file`, it formats and returns a detailed error. - if path == "" { - var ( - buffer = bytes.NewBuffer(nil) - ) - if c.searchPaths.Len() > 0 { - buffer.WriteString(fmt.Sprintf(`[gcfg] cannot find config file "%s" in resource manager or the following paths:`, name)) - c.searchPaths.RLockFunc(func(array []string) { - index := 1 - for _, v := range array { - v = gstr.TrimRight(v, `\/`) - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) - index++ - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) - index++ - } - }) - } else { - buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path configured", name)) - } - err = gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) - } - return -} - -// autoCheckAndAddMainPkgPathToSearchPaths automatically checks and adds directory path of package main -// to the searching path list if it's currently in development environment. -func (c *Config) autoCheckAndAddMainPkgPathToSearchPaths() { - if gmode.IsDevelop() { - mainPkgPath := gfile.MainPkgPath() - if mainPkgPath != "" { - if !c.searchPaths.Contains(mainPkgPath) { - c.searchPaths.Append(mainPkgPath) - } - } - } -} - -// getJson returns a *gjson.Json object for the specified `file` content. -// It would print error if file reading fails. It return nil if any error occurs. -func (c *Config) getJson(file ...string) *gjson.Json { - var name string - if len(file) > 0 && file[0] != "" { - name = file[0] - } else { - name = c.defaultName - } - r := c.jsonMap.GetOrSetFuncLock(name, func() interface{} { - var ( - err error - content string - filePath string - ) - // The configured content can be any kind of data type different from its file type. - isFromConfigContent := true - if content = GetContent(name); content == "" { - isFromConfigContent = false - filePath, err = c.GetFilePath(name) - if err != nil && errorPrint() { - glog.Error(err) - } - if filePath == "" { - return nil - } - if file := gres.Get(filePath); file != nil { - content = string(file.Content()) - } else { - content = gfile.GetContents(filePath) - } - } - // Note that the underlying configuration json object operations are concurrent safe. - var ( - j *gjson.Json - ) - dataType := gfile.ExtName(name) - if gjson.IsValidDataType(dataType) && !isFromConfigContent { - j, err = gjson.LoadContentType(dataType, content, true) - } else { - j, err = gjson.LoadContent(content, true) - } - if err == nil { - j.SetViolenceCheck(c.violenceCheck) - // Add monitor for this configuration file, - // any changes of this file will refresh its cache in Config object. - if filePath != "" && !gres.Contains(filePath) { - _, err = gfsnotify.Add(filePath, func(event *gfsnotify.Event) { - c.jsonMap.Remove(name) - }) - if err != nil && errorPrint() { - glog.Error(err) - } - } - return j - } - if errorPrint() { - if filePath != "" { - glog.Criticalf(`[gcfg] load config file "%s" failed: %s`, filePath, err.Error()) - } else { - glog.Criticalf(`[gcfg] load configuration failed: %s`, err.Error()) - } - } - return nil - }) - if r != nil { - return r.(*gjson.Json) - } - return nil -} diff --git a/os/gcfg/gcfg_config_api.go b/os/gcfg/gcfg_config_api.go deleted file mode 100644 index f3adb7c9c..000000000 --- a/os/gcfg/gcfg_config_api.go +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gcfg - -import ( - "github.com/gogf/gf/errors/gcode" - "github.com/gogf/gf/errors/gerror" - "time" - - "github.com/gogf/gf/encoding/gjson" - - "github.com/gogf/gf/container/gvar" - "github.com/gogf/gf/os/gtime" -) - -// Set sets value with specified `pattern`. -// It supports hierarchical data access by char separator, which is '.' in default. -// It is commonly used for updates certain configuration value in runtime. -func (c *Config) Set(pattern string, value interface{}) error { - if j := c.getJson(); j != nil { - return j.Set(pattern, value) - } - return nil -} - -// Get retrieves and returns value by specified `pattern`. -// It returns all values of current Json object if `pattern` is given empty or string ".". -// It returns nil if no value found by `pattern`. -// -// We can also access slice item by its index number in `pattern` like: -// "list.10", "array.0.name", "array.0.1.id". -// -// It returns a default value specified by `def` if value for `pattern` is not found. -func (c *Config) Get(pattern string, def ...interface{}) interface{} { - if j := c.getJson(); j != nil { - return j.Get(pattern, def...) - } - return nil -} - -// GetVar returns a gvar.Var with value by given `pattern`. -func (c *Config) GetVar(pattern string, def ...interface{}) *gvar.Var { - if j := c.getJson(); j != nil { - return j.GetVar(pattern, def...) - } - return gvar.New(nil) -} - -// Contains checks whether the value by specified `pattern` exist. -func (c *Config) Contains(pattern string) bool { - if j := c.getJson(); j != nil { - return j.Contains(pattern) - } - return false -} - -// GetMap retrieves and returns the value by specified `pattern` as map[string]interface{}. -func (c *Config) GetMap(pattern string, def ...interface{}) map[string]interface{} { - if j := c.getJson(); j != nil { - return j.GetMap(pattern, def...) - } - return nil -} - -// GetMapStrStr retrieves and returns the value by specified `pattern` as map[string]string. -func (c *Config) GetMapStrStr(pattern string, def ...interface{}) map[string]string { - if j := c.getJson(); j != nil { - return j.GetMapStrStr(pattern, def...) - } - return nil -} - -// GetArray retrieves the value by specified `pattern`, -// and converts it to a slice of []interface{}. -func (c *Config) GetArray(pattern string, def ...interface{}) []interface{} { - if j := c.getJson(); j != nil { - return j.GetArray(pattern, def...) - } - return nil -} - -// GetBytes retrieves the value by specified `pattern` and converts it to []byte. -func (c *Config) GetBytes(pattern string, def ...interface{}) []byte { - if j := c.getJson(); j != nil { - return j.GetBytes(pattern, def...) - } - return nil -} - -// GetString retrieves the value by specified `pattern` and converts it to string. -func (c *Config) GetString(pattern string, def ...interface{}) string { - if j := c.getJson(); j != nil { - return j.GetString(pattern, def...) - } - return "" -} - -// GetStrings retrieves the value by specified `pattern` and converts it to []string. -func (c *Config) GetStrings(pattern string, def ...interface{}) []string { - if j := c.getJson(); j != nil { - return j.GetStrings(pattern, def...) - } - return nil -} - -// GetInterfaces is alias of GetArray. -// See GetArray. -func (c *Config) GetInterfaces(pattern string, def ...interface{}) []interface{} { - if j := c.getJson(); j != nil { - return j.GetInterfaces(pattern, def...) - } - return nil -} - -// GetBool retrieves the value by specified `pattern`, -// converts and returns it as bool. -// It returns false when value is: "", 0, false, off, nil; -// or returns true instead. -func (c *Config) GetBool(pattern string, def ...interface{}) bool { - if j := c.getJson(); j != nil { - return j.GetBool(pattern, def...) - } - return false -} - -// GetFloat32 retrieves the value by specified `pattern` and converts it to float32. -func (c *Config) GetFloat32(pattern string, def ...interface{}) float32 { - if j := c.getJson(); j != nil { - return j.GetFloat32(pattern, def...) - } - return 0 -} - -// GetFloat64 retrieves the value by specified `pattern` and converts it to float64. -func (c *Config) GetFloat64(pattern string, def ...interface{}) float64 { - if j := c.getJson(); j != nil { - return j.GetFloat64(pattern, def...) - } - return 0 -} - -// GetFloats retrieves the value by specified `pattern` and converts it to []float64. -func (c *Config) GetFloats(pattern string, def ...interface{}) []float64 { - if j := c.getJson(); j != nil { - return j.GetFloats(pattern, def...) - } - return nil -} - -// GetInt retrieves the value by specified `pattern` and converts it to int. -func (c *Config) GetInt(pattern string, def ...interface{}) int { - if j := c.getJson(); j != nil { - return j.GetInt(pattern, def...) - } - return 0 -} - -// GetInt8 retrieves the value by specified `pattern` and converts it to int8. -func (c *Config) GetInt8(pattern string, def ...interface{}) int8 { - if j := c.getJson(); j != nil { - return j.GetInt8(pattern, def...) - } - return 0 -} - -// GetInt16 retrieves the value by specified `pattern` and converts it to int16. -func (c *Config) GetInt16(pattern string, def ...interface{}) int16 { - if j := c.getJson(); j != nil { - return j.GetInt16(pattern, def...) - } - return 0 -} - -// GetInt32 retrieves the value by specified `pattern` and converts it to int32. -func (c *Config) GetInt32(pattern string, def ...interface{}) int32 { - if j := c.getJson(); j != nil { - return j.GetInt32(pattern, def...) - } - return 0 -} - -// GetInt64 retrieves the value by specified `pattern` and converts it to int64. -func (c *Config) GetInt64(pattern string, def ...interface{}) int64 { - if j := c.getJson(); j != nil { - return j.GetInt64(pattern, def...) - } - return 0 -} - -// GetInts retrieves the value by specified `pattern` and converts it to []int. -func (c *Config) GetInts(pattern string, def ...interface{}) []int { - if j := c.getJson(); j != nil { - return j.GetInts(pattern, def...) - } - return nil -} - -// GetUint retrieves the value by specified `pattern` and converts it to uint. -func (c *Config) GetUint(pattern string, def ...interface{}) uint { - if j := c.getJson(); j != nil { - return j.GetUint(pattern, def...) - } - return 0 -} - -// GetUint8 retrieves the value by specified `pattern` and converts it to uint8. -func (c *Config) GetUint8(pattern string, def ...interface{}) uint8 { - if j := c.getJson(); j != nil { - return j.GetUint8(pattern, def...) - } - return 0 -} - -// GetUint16 retrieves the value by specified `pattern` and converts it to uint16. -func (c *Config) GetUint16(pattern string, def ...interface{}) uint16 { - if j := c.getJson(); j != nil { - return j.GetUint16(pattern, def...) - } - return 0 -} - -// GetUint32 retrieves the value by specified `pattern` and converts it to uint32. -func (c *Config) GetUint32(pattern string, def ...interface{}) uint32 { - if j := c.getJson(); j != nil { - return j.GetUint32(pattern, def...) - } - return 0 -} - -// GetUint64 retrieves the value by specified `pattern` and converts it to uint64. -func (c *Config) GetUint64(pattern string, def ...interface{}) uint64 { - if j := c.getJson(); j != nil { - return j.GetUint64(pattern, def...) - } - return 0 -} - -// GetTime retrieves the value by specified `pattern` and converts it to time.Time. -func (c *Config) GetTime(pattern string, format ...string) time.Time { - if j := c.getJson(); j != nil { - return j.GetTime(pattern, format...) - } - return time.Time{} -} - -// GetDuration retrieves the value by specified `pattern` and converts it to time.Duration. -func (c *Config) GetDuration(pattern string, def ...interface{}) time.Duration { - if j := c.getJson(); j != nil { - return j.GetDuration(pattern, def...) - } - return 0 -} - -// GetGTime retrieves the value by specified `pattern` and converts it to *gtime.Time. -func (c *Config) GetGTime(pattern string, format ...string) *gtime.Time { - if j := c.getJson(); j != nil { - return j.GetGTime(pattern, format...) - } - return nil -} - -// GetJson gets the value by specified `pattern`, -// and converts it to a un-concurrent-safe Json object. -func (c *Config) GetJson(pattern string, def ...interface{}) *gjson.Json { - if j := c.getJson(); j != nil { - return j.GetJson(pattern, def...) - } - return nil -} - -// GetJsons gets the value by specified `pattern`, -// and converts it to a slice of un-concurrent-safe Json object. -func (c *Config) GetJsons(pattern string, def ...interface{}) []*gjson.Json { - if j := c.getJson(); j != nil { - return j.GetJsons(pattern, def...) - } - return nil -} - -// GetJsonMap gets the value by specified `pattern`, -// and converts it to a map of un-concurrent-safe Json object. -func (c *Config) GetJsonMap(pattern string, def ...interface{}) map[string]*gjson.Json { - if j := c.getJson(); j != nil { - return j.GetJsonMap(pattern, def...) - } - return nil -} - -// GetStruct retrieves the value by specified `pattern` and converts it to specified object -// `pointer`. The `pointer` should be the pointer to an object. -func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.GetStruct(pattern, pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// GetStructs converts any slice to given struct slice. -func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.GetStructs(pattern, pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// GetMapToMap retrieves the value by specified `pattern` and converts it to specified map variable. -// See gconv.MapToMap. -func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.GetMapToMap(pattern, pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// GetMapToMaps retrieves the value by specified `pattern` and converts it to specified map slice -// variable. -// See gconv.MapToMaps. -func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.GetMapToMaps(pattern, pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// GetMapToMapsDeep retrieves the value by specified `pattern` and converts it to specified map slice -// variable recursively. -// See gconv.MapToMapsDeep. -func (c *Config) GetMapToMapsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.GetMapToMapsDeep(pattern, pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// Map converts current Json object to map[string]interface{}. It returns nil if fails. -func (c *Config) Map() map[string]interface{} { - if j := c.getJson(); j != nil { - return j.Map() - } - return nil -} - -// Array converts current Json object to []interface{}. -// It returns nil if fails. -func (c *Config) Array() []interface{} { - if j := c.getJson(); j != nil { - return j.Array() - } - return nil -} - -// Struct converts current Json object to specified object. -// The `pointer` should be a pointer type of *struct. -func (c *Config) Struct(pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.Struct(pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// Structs converts current Json object to specified object slice. -// The `pointer` should be a pointer type of []struct/*struct. -func (c *Config) Structs(pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.Structs(pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// MapToMap converts current Json object to specified map variable. -// The parameter of `pointer` should be type of *map. -func (c *Config) MapToMap(pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.MapToMap(pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// MapToMaps converts current Json object to specified map variable slice. -// The parameter of `pointer` should be type of []map/*map. -func (c *Config) MapToMaps(pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.MapToMaps(pointer, mapping...) - } - return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") -} - -// Clear removes all parsed configuration files content cache, -// which will force reload configuration content from file. -func (c *Config) Clear() { - c.jsonMap.Clear() -} - -// Dump prints current Json object with more manually readable. -func (c *Config) Dump() { - if j := c.getJson(); j != nil { - j.Dump() - } -} diff --git a/os/gcfg/gcfg_z_example_pattern_test.go b/os/gcfg/gcfg_z_example_pattern_test.go deleted file mode 100644 index a5ba919d5..000000000 --- a/os/gcfg/gcfg_z_example_pattern_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gcfg_test - -import ( - "fmt" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/internal/intlog" - "github.com/gogf/gf/os/gcfg" -) - -func Example_mapSliceChange() { - intlog.SetEnabled(false) - defer intlog.SetEnabled(true) - // For testing/example only. - content := `{"map":{"key":"value"}, "slice":[59,90]}` - gcfg.SetContent(content) - defer gcfg.RemoveContent() - - m := g.Cfg().GetMap("map") - fmt.Println(m) - - // Change the key-value pair. - m["key"] = "john" - - // It changes the underlying key-value pair. - fmt.Println(g.Cfg().GetMap("map")) - - s := g.Cfg().GetArray("slice") - fmt.Println(s) - - // Change the value of specified index. - s[0] = 100 - - // It changes the underlying slice. - fmt.Println(g.Cfg().GetArray("slice")) - - // output: - // map[key:value] - // map[key:john] - // [59 90] - // [100 90] -} diff --git a/os/gcfg/gcfg_z_init_test.go b/os/gcfg/gcfg_z_init_test.go new file mode 100644 index 000000000..01918f810 --- /dev/null +++ b/os/gcfg/gcfg_z_init_test.go @@ -0,0 +1,15 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" -benchmem + +package gcfg_test + +import "context" + +var ( + ctx = context.TODO() +) diff --git a/os/gcfg/gcfg_z_unit_adapter_file_test.go b/os/gcfg/gcfg_z_unit_adapter_file_test.go new file mode 100644 index 000000000..6c84522d9 --- /dev/null +++ b/os/gcfg/gcfg_z_unit_adapter_file_test.go @@ -0,0 +1,100 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" -benchmem + +package gcfg_test + +import ( + "github.com/gogf/gf/os/gcfg" + "github.com/gogf/gf/test/gtest" + "testing" +) + +func TestAdapterFile_SetPath(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c, err := gcfg.NewAdapterFile("config.yml") + t.AssertNil(err) + + err = c.SetPath("/tmp") + t.AssertNil(err) + + err = c.SetPath("gcfg.go") + t.AssertNE(err, nil) + + v, err := c.Get(ctx, "name") + t.AssertNE(err, nil) + t.Assert(v, nil) + }) +} + +func TestAdapterFile_AddPath(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c, err := gcfg.NewAdapterFile("config.yml") + t.AssertNil(err) + + err = c.AddPath("/tmp") + t.AssertNil(err) + + err = c.AddPath("gcfg.go") + t.AssertNE(err, nil) + + v, err := c.Get(ctx, "name") + t.AssertNE(err, nil) + t.Assert(v, nil) + }) +} + +func TestAdapterFile_SetViolenceCheck(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c, err := gcfg.NewAdapterFile("config.yml") + t.AssertNil(err) + c.SetViolenceCheck(true) + v, err := c.Get(ctx, "name") + t.AssertNE(err, nil) + t.Assert(v, nil) + }) +} + +func TestAdapterFile_FilePath(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c, err := gcfg.NewAdapterFile("config.yml") + t.AssertNil(err) + + path, _ := c.GetFilePath("tmp") + t.Assert(path, "") + + path, _ = c.GetFilePath("tmp") + t.Assert(path, "") + }) +} + +func TestAdapterFile_Content(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c, err := gcfg.NewAdapterFile() + t.AssertNil(err) + + c.SetContent("gf", "config.yml") + t.Assert(c.GetContent("config.yml"), "gf") + c.SetContent("gf1", "config.yml") + t.Assert(c.GetContent("config.yml"), "gf1") + c.RemoveContent("config.yml") + c.ClearContent() + t.Assert(c.GetContent("name"), "") + }) +} + +func TestAdapterFile_With_UTF8_BOM(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c, err := gcfg.NewAdapterFile("test-cfg-with-utf8-bom") + t.AssertNil(err) + + t.Assert(c.SetPath("testdata"), nil) + c.SetFileName("cfg-with-utf8-bom.toml") + t.Assert(c.MustGet(ctx, "test.testInt"), 1) + t.Assert(c.MustGet(ctx, "test.testStr"), "test") + }) +} diff --git a/os/gcfg/gcfg_z_unit_basic_test.go b/os/gcfg/gcfg_z_unit_basic_test.go index 51fd92638..290dc2cc3 100644 --- a/os/gcfg/gcfg_z_unit_basic_test.go +++ b/os/gcfg/gcfg_z_unit_basic_test.go @@ -9,22 +9,15 @@ package gcfg_test import ( - "os" "testing" "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/encoding/gjson" - "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gcfg" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/test/gtest" ) -func init() { - os.Setenv("GF_GCFG_ERRORPRINT", "false") -} - func Test_Basic1(t *testing.T) { config := ` v1 = 1 @@ -42,46 +35,10 @@ array = [1,2,3] t.Assert(err, nil) defer gfile.Remove(path) - c := gcfg.New() - t.Assert(c.Get("v1"), 1) - t.AssertEQ(c.GetInt("v1"), 1) - t.AssertEQ(c.GetInt8("v1"), int8(1)) - t.AssertEQ(c.GetInt16("v1"), int16(1)) - t.AssertEQ(c.GetInt32("v1"), int32(1)) - t.AssertEQ(c.GetInt64("v1"), int64(1)) - t.AssertEQ(c.GetUint("v1"), uint(1)) - t.AssertEQ(c.GetUint8("v1"), uint8(1)) - t.AssertEQ(c.GetUint16("v1"), uint16(1)) - t.AssertEQ(c.GetUint32("v1"), uint32(1)) - t.AssertEQ(c.GetUint64("v1"), uint64(1)) - - t.AssertEQ(c.GetVar("v1").String(), "1") - t.AssertEQ(c.GetVar("v1").Bool(), true) - t.AssertEQ(c.GetVar("v2").String(), "true") - t.AssertEQ(c.GetVar("v2").Bool(), true) - - t.AssertEQ(c.GetString("v1"), "1") - t.AssertEQ(c.GetFloat32("v4"), float32(1.23)) - t.AssertEQ(c.GetFloat64("v4"), float64(1.23)) - t.AssertEQ(c.GetString("v2"), "true") - t.AssertEQ(c.GetBool("v2"), true) - t.AssertEQ(c.GetBool("v3"), false) - - t.AssertEQ(c.Contains("v1"), true) - t.AssertEQ(c.Contains("v2"), true) - t.AssertEQ(c.Contains("v3"), true) - t.AssertEQ(c.Contains("v4"), true) - t.AssertEQ(c.Contains("v5"), false) - - t.AssertEQ(c.GetInts("array"), []int{1, 2, 3}) - t.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"}) - t.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetMap("redis"), map[string]interface{}{ - "disk": "127.0.0.1:6379,0", - "cache": "127.0.0.1:6379,1", - }) - filepath, _ := c.GetFilePath() + c, err := gcfg.New() + t.AssertNil(err) + t.Assert(c.MustGet(ctx, "v1"), 1) + filepath, _ := c.GetAdapter().(*gcfg.AdapterFile).GetFilePath() t.AssertEQ(filepath, gfile.Pwd()+gfile.Separator+path) }) } @@ -96,8 +53,9 @@ func Test_Basic2(t *testing.T) { _ = gfile.Remove(path) }() - c := gcfg.New() - t.Assert(c.Get("log-path"), "logs") + c, err := gcfg.New() + t.AssertNil(err) + t.Assert(c.MustGet(ctx, "log-path"), "logs") }) } @@ -112,49 +70,12 @@ array = [1,2,3] disk = "127.0.0.1:6379,0" cache = "127.0.0.1:6379,1" ` - gcfg.SetContent(content) - defer gcfg.ClearContent() - gtest.C(t, func(t *gtest.T) { - c := gcfg.New() - t.Assert(c.Get("v1"), 1) - t.AssertEQ(c.GetInt("v1"), 1) - t.AssertEQ(c.GetInt8("v1"), int8(1)) - t.AssertEQ(c.GetInt16("v1"), int16(1)) - t.AssertEQ(c.GetInt32("v1"), int32(1)) - t.AssertEQ(c.GetInt64("v1"), int64(1)) - t.AssertEQ(c.GetUint("v1"), uint(1)) - t.AssertEQ(c.GetUint8("v1"), uint8(1)) - t.AssertEQ(c.GetUint16("v1"), uint16(1)) - t.AssertEQ(c.GetUint32("v1"), uint32(1)) - t.AssertEQ(c.GetUint64("v1"), uint64(1)) - - t.AssertEQ(c.GetVar("v1").String(), "1") - t.AssertEQ(c.GetVar("v1").Bool(), true) - t.AssertEQ(c.GetVar("v2").String(), "true") - t.AssertEQ(c.GetVar("v2").Bool(), true) - - t.AssertEQ(c.GetString("v1"), "1") - t.AssertEQ(c.GetFloat32("v4"), float32(1.23)) - t.AssertEQ(c.GetFloat64("v4"), float64(1.23)) - t.AssertEQ(c.GetString("v2"), "true") - t.AssertEQ(c.GetBool("v2"), true) - t.AssertEQ(c.GetBool("v3"), false) - - t.AssertEQ(c.Contains("v1"), true) - t.AssertEQ(c.Contains("v2"), true) - t.AssertEQ(c.Contains("v3"), true) - t.AssertEQ(c.Contains("v4"), true) - t.AssertEQ(c.Contains("v5"), false) - - t.AssertEQ(c.GetInts("array"), []int{1, 2, 3}) - t.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"}) - t.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetMap("redis"), map[string]interface{}{ - "disk": "127.0.0.1:6379,0", - "cache": "127.0.0.1:6379,1", - }) + c, err := gcfg.New() + t.AssertNil(err) + c.GetAdapter().(*gcfg.AdapterFile).SetContent(content) + defer c.GetAdapter().(*gcfg.AdapterFile).ClearContent() + t.Assert(c.MustGet(ctx, "v1"), 1) }) } @@ -184,43 +105,38 @@ func Test_SetFileName(t *testing.T) { _ = gfile.Remove(path) }() - c := gcfg.New() + config, err := gcfg.New() + t.AssertNil(err) + c := config.GetAdapter().(*gcfg.AdapterFile) c.SetFileName(path) - t.Assert(c.Get("v1"), 1) - t.AssertEQ(c.GetInt("v1"), 1) - t.AssertEQ(c.GetInt8("v1"), int8(1)) - t.AssertEQ(c.GetInt16("v1"), int16(1)) - t.AssertEQ(c.GetInt32("v1"), int32(1)) - t.AssertEQ(c.GetInt64("v1"), int64(1)) - t.AssertEQ(c.GetUint("v1"), uint(1)) - t.AssertEQ(c.GetUint8("v1"), uint8(1)) - t.AssertEQ(c.GetUint16("v1"), uint16(1)) - t.AssertEQ(c.GetUint32("v1"), uint32(1)) - t.AssertEQ(c.GetUint64("v1"), uint64(1)) + t.Assert(c.MustGet(ctx, "v1"), 1) + t.AssertEQ(c.MustGet(ctx, "v1").Int(), 1) + t.AssertEQ(c.MustGet(ctx, "v1").Int8(), int8(1)) + t.AssertEQ(c.MustGet(ctx, "v1").Int16(), int16(1)) + t.AssertEQ(c.MustGet(ctx, "v1").Int32(), int32(1)) + t.AssertEQ(c.MustGet(ctx, "v1").Int64(), int64(1)) + t.AssertEQ(c.MustGet(ctx, "v1").Uint(), uint(1)) + t.AssertEQ(c.MustGet(ctx, "v1").Uint8(), uint8(1)) + t.AssertEQ(c.MustGet(ctx, "v1").Uint16(), uint16(1)) + t.AssertEQ(c.MustGet(ctx, "v1").Uint32(), uint32(1)) + t.AssertEQ(c.MustGet(ctx, "v1").Uint64(), uint64(1)) - t.AssertEQ(c.GetVar("v1").String(), "1") - t.AssertEQ(c.GetVar("v1").Bool(), true) - t.AssertEQ(c.GetVar("v2").String(), "true") - t.AssertEQ(c.GetVar("v2").Bool(), true) + t.AssertEQ(c.MustGet(ctx, "v1").String(), "1") + t.AssertEQ(c.MustGet(ctx, "v1").Bool(), true) + t.AssertEQ(c.MustGet(ctx, "v2").String(), "true") + t.AssertEQ(c.MustGet(ctx, "v2").Bool(), true) - t.AssertEQ(c.GetString("v1"), "1") - t.AssertEQ(c.GetFloat32("v4"), float32(1.234)) - t.AssertEQ(c.GetFloat64("v4"), float64(1.234)) - t.AssertEQ(c.GetString("v2"), "true") - t.AssertEQ(c.GetBool("v2"), true) - t.AssertEQ(c.GetBool("v3"), false) + t.AssertEQ(c.MustGet(ctx, "v1").String(), "1") + t.AssertEQ(c.MustGet(ctx, "v4").Float32(), float32(1.234)) + t.AssertEQ(c.MustGet(ctx, "v4").Float64(), float64(1.234)) + t.AssertEQ(c.MustGet(ctx, "v2").String(), "true") + t.AssertEQ(c.MustGet(ctx, "v2").Bool(), true) + t.AssertEQ(c.MustGet(ctx, "v3").Bool(), false) - t.AssertEQ(c.Contains("v1"), true) - t.AssertEQ(c.Contains("v2"), true) - t.AssertEQ(c.Contains("v3"), true) - t.AssertEQ(c.Contains("v4"), true) - t.AssertEQ(c.Contains("v5"), false) - - t.AssertEQ(c.GetInts("array"), []int{1, 2, 3}) - t.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"}) - t.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetMap("redis"), map[string]interface{}{ + t.AssertEQ(c.MustGet(ctx, "array").Ints(), []int{1, 2, 3}) + t.AssertEQ(c.MustGet(ctx, "array").Strings(), []string{"1", "2", "3"}) + t.AssertEQ(c.MustGet(ctx, "array").Interfaces(), []interface{}{1, 2, 3}) + t.AssertEQ(c.MustGet(ctx, "redis").Map(), map[string]interface{}{ "disk": "127.0.0.1:6379,0", "cache": "127.0.0.1:6379,1", }) @@ -229,67 +145,7 @@ func Test_SetFileName(t *testing.T) { }) } -func TestCfg_New(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - os.Setenv("GF_GCFG_PATH", "config") - c := gcfg.New("config.yml") - t.Assert(c.Get("name"), nil) - t.Assert(c.GetFileName(), "config.yml") - - configPath := gfile.Pwd() + gfile.Separator + "config" - _ = gfile.Mkdir(configPath) - defer gfile.Remove(configPath) - - c = gcfg.New("config.yml") - t.Assert(c.Get("name"), nil) - - _ = os.Unsetenv("GF_GCFG_PATH") - c = gcfg.New("config.yml") - t.Assert(c.Get("name"), nil) - }) -} - -func TestCfg_SetPath(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - c := gcfg.New("config.yml") - err := c.SetPath("tmp") - t.AssertNE(err, nil) - err = c.SetPath("gcfg.go") - t.AssertNE(err, nil) - t.Assert(c.Get("name"), nil) - }) -} - -func TestCfg_SetViolenceCheck(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - c := gcfg.New("config.yml") - c.SetViolenceCheck(true) - t.Assert(c.Get("name"), nil) - }) -} - -func TestCfg_AddPath(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - c := gcfg.New("config.yml") - err := c.AddPath("tmp") - t.AssertNE(err, nil) - err = c.AddPath("gcfg.go") - t.AssertNE(err, nil) - t.Assert(c.Get("name"), nil) - }) -} - -func TestCfg_FilePath(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - c := gcfg.New("config.yml") - path, _ := c.GetFilePath("tmp") - t.Assert(path, "") - path, _ = c.GetFilePath("tmp") - t.Assert(path, "") - }) -} - -func TestCfg_et(t *testing.T) { +func TestCfg_Set(t *testing.T) { config := `log-path = "logs"` gtest.C(t, func(t *gtest.T) { path := gcfg.DefaultConfigFile @@ -297,16 +153,18 @@ func TestCfg_et(t *testing.T) { t.Assert(err, nil) defer gfile.Remove(path) - c := gcfg.New() - t.Assert(c.Get("log-path"), "logs") + adapterFile, err := gcfg.NewAdapterFile() + t.AssertNil(err) + t.Assert(adapterFile.MustGet(ctx, "log-path"), "logs") - err = c.Set("log-path", "custom-logs") + c := gcfg.NewWithAdapter(adapterFile) + c.Set(ctx, "log-path", "custom-logs") t.Assert(err, nil) - t.Assert(c.Get("log-path"), "custom-logs") + t.Assert(c.MustGet(ctx, "log-path"), "custom-logs") }) } -func TestCfg_Get(t *testing.T) { +func TestCfg_Get_WrongConfigFile(t *testing.T) { gtest.C(t, func(t *gtest.T) { var err error configPath := gfile.TempDir(gtime.TimestampNanoStr()) @@ -323,82 +181,13 @@ func TestCfg_Get(t *testing.T) { "wrong config", ) t.Assert(err, nil) - c := gcfg.New("config.yml") - t.Assert(c.Get("name"), nil) - t.Assert(c.GetVar("name").Val(), nil) - t.Assert(c.Contains("name"), false) - t.Assert(c.GetMap("name"), nil) - t.Assert(c.GetArray("name"), nil) - t.Assert(c.GetString("name"), "") - t.Assert(c.GetStrings("name"), nil) - t.Assert(c.GetInterfaces("name"), nil) - t.Assert(c.GetBool("name"), false) - t.Assert(c.GetFloat32("name"), 0) - t.Assert(c.GetFloat64("name"), 0) - t.Assert(c.GetFloats("name"), nil) - t.Assert(c.GetInt("name"), 0) - t.Assert(c.GetInt8("name"), 0) - t.Assert(c.GetInt16("name"), 0) - t.Assert(c.GetInt32("name"), 0) - t.Assert(c.GetInt64("name"), 0) - t.Assert(c.GetInts("name"), nil) - t.Assert(c.GetUint("name"), 0) - t.Assert(c.GetUint8("name"), 0) - t.Assert(c.GetUint16("name"), 0) - t.Assert(c.GetUint32("name"), 0) - t.Assert(c.GetUint64("name"), 0) - t.Assert(c.GetTime("name").Format("2006-01-02"), "0001-01-01") - t.Assert(c.GetGTime("name"), nil) - t.Assert(c.GetDuration("name").String(), "0s") - name := struct { - Name string - }{} - t.Assert(c.GetStruct("name", &name) == nil, false) + adapterFile, err := gcfg.NewAdapterFile("config.yml") + t.AssertNil(err) - c.Clear() - - arr, _ := gjson.Encode( - g.Map{ - "name": "gf", - "time": "2019-06-12", - "person": g.Map{"name": "gf"}, - "floats": g.Slice{1, 2, 3}, - }, - ) - err = gfile.PutBytes( - gfile.Join(configPath, "config.yml"), - arr, - ) - t.Assert(err, nil) - t.Assert(c.GetTime("time").Format("2006-01-02"), "2019-06-12") - t.Assert(c.GetGTime("time").Format("Y-m-d"), "2019-06-12") - t.Assert(c.GetDuration("time").String(), "0s") - - err = c.GetStruct("person", &name) - t.Assert(err, nil) - t.Assert(name.Name, "gf") - t.Assert(c.GetFloats("floats") == nil, false) - }) -} - -func TestCfg_Config(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - gcfg.SetContent("gf", "config.yml") - t.Assert(gcfg.GetContent("config.yml"), "gf") - gcfg.SetContent("gf1", "config.yml") - t.Assert(gcfg.GetContent("config.yml"), "gf1") - gcfg.RemoveContent("config.yml") - gcfg.ClearContent() - t.Assert(gcfg.GetContent("name"), "") - }) -} - -func TestCfg_With_UTF8_BOM(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - cfg := g.Cfg("test-cfg-with-utf8-bom") - t.Assert(cfg.SetPath("testdata"), nil) - cfg.SetFileName("cfg-with-utf8-bom.toml") - t.Assert(cfg.GetInt("test.testInt"), 1) - t.Assert(cfg.GetString("test.testStr"), "test") + c := gcfg.NewWithAdapter(adapterFile) + v, err := c.Get(ctx, "name") + t.AssertNE(err, nil) + t.Assert(v, nil) + adapterFile.Clear() }) } diff --git a/os/gcfg/gcfg_z_unit_instance_test.go b/os/gcfg/gcfg_z_unit_instance_test.go index 879a5461d..621d6f183 100644 --- a/os/gcfg/gcfg_z_unit_instance_test.go +++ b/os/gcfg/gcfg_z_unit_instance_test.go @@ -9,6 +9,7 @@ package gcfg import ( + "context" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/os/genv" @@ -17,6 +18,10 @@ import ( "testing" ) +var ( + ctx = context.TODO() +) + func Test_Instance_Basic(t *testing.T) { config := ` array = [1.0, 2.0, 3.0] @@ -39,45 +44,8 @@ v4 = "1.234" }() c := Instance() - t.Assert(c.Get("v1"), 1) - t.AssertEQ(c.GetInt("v1"), 1) - t.AssertEQ(c.GetInt8("v1"), int8(1)) - t.AssertEQ(c.GetInt16("v1"), int16(1)) - t.AssertEQ(c.GetInt32("v1"), int32(1)) - t.AssertEQ(c.GetInt64("v1"), int64(1)) - t.AssertEQ(c.GetUint("v1"), uint(1)) - t.AssertEQ(c.GetUint8("v1"), uint8(1)) - t.AssertEQ(c.GetUint16("v1"), uint16(1)) - t.AssertEQ(c.GetUint32("v1"), uint32(1)) - t.AssertEQ(c.GetUint64("v1"), uint64(1)) - - t.AssertEQ(c.GetVar("v1").String(), "1") - t.AssertEQ(c.GetVar("v1").Bool(), true) - t.AssertEQ(c.GetVar("v2").String(), "true") - t.AssertEQ(c.GetVar("v2").Bool(), true) - - t.AssertEQ(c.GetString("v1"), "1") - t.AssertEQ(c.GetFloat32("v4"), float32(1.234)) - t.AssertEQ(c.GetFloat64("v4"), float64(1.234)) - t.AssertEQ(c.GetString("v2"), "true") - t.AssertEQ(c.GetBool("v2"), true) - t.AssertEQ(c.GetBool("v3"), false) - - t.AssertEQ(c.Contains("v1"), true) - t.AssertEQ(c.Contains("v2"), true) - t.AssertEQ(c.Contains("v3"), true) - t.AssertEQ(c.Contains("v4"), true) - t.AssertEQ(c.Contains("v5"), false) - - t.AssertEQ(c.GetInts("array"), []int{1, 2, 3}) - t.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"}) - t.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetMap("redis"), map[string]interface{}{ - "disk": "127.0.0.1:6379,0", - "cache": "127.0.0.1:6379,1", - }) - filepath, _ := c.GetFilePath() + t.Assert(c.MustGet(ctx, "v1"), 1) + filepath, _ := c.GetAdapter().(*AdapterFile).GetFilePath() t.AssertEQ(filepath, gfile.Pwd()+gfile.Separator+path) }) } @@ -92,28 +60,28 @@ func Test_Instance_AutoLocateConfigFile(t *testing.T) { t.AssertNil(gfile.Chdir(gdebug.TestDataPath())) defer gfile.Chdir(pwd) t.Assert(Instance("c1") != nil, true) - t.Assert(Instance("c1").Get("my-config"), "1") - t.Assert(Instance("folder1/c1").Get("my-config"), "2") + t.Assert(Instance("c1").MustGet(ctx, "my-config"), "1") + t.Assert(Instance("folder1/c1").MustGet(ctx, "my-config"), "2") }) // Automatically locate the configuration file with supported file extensions. gtest.C(t, func(t *gtest.T) { pwd := gfile.Pwd() t.AssertNil(gfile.Chdir(gdebug.TestDataPath("folder1"))) defer gfile.Chdir(pwd) - t.Assert(Instance("c2").Get("my-config"), 2) + t.Assert(Instance("c2").MustGet(ctx, "my-config"), 2) }) // Default configuration file. gtest.C(t, func(t *gtest.T) { - instances.Clear() + localInstances.Clear() pwd := gfile.Pwd() t.AssertNil(gfile.Chdir(gdebug.TestDataPath("default"))) defer gfile.Chdir(pwd) - t.Assert(Instance().Get("my-config"), 1) + t.Assert(Instance().MustGet(ctx, "my-config"), 1) - instances.Clear() + localInstances.Clear() t.AssertNil(genv.Set("GF_GCFG_FILE", "config.json")) defer genv.Set("GF_GCFG_FILE", "") - t.Assert(Instance().Get("my-config"), 2) + t.Assert(Instance().MustGet(ctx, "my-config"), 2) }) } @@ -122,9 +90,9 @@ func Test_Instance_EnvPath(t *testing.T) { genv.Set("GF_GCFG_PATH", gdebug.TestDataPath("envpath")) defer genv.Set("GF_GCFG_PATH", "") t.Assert(Instance("c3") != nil, true) - t.Assert(Instance("c3").Get("my-config"), "3") - t.Assert(Instance("c4").Get("my-config"), "4") - instances = gmap.NewStrAnyMap(true) + t.Assert(Instance("c3").MustGet(ctx, "my-config"), "3") + t.Assert(Instance("c4").MustGet(ctx, "my-config"), "4") + localInstances = gmap.NewStrAnyMap(true) }) } @@ -134,7 +102,7 @@ func Test_Instance_EnvFile(t *testing.T) { defer genv.Set("GF_GCFG_PATH", "") genv.Set("GF_GCFG_FILE", "c6.json") defer genv.Set("GF_GCFG_FILE", "") - t.Assert(Instance().Get("my-config"), "6") - instances = gmap.NewStrAnyMap(true) + t.Assert(Instance().MustGet(ctx, "my-config"), "6") + localInstances = gmap.NewStrAnyMap(true) }) } diff --git a/os/gsession/gsession.go b/os/gsession/gsession.go index ee1d15fc9..07b0661c5 100644 --- a/os/gsession/gsession.go +++ b/os/gsession/gsession.go @@ -14,6 +14,7 @@ import ( ) var ( + // ErrorDisabled is used for marking certain interface function not used. ErrorDisabled = gerror.NewOption(gerror.Option{ Text: "this feature is disabled in this storage", Code: gcode.CodeNotSupported, diff --git a/util/grand/grand.go b/util/grand/grand.go index 63a71618d..77478cee4 100644 --- a/util/grand/grand.go +++ b/util/grand/grand.go @@ -10,7 +10,6 @@ package grand import ( "encoding/binary" "time" - "unsafe" ) var ( @@ -20,7 +19,7 @@ var ( characters = letters + digits + symbols // 94 ) -// Intn returns a int number which is between 0 and max: [0, max). +// Intn returns an int number which is between 0 and max: [0, max). // // Note that: // 1. The `max` can only be greater than 0, or else it returns `max` directly; @@ -95,7 +94,7 @@ func S(n int, symbols ...bool) string { b[i] = characters[numberBytes[i]%62] } } - return *(*string)(unsafe.Pointer(&b)) + return string(b) } // D returns a random time.Duration between min and max: [min, max]. @@ -147,7 +146,7 @@ func Digits(n int) string { for i := range b { b[i] = digits[numberBytes[i]%10] } - return *(*string)(unsafe.Pointer(&b)) + return string(b) } // Letters returns a random string which contains only letters, and its length is `n`. @@ -162,7 +161,7 @@ func Letters(n int) string { for i := range b { b[i] = letters[numberBytes[i]%52] } - return *(*string)(unsafe.Pointer(&b)) + return string(b) } // Symbols returns a random string which contains only symbols, and its length is `n`. @@ -177,7 +176,7 @@ func Symbols(n int) string { for i := range b { b[i] = symbols[numberBytes[i]%32] } - return *(*string)(unsafe.Pointer(&b)) + return string(b) } // Perm returns, as a slice of n int numbers, a pseudo-random permutation of the integers [0,n).