github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/cmd/generates/sources/workfiles.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package sources
    19  
    20  import (
    21  	"fmt"
    22  	"github.com/aacfactory/errors"
    23  	"github.com/aacfactory/fns/cmd/generates/files"
    24  	"golang.org/x/mod/modfile"
    25  	"os"
    26  	"path/filepath"
    27  	"sync"
    28  )
    29  
    30  type Work struct {
    31  	Dir      string
    32  	Filename string
    33  	Uses     []*Module
    34  	Replaces []*Module
    35  	parsed   bool
    36  }
    37  
    38  func (work *Work) Use(path string) (v *Module, used bool) {
    39  	for _, use := range work.Uses {
    40  		if use.Path == path {
    41  			v = use
    42  			used = true
    43  			break
    44  		}
    45  	}
    46  	return
    47  }
    48  
    49  func (work *Work) Parse() (err error) {
    50  	if work.parsed {
    51  		return
    52  	}
    53  	path := work.Filename
    54  	if !filepath.IsAbs(path) {
    55  		absolute, absoluteErr := filepath.Abs(path)
    56  		if absoluteErr != nil {
    57  			err = errors.Warning("sources: parse work failed").
    58  				WithCause(errors.Warning("sources: get absolute representation of work file failed").WithCause(absoluteErr).WithMeta("work", path))
    59  			return
    60  		}
    61  		path = absolute
    62  	}
    63  	if !files.ExistFile(path) {
    64  		err = errors.Warning("sources: parse work failed").
    65  			WithCause(errors.Warning("sources: file was not found").WithMeta("work", path))
    66  		return
    67  	}
    68  	dir := filepath.Dir(path)
    69  	path = filepath.ToSlash(path)
    70  	data, readErr := os.ReadFile(path)
    71  	if readErr != nil {
    72  		err = errors.Warning("sources: parse work failed").WithMeta("work", path).WithCause(readErr)
    73  		return
    74  	}
    75  	file, parseErr := modfile.ParseWork(path, data, nil)
    76  	if parseErr != nil {
    77  		err = errors.Warning("sources: parse work failed").WithMeta("work", path).WithCause(parseErr)
    78  		return
    79  	}
    80  	work.Filename = path
    81  	work.Uses = make([]*Module, 0, 1)
    82  	work.Replaces = make([]*Module, 0, 1)
    83  	if file.Use != nil && len(file.Use) > 0 {
    84  		for _, use := range file.Use {
    85  			usePath := use.Path
    86  			if filepath.IsAbs(usePath) {
    87  				usePath = filepath.ToSlash(usePath)
    88  			} else {
    89  				usePath = filepath.ToSlash(filepath.Join(dir, usePath))
    90  			}
    91  			moduleFile := filepath.ToSlash(filepath.Join(usePath, "go.mod"))
    92  			if !files.ExistFile(moduleFile) {
    93  				err = errors.Warning("sources: parse work failed").WithMeta("work", path).
    94  					WithCause(errors.Warning("sources: mod file was not found").
    95  						WithMeta("mod", moduleFile))
    96  				return
    97  			}
    98  			modData, readModErr := os.ReadFile(moduleFile)
    99  			if readModErr != nil {
   100  				err = errors.Warning("sources: parse work failed").WithMeta("work", path).
   101  					WithCause(errors.Warning("sources: read mod file failed").WithCause(readModErr).WithMeta("mod", moduleFile))
   102  				return
   103  			}
   104  			mf, parseModErr := modfile.Parse(moduleFile, modData, nil)
   105  			if parseModErr != nil {
   106  				err = errors.Warning("sources: parse work failed").WithMeta("work", path).
   107  					WithCause(errors.Warning("sources: parse mod file failed").WithCause(parseModErr).WithMeta("mod", moduleFile))
   108  				return
   109  			}
   110  			mod := &Module{
   111  				Dir:          usePath,
   112  				Path:         mf.Module.Mod.Path,
   113  				Version:      "",
   114  				Requires:     nil,
   115  				Work:         work,
   116  				Replace:      nil,
   117  				locker:       &sync.Mutex{},
   118  				parsed:       false,
   119  				sources:      nil,
   120  				builtinTypes: nil,
   121  				types:        nil,
   122  			}
   123  			registerBuiltinTypes(mod)
   124  			work.Uses = append(work.Uses, mod)
   125  		}
   126  	}
   127  	if file.Replace != nil && len(file.Replace) > 0 {
   128  		for _, replace := range file.Replace {
   129  			replaceDir := ""
   130  			if replace.New.Version != "" {
   131  				replaceDir = filepath.Join(PKG(), fmt.Sprintf("%s@%s", replace.New.Path, replace.New.Version))
   132  			} else {
   133  				if filepath.IsAbs(replace.New.Path) {
   134  					replaceDir = replace.New.Path
   135  				} else {
   136  					replaceDir = filepath.Join(work.Dir, replace.New.Path)
   137  				}
   138  			}
   139  			replaceDir = filepath.ToSlash(replaceDir)
   140  			if !files.ExistFile(replaceDir) {
   141  				err = errors.Warning("sources: parse work failed").WithMeta("work", path).
   142  					WithCause(errors.Warning("sources: replace dir was not found").WithMeta("replace", replaceDir))
   143  				return
   144  			}
   145  			moduleFile := filepath.ToSlash(filepath.Join(replaceDir, "go.mod"))
   146  			if !files.ExistFile(moduleFile) {
   147  				err = errors.Warning("sources: parse work failed").WithMeta("work", path).
   148  					WithCause(errors.Warning("sources: replace mod file was not found").
   149  						WithMeta("mod", moduleFile))
   150  				return
   151  			}
   152  			modData, readModErr := os.ReadFile(moduleFile)
   153  			if readModErr != nil {
   154  				err = errors.Warning("sources: parse work failed").WithMeta("work", path).
   155  					WithCause(errors.Warning("sources: read replace mod file failed").WithCause(readModErr).WithMeta("mod", moduleFile))
   156  				return
   157  			}
   158  			mf, parseModErr := modfile.Parse(moduleFile, modData, nil)
   159  			if parseModErr != nil {
   160  				err = errors.Warning("sources: parse work failed").WithMeta("work", path).
   161  					WithCause(errors.Warning("sources: parse replace mod file failed").WithCause(parseModErr).WithMeta("mod", moduleFile))
   162  				return
   163  			}
   164  			work.Replaces = append(work.Replaces, &Module{
   165  				Dir:      "",
   166  				Path:     replace.Old.Path,
   167  				Version:  replace.Old.Version,
   168  				Requires: nil,
   169  				Work:     nil,
   170  				Replace: &Module{
   171  					Dir:      replaceDir,
   172  					Path:     mf.Module.Mod.Path,
   173  					Version:  mf.Module.Mod.Version,
   174  					Requires: nil,
   175  					Work:     nil,
   176  					Replace:  nil,
   177  					locker:   &sync.Mutex{},
   178  					parsed:   false,
   179  					types:    nil,
   180  				},
   181  				locker:       &sync.Mutex{},
   182  				parsed:       false,
   183  				sources:      nil,
   184  				builtinTypes: map[string]*Type{},
   185  				types:        nil,
   186  			})
   187  		}
   188  	}
   189  	work.parsed = true
   190  	return
   191  }