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 }