美文网首页beego
beego config模块源码分析笔记三

beego config模块源码分析笔记三

作者: ljh123 | 来源:发表于2018-12-27 07:40 被阅读0次

ini.go源码文件分析

这个文件里的代码是实现了解析ini配置文件,为开发者提供了两个结构体。
ini知识
1)type IniConfig struct
2)type IniConfigContainer struct

一、type IniConfig struct
IniConfig实现Config来解析ini文件。

type IniConfig struct {
}

对开发者不可见内容
1)func (ini IniConfig) parseFile(name string) (IniConfigContainer, error)

func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
    data, err := ioutil.ReadFile(name)
    if err != nil {
        return nil, err
    }

    return ini.parseData(filepath.Dir(name), data)
}

2)func (ini IniConfig) parseData(dir string, data []byte) (IniConfigContainer, error)
协程安全

func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, error) {
    cfg := &IniConfigContainer{
        data:           make(map[string]map[string]string),
        sectionComment: make(map[string]string),
        keyComment:     make(map[string]string),
        RWMutex:        sync.RWMutex{},
    }
    cfg.Lock()
    defer cfg.Unlock()

    var comment bytes.Buffer
    buf := bufio.NewReader(bytes.NewBuffer(data))
    // check the BOM
    head, err := buf.Peek(3)
        // 239=∩,187=╗,191=┐
    if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 {
        for i := 1; i <= 3; i++ {
            buf.ReadByte()
        }
    }
    section := defaultSection
    tmpBuf := bytes.NewBuffer(nil)
    for {
        tmpBuf.Reset()

        shouldBreak := false
        for {
            tmp, isPrefix, err := buf.ReadLine()
            if err == io.EOF {
                shouldBreak = true
                break
            }

            //It might be a good idea to throw a error on all unknonw errors?
            if _, ok := err.(*os.PathError); ok {
                return nil, err
            }

            tmpBuf.Write(tmp)
            if isPrefix {
                continue
            }

            if !isPrefix {
                break
            }
        }
        if shouldBreak {
            break
        }

        line := tmpBuf.Bytes()
        line = bytes.TrimSpace(line)
        if bytes.Equal(line, bEmpty) {
            continue
        }
        var bComment []byte
        switch {
        case bytes.HasPrefix(line, bNumComment):
            bComment = bNumComment
        case bytes.HasPrefix(line, bSemComment):
            bComment = bSemComment
        }
        if bComment != nil {
            line = bytes.TrimLeft(line, string(bComment))
            // Need append to a new line if multi-line comments.
            if comment.Len() > 0 {
                comment.WriteByte('\n')
            }
            comment.Write(line)
            continue
        }

        if bytes.HasPrefix(line, sectionStart) && bytes.HasSuffix(line, sectionEnd) {
            section = strings.ToLower(string(line[1 : len(line)-1])) // section name case insensitive
            if comment.Len() > 0 {
                cfg.sectionComment[section] = comment.String()
                comment.Reset()
            }
            if _, ok := cfg.data[section]; !ok {
                cfg.data[section] = make(map[string]string)
            }
            continue
        }

        if _, ok := cfg.data[section]; !ok {
            cfg.data[section] = make(map[string]string)
        }
        keyValue := bytes.SplitN(line, bEqual, 2)

        key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
        key = strings.ToLower(key)

        // handle include "other.conf"
        if len(keyValue) == 1 && strings.HasPrefix(key, "include") {

            includefiles := strings.Fields(key)
            if includefiles[0] == "include" && len(includefiles) == 2 {

                otherfile := strings.Trim(includefiles[1], "\"")
                if !filepath.IsAbs(otherfile) {
                    otherfile = filepath.Join(dir, otherfile)
                }

                i, err := ini.parseFile(otherfile)
                if err != nil {
                    return nil, err
                }

                for sec, dt := range i.data {
                    if _, ok := cfg.data[sec]; !ok {
                        cfg.data[sec] = make(map[string]string)
                    }
                    for k, v := range dt {
                        cfg.data[sec][k] = v
                    }
                }

                for sec, comm := range i.sectionComment {
                    cfg.sectionComment[sec] = comm
                }

                for k, comm := range i.keyComment {
                    cfg.keyComment[k] = comm
                }

                continue
            }
        }

        if len(keyValue) != 2 {
            return nil, errors.New("read the content error: \"" + string(line) + "\", should key = val")
        }
        val := bytes.TrimSpace(keyValue[1])
        if bytes.HasPrefix(val, bDQuote) {
            val = bytes.Trim(val, `"`)
        }

        cfg.data[section][key] = ExpandValueEnv(string(val))
        if comment.Len() > 0 {
            cfg.keyComment[section+"."+key] = comment.String()
            comment.Reset()
        }

    }
    return cfg, nil
}

对开发者可见内容
1)
二、type IniConfigContainer struct

相关文章

网友评论

    本文标题:beego config模块源码分析笔记三

    本文链接:https://www.haomeiwen.com/subject/cwvklqtx.html