kcl-lang.io/kpm@v0.8.7-0.20240520061008-9fc4c5efc8c7/pkg/package/toml.go (about)

     1  // Copyright 2022 The KCL Authors. All rights reserved.
     2  //
     3  // Because the same dependency package will be serialized
     4  // into toml files in different formats in kcl.mod and kcl.mod.lock,
     5  // the toml library 'github.com/BurntSushi/toml' is encapsulated in this file,
     6  // and two different format are provided according to different files.
     7  //
     8  // In kcl.mod, the dependency toml looks like:
     9  //
    10  // <dependency_name> = { git = "<git_url>", tag = "<git_tag>" }
    11  //
    12  // In kcl.mod.lock, the dependency toml looks like:
    13  //
    14  // [dependencies.<dependency_name>]
    15  // name = "<dependency_name>"
    16  // full_name = "<dependency_fullname>"
    17  // version = "<dependency_version>"
    18  // sum = "yNADGqn3jclWtfpwvWMHBsgkAKzOaMWg/VYxfcOJs64="
    19  // url = "https://github.com/xxxx"
    20  // tag = "<dependency_tag>"
    21  package pkg
    22  
    23  import (
    24  	"bytes"
    25  	"fmt"
    26  	"strings"
    27  
    28  	"github.com/BurntSushi/toml"
    29  
    30  	"kcl-lang.io/kpm/pkg/constants"
    31  	"kcl-lang.io/kpm/pkg/reporter"
    32  )
    33  
    34  const NEWLINE = "\n"
    35  
    36  func (mod *ModFile) MarshalTOML() string {
    37  	var sb strings.Builder
    38  	sb.WriteString(mod.Pkg.MarshalTOML())
    39  	sb.WriteString(mod.Dependencies.MarshalTOML())
    40  	sb.WriteString(mod.Profiles.MarshalTOML())
    41  	return sb.String()
    42  }
    43  
    44  const PACKAGE_PATTERN = "[package]"
    45  
    46  func (pkg *Package) MarshalTOML() string {
    47  	var sb strings.Builder
    48  	sb.WriteString(PACKAGE_PATTERN)
    49  	sb.WriteString(NEWLINE)
    50  	var buf bytes.Buffer
    51  	if err := toml.NewEncoder(&buf).Encode(pkg); err != nil {
    52  		fmt.Println(err)
    53  		return ""
    54  	}
    55  	sb.WriteString(buf.String())
    56  	sb.WriteString(NEWLINE)
    57  	return sb.String()
    58  }
    59  
    60  const DEPS_PATTERN = "[dependencies]"
    61  
    62  func (dep *Dependencies) MarshalTOML() string {
    63  	var sb strings.Builder
    64  	if len(dep.Deps) != 0 {
    65  		sb.WriteString(DEPS_PATTERN)
    66  		for _, dep := range dep.Deps {
    67  			sb.WriteString(NEWLINE)
    68  			sb.WriteString(dep.MarshalTOML())
    69  		}
    70  		sb.WriteString(NEWLINE)
    71  	}
    72  	return sb.String()
    73  }
    74  
    75  const DEP_PATTERN = "%s = %s"
    76  
    77  func (dep *Dependency) MarshalTOML() string {
    78  	source := dep.Source.MarshalTOML()
    79  	var sb strings.Builder
    80  	if len(source) != 0 {
    81  		sb.WriteString(fmt.Sprintf(DEP_PATTERN, dep.Name, source))
    82  	}
    83  	return sb.String()
    84  }
    85  
    86  const SOURCE_PATTERN = "{ %s }"
    87  
    88  func (source *Source) MarshalTOML() string {
    89  	var sb strings.Builder
    90  	if source.Git != nil {
    91  		gitToml := source.Git.MarshalTOML()
    92  		if len(gitToml) != 0 {
    93  			sb.WriteString(fmt.Sprintf(SOURCE_PATTERN, gitToml))
    94  		}
    95  	}
    96  
    97  	if source.Oci != nil {
    98  		ociToml := source.Oci.MarshalTOML()
    99  		if len(ociToml) != 0 {
   100  			if len(source.Oci.Reg) != 0 && len(source.Oci.Repo) != 0 {
   101  				sb.WriteString(fmt.Sprintf(SOURCE_PATTERN, ociToml))
   102  			} else {
   103  				sb.WriteString(ociToml)
   104  			}
   105  		}
   106  	}
   107  
   108  	if source.Local != nil {
   109  		localPathToml := source.Local.MarshalTOML()
   110  		if len(localPathToml) != 0 {
   111  			sb.WriteString(fmt.Sprintf(SOURCE_PATTERN, localPathToml))
   112  		}
   113  	}
   114  
   115  	return sb.String()
   116  }
   117  
   118  const GIT_URL_PATTERN = "git = \"%s\""
   119  const TAG_PATTERN = "tag = \"%s\""
   120  const GIT_COMMIT_PATTERN = "commit = \"%s\""
   121  const VERSION_PATTERN = "version = \"%s\""
   122  const SEPARATOR = ", "
   123  
   124  func (git *Git) MarshalTOML() string {
   125  	var sb strings.Builder
   126  	if len(git.Url) != 0 {
   127  		sb.WriteString(fmt.Sprintf(GIT_URL_PATTERN, git.Url))
   128  	}
   129  	if len(git.Tag) != 0 {
   130  		sb.WriteString(SEPARATOR)
   131  		sb.WriteString(fmt.Sprintf(TAG_PATTERN, git.Tag))
   132  	}
   133  	if len(git.Commit) != 0 {
   134  		sb.WriteString(SEPARATOR)
   135  		sb.WriteString(fmt.Sprintf(GIT_COMMIT_PATTERN, git.Commit))
   136  	}
   137  	if len(git.Version) != 0 {
   138  		sb.WriteString(SEPARATOR)
   139  		sb.WriteString(fmt.Sprintf(VERSION_PATTERN, git.Version))
   140  	}
   141  	return sb.String()
   142  }
   143  
   144  const OCI_URL_PATTERN = "oci = \"%s\""
   145  
   146  func (oci *Oci) MarshalTOML() string {
   147  	var sb strings.Builder
   148  	if len(oci.Reg) != 0 && len(oci.Repo) != 0 {
   149  		sb.WriteString(fmt.Sprintf(OCI_URL_PATTERN, oci.IntoOciUrl()))
   150  		if len(oci.Tag) != 0 {
   151  			sb.WriteString(SEPARATOR)
   152  			sb.WriteString(fmt.Sprintf(TAG_PATTERN, oci.Tag))
   153  		}
   154  	} else if len(oci.Reg) == 0 && len(oci.Repo) == 0 && len(oci.Tag) != 0 {
   155  		sb.WriteString(fmt.Sprintf(`"%s"`, oci.Tag))
   156  	}
   157  
   158  	return sb.String()
   159  }
   160  
   161  const LOCAL_PATH_PATTERN = "path = %s"
   162  
   163  func (local *Local) MarshalTOML() string {
   164  	var sb strings.Builder
   165  	if len(local.Path) != 0 {
   166  		sb.WriteString(fmt.Sprintf(LOCAL_PATH_PATTERN, fmt.Sprintf("%q", local.Path)))
   167  	}
   168  	return sb.String()
   169  }
   170  
   171  const PROFILE_PATTERN = "[profile]"
   172  
   173  func (p *Profile) MarshalTOML() string {
   174  	var sb strings.Builder
   175  	if p != nil {
   176  		sb.WriteString(PROFILE_PATTERN)
   177  		sb.WriteString(NEWLINE)
   178  		var buf bytes.Buffer
   179  		if err := toml.NewEncoder(&buf).Encode(p); err != nil {
   180  			fmt.Println(err)
   181  			return ""
   182  		}
   183  		sb.WriteString(buf.String())
   184  		sb.WriteString(NEWLINE)
   185  	}
   186  	return sb.String()
   187  }
   188  
   189  const PACKAGE_FLAG = "package"
   190  const DEPS_FLAG = "dependencies"
   191  const PROFILES_FLAG = "profile"
   192  
   193  func (mod *ModFile) UnmarshalTOML(data interface{}) error {
   194  	meta, ok := data.(map[string]interface{})
   195  	if !ok {
   196  		return fmt.Errorf("expected map[string]interface{}, got %T", data)
   197  	}
   198  
   199  	if v, ok := meta[PACKAGE_FLAG]; ok {
   200  		pkg := Package{}
   201  		err := pkg.UnmarshalTOML(v)
   202  		if err != nil {
   203  			return err
   204  		}
   205  		mod.Pkg = pkg
   206  	}
   207  
   208  	if v, ok := meta[DEPS_FLAG]; ok {
   209  		deps := Dependencies{
   210  			Deps: make(map[string]Dependency),
   211  		}
   212  		err := deps.UnmarshalModTOML(v)
   213  		if err != nil {
   214  			return err
   215  		}
   216  		mod.Dependencies = deps
   217  	}
   218  
   219  	if v, ok := meta[PROFILES_FLAG]; ok {
   220  		p := NewProfile()
   221  		var buf bytes.Buffer
   222  		if err := toml.NewEncoder(&buf).Encode(v); err != nil {
   223  			return err
   224  		}
   225  		err := toml.Unmarshal(buf.Bytes(), &p)
   226  
   227  		if err != nil {
   228  			return err
   229  		}
   230  		mod.Profiles = &p
   231  	}
   232  	return nil
   233  }
   234  
   235  const NAME_FLAG = "name"
   236  const EDITION_FLAG = "edition"
   237  const VERSION_FLAG = "version"
   238  const DESCRIPTION_FLAG = "description"
   239  const INCLUDE_FLAG = "include"
   240  const EXCLUDE_FLAG = "exclude"
   241  
   242  func (pkg *Package) UnmarshalTOML(data interface{}) error {
   243  	meta, ok := data.(map[string]interface{})
   244  	if !ok {
   245  		return fmt.Errorf("expected map[string]interface{}, got %T", data)
   246  	}
   247  
   248  	if v, ok := meta[NAME_FLAG].(string); ok {
   249  		pkg.Name = v
   250  	}
   251  
   252  	if v, ok := meta[EDITION_FLAG].(string); ok {
   253  		pkg.Edition = v
   254  	}
   255  
   256  	if v, ok := meta[VERSION_FLAG].(string); ok {
   257  		pkg.Version = v
   258  	}
   259  
   260  	if v, ok := meta[DESCRIPTION_FLAG].(string); ok {
   261  		pkg.Description = v
   262  	}
   263  
   264  	convertToStringArray := func(v interface{}) []string {
   265  		var arr []string
   266  		for _, item := range v.([]interface{}) {
   267  			arr = append(arr, item.(string))
   268  		}
   269  		return arr
   270  	}
   271  
   272  	if v, ok := meta[INCLUDE_FLAG].([]interface{}); ok {
   273  		pkg.Include = convertToStringArray(v)
   274  	}
   275  
   276  	if v, ok := meta[EXCLUDE_FLAG].([]interface{}); ok {
   277  		pkg.Exclude = convertToStringArray(v)
   278  	}
   279  
   280  	return nil
   281  }
   282  
   283  func (deps *Dependencies) UnmarshalModTOML(data interface{}) error {
   284  	meta, ok := data.(map[string]interface{})
   285  	if !ok {
   286  		return fmt.Errorf("expected map[string]interface{}, got %T", data)
   287  	}
   288  
   289  	for k, v := range meta {
   290  		dep := Dependency{}
   291  		dep.Name = k
   292  
   293  		err := dep.UnmarshalModTOML(v)
   294  		if err != nil {
   295  			return err
   296  		}
   297  		deps.Deps[k] = dep
   298  	}
   299  
   300  	return nil
   301  }
   302  
   303  func (dep *Dependency) UnmarshalModTOML(data interface{}) error {
   304  	source := Source{}
   305  	err := source.UnmarshalModTOML(data)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	dep.Source = source
   311  	var version string
   312  	if source.Git != nil {
   313  		version, err = source.Git.GetValidGitReference()
   314  		if err != nil {
   315  			return err
   316  		}
   317  	}
   318  	if source.Oci != nil {
   319  		version = source.Oci.Tag
   320  	}
   321  
   322  	dep.FullName = fmt.Sprintf(PKG_NAME_PATTERN, dep.Name, version)
   323  	dep.Version = version
   324  	return nil
   325  }
   326  
   327  func (source *Source) UnmarshalModTOML(data interface{}) error {
   328  	meta, ok := data.(map[string]interface{})
   329  	if ok {
   330  		if _, ok := meta[LOCAL_PATH_FLAG].(string); ok {
   331  			localPath := Local{}
   332  			err := localPath.UnmarshalModTOML(data)
   333  			if err != nil {
   334  				return err
   335  			}
   336  			source.Local = &localPath
   337  		} else if _, ok := meta["git"]; ok {
   338  			git := Git{}
   339  			err := git.UnmarshalModTOML(data)
   340  			if err != nil {
   341  				return err
   342  			}
   343  			source.Git = &git
   344  		} else {
   345  			oci := Oci{}
   346  			err := oci.UnmarshalModTOML(data)
   347  			if err != nil {
   348  				return err
   349  			}
   350  			source.Oci = &oci
   351  		}
   352  	}
   353  
   354  	_, ok = data.(string)
   355  	if ok {
   356  		oci := Oci{}
   357  		err := oci.UnmarshalModTOML(data)
   358  		if err != nil {
   359  			return err
   360  		}
   361  		source.Oci = &oci
   362  	}
   363  
   364  	return nil
   365  }
   366  
   367  const GIT_URL_FLAG = "git"
   368  const TAG_FLAG = "tag"
   369  const GIT_COMMIT_FLAG = "commit"
   370  
   371  func (git *Git) UnmarshalModTOML(data interface{}) error {
   372  	meta, ok := data.(map[string]interface{})
   373  	if !ok {
   374  		return fmt.Errorf("expected map[string]interface{}, got %T", data)
   375  	}
   376  
   377  	if v, ok := meta[GIT_URL_FLAG].(string); ok {
   378  		git.Url = v
   379  	}
   380  
   381  	if v, ok := meta[TAG_FLAG].(string); ok {
   382  		git.Tag = v
   383  	}
   384  
   385  	if v, ok := meta[GIT_COMMIT_FLAG].(string); ok {
   386  		git.Commit = v
   387  	}
   388  
   389  	return nil
   390  }
   391  
   392  func (oci *Oci) UnmarshalModTOML(data interface{}) error {
   393  	tag, ok := data.(string)
   394  	if ok {
   395  		oci.Tag = tag
   396  	} else if meta, ok := data.(map[string]interface{}); ok {
   397  		if v, ok := meta[constants.OciScheme].(string); ok {
   398  			_, err := oci.FromString(v)
   399  			if err != nil {
   400  				return err
   401  			}
   402  		}
   403  
   404  		if v, ok := meta[TAG_FLAG].(string); ok {
   405  			oci.Tag = v
   406  		}
   407  	} else {
   408  		return fmt.Errorf("unexpected data %T", data)
   409  	}
   410  
   411  	return nil
   412  }
   413  
   414  const LOCAL_PATH_FLAG = "path"
   415  
   416  func (local *Local) UnmarshalModTOML(data interface{}) error {
   417  	meta, ok := data.(map[string]interface{})
   418  	if !ok {
   419  		return fmt.Errorf("expected map[string]interface{}, got %T", data)
   420  	}
   421  
   422  	if v, ok := meta[LOCAL_PATH_FLAG].(string); ok {
   423  		local.Path = v
   424  	}
   425  
   426  	return nil
   427  }
   428  
   429  func (dep *Dependencies) MarshalLockTOML() (string, error) {
   430  	buf := new(bytes.Buffer)
   431  	if err := toml.NewEncoder(buf).Encode(dep); err != nil {
   432  		return "", reporter.NewErrorEvent(reporter.FailedLoadKclModLock, err, "failed to lock dependencies version")
   433  	}
   434  	return buf.String(), nil
   435  }
   436  
   437  func (dep *Dependencies) UnmarshalLockTOML(data string) error {
   438  	if _, err := toml.NewDecoder(strings.NewReader(data)).Decode(dep); err != nil {
   439  		return reporter.NewErrorEvent(reporter.FailedLoadKclModLock, err, "failed to load kcl.mod.lock")
   440  	}
   441  
   442  	return nil
   443  }