github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xconstant/zones/generate_test.go (about)

     1  package zones
     2  
     3  import (
     4  	"archive/zip"
     5  	"bytes"
     6  	"fmt"
     7  	"github.com/Aoi-hosizora/ahlib/xstring"
     8  	"github.com/Aoi-hosizora/ahlib/xtime"
     9  	"go/format"
    10  	"io"
    11  	"io/ioutil"
    12  	"math"
    13  	"os"
    14  	"path/filepath"
    15  	"runtime"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  )
    20  
    21  func TestGenerate(t *testing.T) {
    22  	_ = generate
    23  	// err := generate()
    24  	// if err != nil {
    25  	//	log.Fatalln(err)
    26  	// }
    27  }
    28  
    29  func generate() error {
    30  	nameList, setList, err := getList()
    31  	if err != nil {
    32  		return fmt.Errorf("getList: %w", err)
    33  	}
    34  
    35  	// 1. code
    36  	sb := bytes.Buffer{}
    37  	sb.WriteString(`// Code generated by generate_test. DO NOT EDIT.
    38  
    39  package zones
    40  
    41  // Time zones are referred from "${runtime.GOROOT()}/lib/time/zoneinfo.zip".
    42  
    43  const (
    44  ` + strings.Join(setList, "\n") + `
    45  )`)
    46  	err = formatAndWrite(sb.Bytes(), "_generate/zones.go")
    47  	if err != nil {
    48  		return fmt.Errorf("code formatAndWrite: %w", err)
    49  	}
    50  
    51  	// 2. test code
    52  	sb.Reset()
    53  	sb.WriteString(`// Code generated by generate_test. DO NOT EDIT.
    54  
    55  package zones
    56  
    57  import (
    58  	"github.com/Aoi-hosizora/ahlib/xtesting"
    59  	"testing"
    60  	"time"
    61  )
    62  
    63  func TestZones(t *testing.T) {
    64  	for _, zone := range []string{
    65  ` + strings.Join(nameList, ",\n") + `,
    66  	} {
    67  		_, err := time.LoadLocation(zone)
    68  		xtesting.Nil(t, err)
    69  	}
    70  }`)
    71  	err = formatAndWrite(sb.Bytes(), "_generate/zones_test.go")
    72  	if err != nil {
    73  		return fmt.Errorf("test code formatAndWrite: %w", err)
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  func formatAndWrite(bs []byte, filename string) error {
    80  	bs, err := format.Source(bs)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	err = os.MkdirAll(filepath.Dir(filename), 0644)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	defer f.Close()
    93  	_, err = f.Write(bs)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	return nil
    98  }
    99  
   100  func unzip(srcFile string, dstDir string) error {
   101  	archive, err := zip.OpenReader(srcFile)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	defer archive.Close()
   106  
   107  	for _, f := range archive.File {
   108  		path := filepath.Join(dstDir, f.Name)
   109  		if f.FileInfo().IsDir() {
   110  			if err := os.MkdirAll(path, 0644); err != nil {
   111  				return err
   112  			}
   113  			continue
   114  		}
   115  		if err := os.MkdirAll(filepath.Dir(path), 0644); err != nil {
   116  			return err
   117  		}
   118  		fileInArchive, err := f.Open()
   119  		if err != nil {
   120  			return err
   121  		}
   122  		dstFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
   123  		if err != nil {
   124  			return err
   125  		}
   126  
   127  		if _, err := io.Copy(dstFile, fileInArchive); err != nil {
   128  			return err
   129  		}
   130  		_ = dstFile.Close()
   131  		_ = fileInArchive.Close()
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  func getList() (varNameList []string, varSetList []string, err error) {
   138  	zoneSource := filepath.Join(runtime.GOROOT(), "lib/time/zoneinfo.zip")
   139  	tempDir := "./temp"
   140  	err = unzip(zoneSource, tempDir)
   141  	if err != nil {
   142  		return nil, nil, fmt.Errorf("unzip: %w", err)
   143  	}
   144  	defer os.RemoveAll(tempDir)
   145  
   146  	// read zoneinfo folder
   147  	result := make([]string, 0)
   148  	var readDir func(path string) error
   149  	readDir = func(path string) error {
   150  		files, err := ioutil.ReadDir(filepath.Join(tempDir, path))
   151  		if err != nil {
   152  			return err
   153  		}
   154  		for _, entry := range files {
   155  			name := entry.Name()
   156  			if name != strings.ToUpper(name[:1])+name[1:] {
   157  				continue
   158  			}
   159  			if entry.IsDir() {
   160  				err = readDir(path + "/" + name)
   161  				if err != nil {
   162  					return err
   163  				}
   164  			} else {
   165  				result = append(result, (path + "/" + name)[1:])
   166  			}
   167  		}
   168  		return nil
   169  	}
   170  	err = readDir("")
   171  	if err != nil {
   172  		return nil, nil, fmt.Errorf("readDir: %w", err)
   173  	}
   174  
   175  	// generate final list
   176  	varNameList = make([]string, 0, len(result)+2)
   177  	varSetList = make([]string, 0, len(result)+2)
   178  	varNameList = append(varNameList, "UTC", "Local")
   179  	varSetList = append(varSetList, `UTC = "UTC" // time.UTC`, `Local = "Local" // time.Local`)
   180  	for _, r := range result {
   181  		loc, err := time.LoadLocation(r)
   182  		if err != nil {
   183  			return nil, nil, fmt.Errorf("LoadLocation: %w", err)
   184  		}
   185  		words := xstring.SplitToWords(loc.String(), "/")
   186  		for i, w := range words {
   187  			if strings.HasPrefix(w, "Etc") || strings.HasPrefix(w, "GMT") {
   188  				// skip
   189  			} else {
   190  				w = xstring.PascalCase(w)
   191  			}
   192  			words[i] = w
   193  		}
   194  		varName := strings.Join(words, "_")
   195  
   196  		du := xtime.LocationDuration(loc)
   197  		flag := "+"
   198  		if du < 0 {
   199  			flag = "-"
   200  		}
   201  		comment := fmt.Sprintf("%s%02d:%02d", flag, int(math.Abs(du.Hours())), xtime.DurationMinuteComponent(du))
   202  
   203  		if varName == "UTC" {
   204  			continue
   205  		} else if strings.HasPrefix(varName, "Etc") || strings.HasPrefix(varName, "GMT") {
   206  			varName = strings.Replace(varName, "+", "_P", 1)
   207  			varName = strings.Replace(varName, "-", "_M", 1)
   208  		}
   209  		varNameList = append(varNameList, varName)
   210  		varSetList = append(varSetList, fmt.Sprintf("%s = \"%s\" // %s", varName, loc.String(), comment))
   211  	}
   212  	return varNameList, varSetList, nil
   213  }