1
\$\begingroup\$

Below is a piece of code that is responsible for serializing files. ZL_LIST and PERS_LIST are XML file structs, I have not shown them here to save space.

Please check my code.

package parser

import (
    "encoding/xml"
    "fmt"
    "golang.org/x/text/encoding/charmap"
    "io"
    "io/ioutil"
    "os"
    "path/filepath"
    "sync"
)

type Files struct {
   Name          string
   PathZipFile   string
   PathUnzipFile string
}

type nameMap map[string]bool

func (ZL_LIST) Parse(FilesName []*Files) {
    unzipPath, err := filepath.Abs("tmp")

    files, err := ioutil.ReadDir(unzipPath)
    f := make([]*Files, 0)
    for _, file := range files {
        fileZip := new(Files)
        if !file.IsDir() && file.Name()[:1] != "." {
            fileZip.Name = file.Name()
            fileZip.PathUnzipFile = filepath.Join(unzipPath, file.Name())
            f = append(f, fileZip)
        }
    }

    if err != nil {
        fmt.Printf("[ERROR] Error retrieving files %v", err)
    }

    names := make(nameMap, 1)
    var wg sync.WaitGroup
    var countHmLmFiles int = -1
    var countHmFiles int = 0
    var countLmFiles int = 0
    var file *Files
    for countHmLmFiles, file = range f {
        wg.Add(1)
        go func(filename *Files) {
            parseHmLm(filename, &countHmFiles, &countLmFiles, &names)
            wg.Done()
        }(file)
    }
    wg.Wait()

    fmt.Printf("[INFO] Read %d files from them  HM files %d and LM files %d ", countHmLmFiles+1, countHmFiles, countLmFiles)
}

func parseHmLm(filename *Files, countHmFiles *int, countLmFiles *int, names *nameMap) {
    fmt.Println("[INFO] считывается файл ", filename.Name, " с заголовком ", filename.Name[:2])
    xmlFile, err := os.Open(filepath.Join(filename.PathUnzipFile))
    if err != nil {
        fmt.Printf("[ERROR] Cannot open file %e \n", err)
    }

    defer xmlFile.Close()

    switch filename.Name[:2] {
    case "HM":
        (*names)[filename.Name] = true
        (*countHmFiles) += 1
        var hFile ZL_LIST
        decoder := xml.NewDecoder(xmlFile)
        decoder.CharsetReader =  charset
        err := decoder.Decode(&hFile)
        if err != nil {
            fmt.Errorf("[ERROR] Cannot decode file %e", err)
        }
    case "LM":
        (*countLmFiles) += 1
        var lFile PERS_LIST
        decoder := xml.NewDecoder(xmlFile)
        decoder.CharsetReader = charset
        err := decoder.Decode(&lFile)
        if err != nil {
            fmt.Errorf("[ERROR] Cannot decode file %e", err)
        }
    }

}

func charset(charset string, input io.Reader) (io.Reader, error) {
    switch charset {
    case "windows-1251":
        return charmap.Windows1251.NewDecoder().Reader(input), nil
    default:
        return nil, fmt.Errorf("unknown charset: %s", charset)
    }
}

Calling function

package api
    import (
    "net/http"
    "github.com/go-chi/render"
    "../../store/parser"
    "fmt"
    "path/filepath"
)
    func (s *Rest) parse(w http.ResponseWriter, r *http.Request) {
        uploadPath, err := filepath.Abs("./upload/")
        if err != nil {
            fmt.Printf("[ERROR] can not find the specified path %e", err)
        }
        unzipPath, err := filepath.Abs("./tmp")
        if err != nil {
            fmt.Printf("[ERROR] can not find the specified path %e", err)
        }
        p := parser.Files{}
        filesName := p.GetFileName(uploadPath, unzipPath)
            p.UnzipFiles(filesName, unzipPath)
        ...
        ...
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Variables

Go has two ways of declaring new variables: a var notation and a short one. It's preferable to use the short one:

wg := sync.WaitGroup{}
countHmLmFiles := -1
countHmFiles := 0
countLmFiles := 0

Also when declaring lots of global variables in the package block you may use block notation:

var (
    a int = 1
    b string = "foo"
)

You can declare variables directly in the for loop:

for countHmLmFiles, file := range f {
    // countHmLmFiles and file will be visible only here
}

This way their scope will be limited to for body.

Maps in Go

You've created a map with make:

names := make(nameMap, 1)

I don't see a point in creating a map for a single element. Also when you don't need the size argument in make you may simply write names := nameMap{}.

No need pass maps with pointer. Maps and slices are already a reference type and may be passed directly by value. It won't issue a full copy.

Error handling

You've missed lots of error checks. Ideally every error must be handled in place. Otherwise use _ to ignore it and make a comment to be specific on this as it looks like a broken code.

The switch lacks the default case. It worth adding it to catch other possible values of filename.Name[:2].

Also don't forget to add terminating \n to fmt.Printf to separate log messages.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.