github.com/octohelm/cuemod@v0.9.4/pkg/cuemod/modfile/modfile.go (about) 1 package modfile 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "sort" 8 "strconv" 9 10 "cuelang.org/go/cue/ast" 11 "cuelang.org/go/cue/format" 12 ) 13 14 const ModFilename = "cue.mod/module.cue" 15 16 type ModVersion struct { 17 Version string 18 VcsRef string 19 } 20 21 func (mv ModVersion) Exactly() bool { 22 return mv.Version != "" && mv.VcsRef == "" 23 } 24 25 type Requirement struct { 26 ModVersion 27 Indirect bool 28 } 29 30 type ReplaceTarget struct { 31 VersionedPathIdentity 32 Import string 33 } 34 35 type ModFile struct { 36 // Module name 37 Module string 38 39 // Replace 40 // version limit 41 Replace map[VersionedPathIdentity]ReplaceTarget 42 // Require same as go root 43 // require { module: version } 44 // indirect require { module:: version } 45 Require map[string]Requirement 46 47 comments map[string][]*ast.CommentGroup 48 } 49 50 func (m *ModFile) String() string { 51 return string(m.Bytes()) 52 } 53 54 func (m *ModFile) Bytes() []byte { 55 buf := bytes.NewBuffer(nil) 56 57 _, _ = fmt.Fprintf(buf, "module: %s\n", strconv.Quote(m.Module)) 58 59 if len(m.Require) > 0 { 60 modules := make([]string, 0) 61 62 for module := range m.Require { 63 modules = append(modules, module) 64 } 65 66 sort.Strings(modules) 67 68 m.writeRequires(buf, modules, func(r *Requirement) bool { 69 return !r.Indirect 70 }) 71 72 m.writeRequires(buf, modules, func(r *Requirement) bool { 73 return r.Indirect 74 }) 75 } 76 77 if len(m.Replace) > 0 { 78 replacements := make([]VersionedPathIdentity, 0) 79 80 for r := range m.Replace { 81 replacements = append(replacements, r) 82 } 83 84 sort.Slice(replacements, func(i, j int) bool { 85 return replacements[i].Path < replacements[j].Path 86 }) 87 88 m.writeReplaces(buf, replacements, func(r *ReplaceTarget) bool { 89 return r.Import == "" 90 }) 91 92 m.writeReplaces(buf, replacements, func(r *ReplaceTarget) bool { 93 return r.Import != "" 94 }) 95 } 96 97 data, err := format.Source(buf.Bytes()) 98 if err != nil { 99 panic(err) 100 } 101 102 return data 103 } 104 105 func (m *ModFile) writeReplaces(w io.Writer, replacements []VersionedPathIdentity, filter func(replaceTarget *ReplaceTarget) bool) { 106 fields := make([]interface{}, 0) 107 108 for _, replaceFrom := range replacements { 109 replaceTarget := m.Replace[replaceFrom] 110 111 if !filter(&replaceTarget) { 112 continue 113 } 114 115 i := replaceFrom.String() 116 117 f := &ast.Field{Label: ast.NewString(i)} 118 119 if replaceTarget.Path == replaceFrom.Path { 120 f.Value = ast.NewString((&VersionedPathIdentity{ModVersion: replaceTarget.ModVersion}).String()) 121 } else { 122 f.Value = ast.NewString(replaceTarget.String()) 123 } 124 125 if cg, ok := m.comments["replace://"+i]; ok { 126 for i := range cg { 127 f.AddComment(cg[i]) 128 } 129 } 130 131 if replaceTarget.Import != "" { 132 f.Attrs = append(f.Attrs, &ast.Attribute{Text: attr("import", replaceTarget.Import)}) 133 } 134 135 fields = append(fields, f) 136 } 137 138 if len(fields) == 0 { 139 return 140 } 141 142 data, _ := format.Node(ast.NewStruct(fields...)) 143 144 _, _ = fmt.Fprintf(w, ` 145 replace: %s 146 `, string(data)) 147 } 148 149 func (m *ModFile) writeRequires(w io.Writer, modules []string, filter func(r *Requirement) bool) { 150 fields := make([]interface{}, 0) 151 152 // direct require 153 for _, module := range modules { 154 r := m.Require[module] 155 156 if !filter(&r) { 157 continue 158 } 159 160 f := &ast.Field{Label: ast.NewString(module)} 161 f.Value = ast.NewString(r.Version) 162 163 if r.Indirect { 164 f.Attrs = append(f.Attrs, &ast.Attribute{Text: attr("indirect")}) 165 } 166 167 if cg, ok := m.comments["require://"+module]; ok { 168 for i := range cg { 169 f.AddComment(cg[i]) 170 } 171 } 172 173 fields = append(fields, f) 174 } 175 176 if len(fields) > 0 { 177 data, _ := format.Node(ast.NewStruct(fields...)) 178 _, _ = fmt.Fprintf(w, ` 179 require: %s 180 `, string(data)) 181 } 182 } 183 184 func attr(key string, values ...string) string { 185 buf := bytes.NewBufferString("@") 186 buf.WriteString(key) 187 buf.WriteString("(") 188 189 for i := range values { 190 if i > 0 { 191 buf.WriteString(",") 192 } 193 194 buf.WriteString(strconv.Quote(values[i])) 195 } 196 197 buf.WriteString(")") 198 199 return buf.String() 200 }