You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
staros/sysconf/sysconf.go

853 lines
21 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package sysconf
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"reflect"
"strconv"
"strings"
"sync"
)
type SysConf struct {
Data []*SysSegment
segmap map[string]int64
segId int64
HaveSegMent bool //是否有节这个概念
SegStart string
SegEnd string
CommentFlag []string //评论标识符,如#
EqualFlag string //赋值标识符,如=
ValueFlag string //值标识符,如"
EscapeFlag string //转义字符
CommentCR bool //评论是否能与value同一行true不行false可以
SpaceStr string //美化符号
lock sync.RWMutex
}
type SysSegment struct {
Name string
//nodeMap
Comment string
NodeData []*SysNode
nodeId int64
nodeMap map[string]int64
lock sync.RWMutex
}
type SysNode struct {
Key string
Value []string
Comment string
NoValue bool
lock sync.RWMutex
}
func NewIni() *SysConf {
ini := NewSysConf("=")
ini.CommentCR = true
ini.CommentFlag = []string{"#", ";"}
ini.HaveSegMent = true
ini.SegStart = "["
ini.SegEnd = "]"
ini.SpaceStr = " "
ini.EscapeFlag = "\\"
return ini
}
func NewSysConf(EqualFlag string) *SysConf {
syscnf := new(SysConf)
syscnf.EqualFlag = EqualFlag
return syscnf
}
// NewLinuxConf sysctl.conf like file
func NewLinuxConf(EqualFlag string) *SysConf {
syscnf := new(SysConf)
syscnf.EqualFlag = EqualFlag
syscnf.HaveSegMent = false
syscnf.CommentCR = true
syscnf.CommentFlag = []string{"#"}
return syscnf
}
func (syscfg *SysConf) ParseFromFile(filepath string) error {
data, err := ioutil.ReadFile(filepath)
if err != nil {
return err
}
return syscfg.Parse(data)
}
// Parse 生成INI文件结构
func (syscfg *SysConf) Parse(data []byte) error {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if syscfg.HaveSegMent && (syscfg.SegStart == "" || syscfg.SegEnd == "") {
return errors.New("SegMent Start or End Flag Not Allowed!")
}
if !syscfg.CommentCR {
return syscfg.parseNOCRComment(data)
}
return syscfg.parseCRComment(data)
}
func (syscfg *SysConf) parseNOCRComment(data []byte) error { //允许comment在同一行
data = bytes.TrimSpace(data)
dataLists := bytes.Split(data, []byte("\n"))
seg := new(SysSegment)
seg.nodeMap = make(map[string]int64)
syscfg.segmap = make(map[string]int64)
if syscfg.HaveSegMent {
seg.Name = "unnamed"
}
syscfg.segId = 0
var node *SysNode
for _, v1 := range dataLists {
var (
isSegStart bool = false
isEscape bool = false
isEqual bool = false
isComment bool = false
tsuMo string = ""
)
cowStr := strings.TrimSpace(string(v1))
for i := 0; i < len(cowStr); i++ {
runeStr := cowStr[i : i+1] //当前字符rune扫描
if runeStr == syscfg.EscapeFlag && (!isEscape) {
isEscape = true
continue
}
if runeStr == syscfg.SegStart && (!isEscape) {
isSegStart = true
continue
}
if runeStr == syscfg.SegEnd && (!isEscape) {
isSegStart = false
//New segment start from here
if seg.Name == "unnamed" && len(seg.NodeData) == 0 {
seg.Name = tsuMo
tsuMo = ""
continue
}
syscfg.segmap[seg.Name] = syscfg.segId
syscfg.segId++
syscfg.Data = append(syscfg.Data, seg)
seg = new(SysSegment)
seg.nodeMap = make(map[string]int64)
seg.Name = tsuMo
tsuMo = ""
continue
}
if isSegStart {
tsuMo += runeStr
if isEscape {
isEscape = false
}
continue
}
if syscfg.EqualFlag == runeStr && (!isEscape) && (!isEqual) {
key := strings.TrimSpace(tsuMo)
if val, ok := seg.nodeMap[key]; ok {
node = seg.NodeData[val]
} else {
node = new(SysNode)
node.Key = key
seg.nodeMap[node.Key] = seg.nodeId
seg.nodeId++
seg.NodeData = append(seg.NodeData, node)
}
tsuMo = ""
isEqual = true
if syscfg.ValueFlag != "" {
nokoriStr := strings.TrimSpace(cowStr[i+1:])
isFound := false
isValue := false
for k4, v4 := range nokoriStr {
if string([]rune{v4}) == syscfg.ValueFlag {
isValue = !isValue
}
if SliceIn(syscfg.CommentFlag, string([]rune{v4})) && !isValue {
val := nokoriStr[:k4]
isFound = true
startFinder := strings.Index(val, syscfg.ValueFlag)
endFinder := strings.LastIndex(val, syscfg.ValueFlag)
if !((startFinder == -1 || endFinder == -1) || (endFinder-startFinder <= 0)) {
node.Value = append(node.Value, strings.TrimSpace(val[startFinder+1:endFinder]))
}
node.Comment = nokoriStr[k4+1:] + "\n"
}
}
if !isFound {
startFinder := strings.Index(nokoriStr, syscfg.ValueFlag)
endFinder := strings.LastIndex(nokoriStr, syscfg.ValueFlag)
if (startFinder == -1 || endFinder == -1) || (endFinder-startFinder <= 0) {
break
}
node.Value = append(node.Value, strings.TrimSpace(nokoriStr[startFinder+1:endFinder]))
}
break
}
continue
}
if SliceIn(syscfg.CommentFlag, runeStr) && (!isEscape) {
isComment = true
if seg.nodeId == 0 {
seg.Comment += strings.TrimSpace(cowStr[i+1:]) + "\n"
break
}
if tsuMo != "" {
node.Value = append(node.Value, strings.TrimSpace(tsuMo))
tsuMo = ""
}
node.Comment += strings.TrimSpace(cowStr[i+1:]) + "\n"
break
}
isEscape = false
tsuMo += runeStr
}
if isEqual && tsuMo != "" {
node.Value = append(node.Value, strings.TrimSpace(tsuMo))
}
if !isEqual && tsuMo != "" && !isComment {
node = new(SysNode)
node.Key = tsuMo
seg.nodeMap[node.Key] = seg.nodeId
seg.nodeId++
seg.NodeData = append(seg.NodeData, node)
node.NoValue = true
}
}
if seg != nil {
syscfg.segmap[seg.Name] = syscfg.segId
syscfg.segId++
syscfg.Data = append(syscfg.Data, seg)
}
return nil
}
func (syscfg *SysConf) parseCRComment(data []byte) error { //不允许comment在同一行
data = bytes.TrimSpace(data)
dataLists := bytes.Split(data, []byte("\n"))
seg := new(SysSegment)
seg.nodeMap = make(map[string]int64)
syscfg.segmap = make(map[string]int64)
if syscfg.HaveSegMent {
seg.Name = "unnamed"
}
syscfg.segId = 0
var node *SysNode
for _, v1 := range dataLists {
var (
isSegStart bool = false
isEscape bool = false
isEqual bool = false
isComment bool = false
tsuMo string = ""
)
cowStr := strings.TrimSpace(string(v1))
for i := 0; i < len(cowStr); i++ {
runeStr := cowStr[i : i+1] //当前字符rune扫描
if runeStr == syscfg.EscapeFlag && (!isEscape) {
isEscape = true
continue
}
if runeStr == syscfg.SegStart && (!isEscape) {
isSegStart = true
continue
}
if runeStr == syscfg.SegEnd && (!isEscape) {
isSegStart = false
//New segment start from here
if seg.Name == "unnamed" && len(seg.NodeData) == 0 {
seg.Name = tsuMo
tsuMo = ""
break
}
syscfg.segmap[seg.Name] = syscfg.segId
syscfg.segId++
syscfg.Data = append(syscfg.Data, seg)
seg = new(SysSegment)
seg.nodeMap = make(map[string]int64)
seg.Name = tsuMo
tsuMo = ""
break
}
if isSegStart {
tsuMo += runeStr
if isEscape {
isEscape = false
}
continue
}
if syscfg.EqualFlag == runeStr && (!isEscape) && (!isEqual) {
key := strings.TrimSpace(tsuMo)
if val, ok := seg.nodeMap[key]; ok {
node = seg.NodeData[val]
} else {
node = new(SysNode)
node.Key = key
seg.nodeMap[node.Key] = seg.nodeId
seg.nodeId++
seg.NodeData = append(seg.NodeData, node)
}
tsuMo = ""
isEqual = true
if syscfg.ValueFlag == "" {
node.Value = append(node.Value, TrimEscape(strings.TrimSpace(cowStr[i+1:]), syscfg.EscapeFlag))
} else {
nokoriStr := strings.TrimSpace(cowStr[i+1:])
startFinder := strings.Index(nokoriStr, syscfg.ValueFlag)
endFinder := strings.LastIndex(nokoriStr, syscfg.ValueFlag)
if (startFinder == -1 || endFinder == -1) || (endFinder-startFinder <= 0) {
break
}
node.Value = append(node.Value, strings.TrimSpace(nokoriStr[startFinder+1:endFinder]))
}
break
}
if SliceIn(syscfg.CommentFlag, runeStr) && (!isEscape) {
isComment = true
tsuMo = ""
if seg.nodeId == 0 {
seg.Comment += strings.TrimSpace(cowStr[i+1:]) + "\n"
break
}
node.Comment += strings.TrimSpace(cowStr[i+1:]) + "\n"
break
}
isEscape = false
tsuMo += runeStr
}
if !isEqual && tsuMo != "" && !isComment {
node = new(SysNode)
node.Key = tsuMo
seg.nodeMap[node.Key] = seg.nodeId
seg.nodeId++
seg.NodeData = append(seg.NodeData, node)
node.NoValue = true
}
}
if seg != nil {
syscfg.segmap[seg.Name] = syscfg.segId
syscfg.segId++
syscfg.Data = append(syscfg.Data, seg)
}
return nil
}
func (syscfg *SysConf) Build() []byte {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
var outPut string
for _, v := range syscfg.Data {
if v == nil {
continue
}
if syscfg.HaveSegMent {
outPut += syscfg.SegStart + v.Name + syscfg.SegEnd + "\n"
}
if v.Comment != "" {
v.Comment = v.Comment[:len(v.Comment)-1]
comment := strings.Split(v.Comment, "\n")
for _, vc := range comment {
if vc != "" {
outPut += syscfg.CommentFlag[0] + vc + "\n"
} else {
outPut += "\n"
}
}
}
for _, v2 := range v.NodeData {
if v2 == nil {
continue
}
if v2.NoValue {
outPut += v2.Key + "\n"
} else {
for _, v3 := range v2.Value {
if syscfg.ValueFlag != "" {
outPut += v2.Key + syscfg.SpaceStr + syscfg.EqualFlag + syscfg.SpaceStr + syscfg.ValueFlag + v3 + syscfg.ValueFlag + "\n"
} else {
outPut += v2.Key + syscfg.SpaceStr + syscfg.EqualFlag + syscfg.SpaceStr + syscfg.addEscape(v3) + "\n"
}
}
if len(v2.Value) == 0 {
outPut += v2.Key + syscfg.SpaceStr + syscfg.EqualFlag + "\n"
}
if v2.Comment != "" {
v2.Comment = v2.Comment[:len(v2.Comment)-1]
comment := strings.Split(v2.Comment, "\n")
for _, vc := range comment {
if vc != "" {
outPut += syscfg.CommentFlag[0] + vc + "\n"
} else {
outPut += "\n"
}
}
}
}
}
}
return []byte(outPut)
}
func (syscfg *SysConf) addEscape(str string) string {
str = strings.ReplaceAll(str, syscfg.EscapeFlag, syscfg.EscapeFlag+syscfg.EscapeFlag)
str = strings.ReplaceAll(str, syscfg.EqualFlag, syscfg.EscapeFlag+syscfg.EqualFlag)
str = strings.ReplaceAll(str, syscfg.SegStart, syscfg.EscapeFlag+syscfg.SegStart)
str = strings.ReplaceAll(str, syscfg.SegEnd, syscfg.EscapeFlag+syscfg.SegEnd)
for _, v := range syscfg.CommentFlag {
str = strings.ReplaceAll(str, v, syscfg.EscapeFlag+v)
}
return str
}
func (syscfg *SysConf) Reverse() {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
for _, v := range syscfg.Data {
if v == nil {
continue
}
var (
NodeData []*SysNode
nodeId int64
nodeMap map[string]int64
)
nodeMap = make(map[string]int64)
for _, v2 := range v.NodeData {
if v2 == nil {
continue
}
for _, v3 := range v2.Value {
var node *SysNode
if val, ok := nodeMap[v3]; ok {
node = NodeData[val]
} else {
node = new(SysNode)
node.Key = v3
NodeData = append(NodeData, node)
nodeMap[v3] = nodeId
nodeId++
}
node.Value = append(node.Value, strings.TrimSpace(v2.Key))
node.Comment += v2.Comment
}
v.NodeData = NodeData
v.nodeId = nodeId
v.nodeMap = nodeMap
}
}
}
func TrimEscape(text, escape string) string {
var isEscape bool = false
var outPut []rune
if escape == "" {
return text
}
text = strings.TrimSpace(text)
for _, v := range text {
if v == []rune(escape)[0] && !isEscape {
isEscape = true
continue
}
outPut = append(outPut, v)
}
return string(outPut)
}
func SliceIn(slice interface{}, data interface{}) bool {
typed := reflect.ValueOf(slice)
if typed.Kind() == reflect.Slice || typed.Kind() == reflect.Array {
for i := 0; i < typed.Len(); i++ {
if typed.Index(i).Interface() == data {
return true
}
}
}
return false
}
// Unmarshal 输出结果到结构体中
func (cfg *SysConf) Unmarshal(ins interface{}) error {
var structSet func(t reflect.Type, v reflect.Value,oriSeg string) error
t := reflect.TypeOf(ins)
v := reflect.ValueOf(ins).Elem()
if v.Kind() != reflect.Struct {
return errors.New("Not a Struct")
}
if t.Kind() != reflect.Ptr || !v.CanSet() {
return errors.New("Cannot Write!")
}
t = t.Elem()
structSet = func(t reflect.Type, v reflect.Value,oriSeg string) error {
for i := 0; i < t.NumField(); i++ {
tp := t.Field(i)
vl := v.Field(i)
if !vl.CanSet() {
continue
}
if vl.Type().Kind() == reflect.Struct {
structSet(vl.Type(), vl,tp.Tag.Get("seg"))
continue
}
seg := tp.Tag.Get("seg")
key := tp.Tag.Get("key")
if oriSeg!="" {
seg=oriSeg
}
if seg == "" || key == "" {
continue
}
if _, ok := cfg.segmap[seg]; !ok {
continue
}
segs := cfg.Data[cfg.segmap[seg]]
if segs.Get(key) == "" {
continue
}
switch vl.Kind() {
case reflect.String:
vl.SetString(segs.Get(key))
case reflect.Int, reflect.Int32, reflect.Int64:
vl.SetInt(segs.Int64(key))
case reflect.Float32, reflect.Float64:
vl.SetFloat(segs.Float64(key))
case reflect.Bool:
vl.SetBool(segs.Bool(key))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
vl.SetUint(uint64(segs.Int64(key)))
default:
continue
}
}
return nil
}
return structSet(t, v,"")
}
// Marshal 输出结果到结构体中
func (cfg *SysConf) Marshal(ins interface{}) ([]byte, error) {
var structSet func(t reflect.Type, v reflect.Value,oriSeg string)
t := reflect.TypeOf(ins)
v := reflect.ValueOf(ins)
if v.Kind() != reflect.Struct {
return nil, errors.New("Not a Struct")
}
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
structSet = func(t reflect.Type, v reflect.Value,oriSeg string) {
for i := 0; i < t.NumField(); i++ {
var seg, key, comment string = "", "", ""
tp := t.Field(i)
vl := v.Field(i)
if vl.Type().Kind() == reflect.Struct {
structSet(vl.Type(), vl,tp.Tag.Get("seg"))
continue
}
seg = tp.Tag.Get("seg")
key = tp.Tag.Get("key")
comment = tp.Tag.Get("comment")
if oriSeg != "" {
seg=oriSeg
}
if seg == "" || key == "" {
continue
}
if _, ok := cfg.segmap[seg]; !ok {
cfg.AddSeg(seg)
}
cfg.Seg(seg).Set(key, fmt.Sprint(vl), comment)
}
}
structSet(t, v,"")
return cfg.Build(), nil
}
func (syscfg *SysConf) Seg(name string) *SysSegment {
if _, ok := syscfg.segmap[name]; !ok {
return nil
}
seg := syscfg.Data[syscfg.segmap[name]]
return seg
}
func (syscfg *SysConf) AddSeg(name string) *SysSegment {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if _, ok := syscfg.segmap[name]; !ok {
newseg := new(SysSegment)
newseg.Name = name
newseg.nodeMap = make(map[string]int64)
syscfg.Data = append(syscfg.Data, newseg)
syscfg.segId++
if syscfg.segmap == nil {
syscfg.segId = 0
syscfg.segmap = make(map[string]int64)
}
syscfg.segmap[newseg.Name] = syscfg.segId
return newseg
}
seg := syscfg.Data[syscfg.segmap[name]]
return seg
}
func (syscfg *SysConf) DeleteSeg(name string) error {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if _, ok := syscfg.segmap[name]; !ok {
return errors.New("Seg Not Exists")
}
syscfg.Data[syscfg.segmap[name]] = nil
delete(syscfg.segmap, name)
return nil
}
func (syscfg *SysSegment) GetComment(key string) string {
if v, ok := syscfg.nodeMap[key]; !ok {
return ""
} else {
return syscfg.NodeData[v].Comment
}
}
func (syscfg *SysSegment) SetComment(key, comment string) error {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if v, ok := syscfg.nodeMap[key]; !ok {
return errors.New("Key Not Exists")
} else {
syscfg.NodeData[v].Comment = comment
return nil
}
}
func (syscfg *SysSegment) Exist(key string) bool {
if _, ok := syscfg.nodeMap[key]; !ok {
return false
} else {
return true
}
}
func (syscfg *SysSegment) Get(key string) string {
if v, ok := syscfg.nodeMap[key]; !ok {
return ""
} else {
if len(syscfg.NodeData[v].Value) >= 1 {
return syscfg.NodeData[v].Value[0]
}
}
return ""
}
func (syscfg *SysSegment) GetAll(key string) []string {
if v, ok := syscfg.nodeMap[key]; !ok {
return []string{}
} else {
return syscfg.NodeData[v].Value
}
}
func (syscfg *SysSegment) Int(key string) int {
val := syscfg.Get(key)
if val == "" {
return 0
}
res, _ := strconv.Atoi(val)
return res
}
func (syscfg *SysSegment) Int64(key string) int64 {
val := syscfg.Get(key)
if val == "" {
return 0
}
res, _ := strconv.ParseInt(val, 10, 64)
return res
}
func (syscfg *SysSegment) Int32(key string) int32 {
val := syscfg.Get(key)
if val == "" {
return 0
}
res, _ := strconv.ParseInt(val, 10, 32)
return int32(res)
}
func (syscfg *SysSegment) Float64(key string) float64 {
val := syscfg.Get(key)
if val == "" {
return 0
}
res, _ := strconv.ParseFloat(val, 64)
return res
}
func (syscfg *SysSegment) Float32(key string) float32 {
val := syscfg.Get(key)
if val == "" {
return 0
}
res, _ := strconv.ParseFloat(val, 32)
return float32(res)
}
func (syscfg *SysSegment) Bool(key string) bool {
val := syscfg.Get(key)
if val == "" {
return false
}
res, _ := strconv.ParseBool(val)
return res
}
func (syscfg *SysSegment) SetBool(key string, value bool, comment string) error {
res := strconv.FormatBool(value)
return syscfg.Set(key, res, comment)
}
func (syscfg *SysSegment) SetFloat64(key string, prec int, value float64, comment string) error {
res := strconv.FormatFloat(value, 'f', prec, 64)
return syscfg.Set(key, res, comment)
}
func (syscfg *SysSegment) SetFloat32(key string, prec int, value float32, comment string) error {
res := strconv.FormatFloat(float64(value), 'f', prec, 32)
return syscfg.Set(key, res, comment)
}
func (syscfg *SysSegment) SetUint64(key string, value uint64, comment string) error {
res := strconv.FormatUint(value, 10)
return syscfg.Set(key, res, comment)
}
func (syscfg *SysSegment) SetInt64(key string, value int64, comment string) error {
res := strconv.FormatInt(value, 10)
return syscfg.Set(key, res, comment)
}
func (syscfg *SysSegment) SetInt32(key string, value int32, comment string) error {
res := strconv.FormatInt(int64(value), 10)
return syscfg.Set(key, res, comment)
}
func (syscfg *SysSegment) SetInt(key string, value int, comment string) error {
res := strconv.Itoa(value)
return syscfg.Set(key, res, comment)
}
func (syscfg *SysSegment) Set(key, value, comment string) error {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if v, ok := syscfg.nodeMap[key]; !ok {
node := new(SysNode)
node.Key = key
node.Value = append(node.Value, value)
node.Comment = comment
syscfg.NodeData = append(syscfg.NodeData, node)
syscfg.nodeMap[key] = syscfg.nodeId
syscfg.nodeId++
return nil
} else {
syscfg.NodeData[v].Value = []string{value}
if comment != "" {
syscfg.NodeData[v].Comment = comment
}
}
return nil
}
func (syscfg *SysSegment) SetAll(key string, value []string, comment string) error {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if v, ok := syscfg.nodeMap[key]; !ok {
node := new(SysNode)
node.Key = key
node.Value = value
node.Comment = comment
syscfg.NodeData = append(syscfg.NodeData, node)
syscfg.nodeMap[key] = syscfg.nodeId
syscfg.nodeId++
return nil
} else {
syscfg.NodeData[v].Value = value
if comment != "" {
syscfg.NodeData[v].Comment = comment
}
}
return nil
}
func (syscfg *SysSegment) AddValue(key, value, comment string) error {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if v, ok := syscfg.nodeMap[key]; !ok {
node := new(SysNode)
node.Key = key
node.Value = append(node.Value, value)
node.Comment = comment
syscfg.NodeData = append(syscfg.NodeData, node)
syscfg.nodeMap[key] = syscfg.nodeId
syscfg.nodeId++
return nil
} else {
syscfg.NodeData[v].Value = append(syscfg.NodeData[v].Value, value)
if comment != "" {
syscfg.NodeData[v].Comment = comment
}
}
return nil
}
func (syscfg *SysSegment) Delete(key string) error {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if v, ok := syscfg.nodeMap[key]; !ok {
return errors.New("Key not exists!")
} else {
if syscfg.NodeData[v].Comment != "" {
cmtSet := false
for j := v - 1; j >= 0; j-- {
if syscfg.NodeData[j] != nil {
syscfg.NodeData[j].Comment += syscfg.NodeData[v].Comment
cmtSet = true
break
}
}
if !cmtSet {
syscfg.Comment += syscfg.NodeData[v].Comment
}
}
syscfg.NodeData[v] = nil
delete(syscfg.nodeMap, key)
}
return nil
}
func (syscfg *SysSegment) DeleteValue(key string, Value string) error {
syscfg.lock.Lock()
defer syscfg.lock.Unlock()
if v, ok := syscfg.nodeMap[key]; !ok {
return errors.New("Key not exists!")
} else {
data := syscfg.NodeData[v].Value
var vals []string
for _, v := range data {
if v != Value {
vals = append(vals, v)
}
}
syscfg.NodeData[v].Value = vals
}
return nil
}