1// Copyright (c) Microsoft Corporation. All rights reserved. 2// Licensed under the MIT License. See License.txt in the project root for license information. 3 4package model 5 6import ( 7 "fmt" 8 "strings" 9 10 "github.com/Azure/azure-sdk-for-go/tools/internal/delta" 11 "github.com/Azure/azure-sdk-for-go/tools/internal/markdown" 12 "github.com/Azure/azure-sdk-for-go/tools/internal/report" 13) 14 15// Changelog describes the changelog generated for a package. 16type Changelog struct { 17 // NewPackage is true if this package does not exist in the old version 18 NewPackage bool 19 // RemovedPackage is true if this package does not exist in the new version 20 RemovedPackage bool 21 // Modified contains the details of a modified package. This is nil when either NewPackage or RemovedPackage is true 22 Modified *report.Package 23} 24 25// HasBreakingChanges returns if this report of changelog contains breaking changes 26func (c Changelog) HasBreakingChanges() bool { 27 return c.RemovedPackage || (c.Modified != nil && c.Modified.HasBreakingChanges()) 28} 29 30// String ... 31func (c Changelog) String() string { 32 return c.ToMarkdown() 33} 34 35// ToMarkdown returns the markdown string of this changelog 36func (c Changelog) ToMarkdown() string { 37 if c.NewPackage { 38 return "" 39 } 40 if c.RemovedPackage { 41 return "This package was removed" // this should never be executed 42 } 43 return c.Modified.ToMarkdown() 44} 45 46// ToCompactMarkdown returns the markdown string of this changelog but more compact 47func (c Changelog) ToCompactMarkdown() string { 48 if c.NewPackage { 49 return "This is a new package" 50 } 51 if c.RemovedPackage { 52 return "This package was removed" 53 } 54 return writeChangelogForPackage(c.Modified) 55} 56 57// GetBreakingChangeItems returns an array of the breaking change items 58func (c Changelog) GetBreakingChangeItems() []string { 59 if c.RemovedPackage { 60 return []string{ 61 fmt.Sprintf("Package was removed"), 62 } 63 } 64 if c.Modified == nil { 65 return []string{} 66 } 67 return getBreakingChanges(c.Modified.BreakingChanges) 68} 69 70func writeChangelogForPackage(r *report.Package) string { 71 if r == nil || r.IsEmpty() { 72 return "No exported changes" 73 } 74 75 md := &markdown.Writer{} 76 77 // write breaking changes 78 md.WriteHeader("Breaking Changes") 79 for _, item := range getBreakingChanges(r.BreakingChanges) { 80 md.WriteListItem(item) 81 } 82 83 // write additional changes 84 md.WriteHeader("New Content") 85 for _, item := range getNewContents(r.AdditiveChanges) { 86 md.WriteListItem(item) 87 } 88 89 md.EmptyLine() 90 summaries := getSummaries(r.BreakingChanges, r.AdditiveChanges) 91 md.WriteLine(summaries) 92 93 return md.String() 94} 95 96func getSummaries(breaking *report.BreakingChanges, additive *delta.Content) string { 97 bc := 0 98 if breaking != nil { 99 bc = breaking.Count() 100 } 101 ac := 0 102 if additive != nil { 103 ac = additive.Count() 104 } 105 106 return fmt.Sprintf("Total %d breaking change(s), %d additive change(s).", bc, ac) 107} 108 109func getNewContents(c *delta.Content) []string { 110 if c == nil || c.IsEmpty() { 111 return nil 112 } 113 114 var items []string 115 116 if len(c.Consts) > 0 { 117 for k := range c.Consts { 118 line := fmt.Sprintf("New const `%s`", k) 119 items = append(items, line) 120 } 121 } 122 if len(c.Funcs) > 0 { 123 for k, v := range c.Funcs { 124 params := "" 125 if v.Params != nil { 126 params = *v.Params 127 } 128 returns := "" 129 if v.Returns != nil { 130 returns = *v.Returns 131 if strings.Contains(returns, ",") { 132 returns = fmt.Sprintf("(%s)", returns) 133 } 134 } 135 line := fmt.Sprintf("New function `%s(%s) %s`", k, params, returns) 136 items = append(items, line) 137 } 138 } 139 if len(c.CompleteStructs) > 0 { 140 for _, v := range c.CompleteStructs { 141 line := fmt.Sprintf("New struct `%s`", v) 142 items = append(items, line) 143 } 144 } 145 if len(c.Structs) > 0 { 146 modified := c.GetModifiedStructs() 147 for s, f := range modified { 148 for _, af := range f.AnonymousFields { 149 line := fmt.Sprintf("New anonymous field `%s` in struct `%s`", af, s) 150 items = append(items, line) 151 } 152 for f := range f.Fields { 153 line := fmt.Sprintf("New field `%s` in struct `%s`", f, s) 154 items = append(items, line) 155 } 156 } 157 } 158 159 return items 160} 161 162func getBreakingChanges(b *report.BreakingChanges) []string { 163 items := make([]string, 0) 164 if b == nil || b.IsEmpty() { 165 return items 166 } 167 168 // get signature changes 169 items = append(items, getSignatureChangeItems(b)...) 170 171 // get removed content 172 items = append(items, getRemovedContent(b.Removed)...) 173 174 return items 175} 176 177func getSignatureChangeItems(b *report.BreakingChanges) []string { 178 if b.IsEmpty() { 179 return nil 180 } 181 182 var items []string 183 184 // write const changes 185 if len(b.Consts) > 0 { 186 for k, v := range b.Consts { 187 line := fmt.Sprintf("Const `%s` type has been changed from `%s` to `%s`", k, v.From, v.To) 188 items = append(items, line) 189 } 190 // TODO -- sort? 191 } 192 // write function changes 193 if len(b.Funcs) > 0 { 194 for k, v := range b.Funcs { 195 if v.Params != nil { 196 line := fmt.Sprintf("Function `%s` parameter(s) have been changed from `(%s)` to `(%s)`", k, v.Params.From, v.Params.To) 197 items = append(items, line) 198 } 199 if v.Returns != nil { 200 line := fmt.Sprintf("Function `%s` return value(s) have been changed from `(%s)` to `(%s)`", k, v.Returns.From, v.Returns.To) 201 items = append(items, line) 202 } 203 } 204 } 205 // write struct changes 206 if len(b.Structs) > 0 { 207 for k, v := range b.Structs { 208 for f, d := range v.Fields { 209 line := fmt.Sprintf("Type of `%s.%s` has been changed from `%s` to `%s`", k, f, d.From, d.To) 210 items = append(items, line) 211 } 212 } 213 } 214 // interfaces are skipped, which are identical to some of the functions 215 216 return items 217} 218 219func getRemovedContent(removed *delta.Content) []string { 220 if removed == nil { 221 return nil 222 } 223 224 var items []string 225 // write constants 226 if len(removed.Consts) > 0 { 227 for k := range removed.Consts { 228 line := fmt.Sprintf("Const `%s` has been removed", k) 229 items = append(items, line) 230 } 231 } 232 // write functions 233 if len(removed.Funcs) > 0 { 234 for k := range removed.Funcs { 235 line := fmt.Sprintf("Function `%s` has been removed", k) 236 items = append(items, line) 237 } 238 } 239 // write complete struct removal 240 if len(removed.CompleteStructs) > 0 { 241 for _, v := range removed.CompleteStructs { 242 line := fmt.Sprintf("Struct `%s` has been removed", v) 243 items = append(items, line) 244 } 245 } 246 // write struct modification (some fields are removed) 247 modified := removed.GetModifiedStructs() 248 if len(modified) > 0 { 249 for s, f := range modified { 250 for _, af := range f.AnonymousFields { 251 line := fmt.Sprintf("Field `%s` of struct `%s` has been removed", af, s) 252 items = append(items, line) 253 } 254 for f := range f.Fields { 255 line := fmt.Sprintf("Field `%s` of struct `%s` has been removed", f, s) 256 items = append(items, line) 257 } 258 } 259 } 260 261 return items 262} 263