本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

301 lines
7.9KB

  1. package version
  2. import (
  3. "regexp"
  4. "strconv"
  5. "strings"
  6. "sync"
  7. )
  8. type ConstraintGroup struct {
  9. constraints []*Constraint
  10. }
  11. // Return a new NewConstrainGroup
  12. func NewConstrainGroup() *ConstraintGroup {
  13. group := new(ConstraintGroup)
  14. return group
  15. }
  16. // Return a new NewConstrainGroup and create the constraints based on a string
  17. //
  18. // Version constraints can be specified in a few different ways:
  19. //
  20. // Exact version: You can specify the exact version of a package, for
  21. // example 1.0.2.
  22. //
  23. // Range: By using comparison operators you can specify ranges of valid versions.
  24. // Valid operators are >, >=, <, <=, !=. An example range would be >=1.0. You can
  25. // define multiple ranges, separated by a comma: >=1.0,<2.0.
  26. //
  27. // Wildcard: You can specify a pattern with a * wildcard. 1.0.* is the equivalent
  28. // of >=1.0,<1.1.
  29. //
  30. // Next Significant Release (Tilde Operator): The ~ operator is best explained by
  31. // example: ~1.2 is equivalent to >=1.2,<2.0, while ~1.2.3 is equivalent to
  32. // >=1.2.3,<1.3. As you can see it is mostly useful for projects respecting
  33. // semantic versioning. A common usage would be to mark the minimum minor
  34. // version you depend on, like ~1.2 (which allows anything up to, but not
  35. // including, 2.0). Since in theory there should be no backwards compatibility
  36. // breaks until 2.0, that works well. Another way of looking at it is that
  37. // using ~ specifies a minimum version, but allows the last digit specified
  38. // to go up.
  39. //
  40. // By default only stable releases are taken into consideration. If you would like
  41. // to also get RC, beta, alpha or dev versions of your dependencies you can do so
  42. // using stability flags. To change that for all packages instead of doing per
  43. // dependency you can also use the minimum-stability setting.
  44. //
  45. // From: http://getcomposer.org/doc/01-basic-usage.md#package-versions
  46. func NewConstrainGroupFromString(name string) *ConstraintGroup {
  47. group := new(ConstraintGroup)
  48. group.fromString(name)
  49. return group
  50. }
  51. // Adds a Contraint to the group
  52. func (self *ConstraintGroup) AddConstraint(constraint ...*Constraint) {
  53. if self.constraints == nil {
  54. self.constraints = make([]*Constraint, 0)
  55. }
  56. self.constraints = append(self.constraints, constraint...)
  57. }
  58. // Return all the constraints
  59. func (self *ConstraintGroup) GetConstraints() []*Constraint {
  60. return self.constraints
  61. }
  62. // Match a given version againts the group
  63. //
  64. // Usage
  65. // c := version.NewConstrainGroupFromString(">2.0,<=3.0")
  66. // c.Match("2.5.0beta")
  67. // Returns: true
  68. //
  69. // c := version.NewConstrainGroupFromString("~1.2.3")
  70. // c.Match("1.2.3.5")
  71. // Returns: true
  72. func (self *ConstraintGroup) Match(version string) bool {
  73. for _, constraint := range self.constraints {
  74. if constraint.Match(version) == false {
  75. return false
  76. }
  77. }
  78. return true
  79. }
  80. func (self *ConstraintGroup) fromString(constraint string) bool {
  81. result := RegFind(`(?i)^([^,\s]*?)@(stable|RC|beta|alpha|dev)$`, constraint)
  82. if result != nil {
  83. constraint = result[1]
  84. if constraint == "" {
  85. constraint = "*"
  86. }
  87. }
  88. result = RegFind(`(?i)^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$`, constraint)
  89. if result != nil {
  90. if result[1] != "" {
  91. constraint = result[1]
  92. }
  93. }
  94. constraints := RegSplit(`\s*,\s*`, strings.Trim(constraint, " "))
  95. if len(constraints) > 1 {
  96. for _, part := range constraints {
  97. self.AddConstraint(self.parseConstraint(part)...)
  98. }
  99. return true
  100. }
  101. self.AddConstraint(self.parseConstraint(constraints[0])...)
  102. return true
  103. }
  104. func (self *ConstraintGroup) parseConstraint(constraint string) []*Constraint {
  105. stabilityModifier := ""
  106. result := RegFind(`(?i)^([^,\s]+?)@(stable|RC|beta|alpha|dev)$`, constraint)
  107. if result != nil {
  108. constraint = result[1]
  109. if result[2] != "stable" {
  110. stabilityModifier = result[2]
  111. }
  112. }
  113. result = RegFind(`^[x*](\.[x*])*$`, constraint)
  114. if result != nil {
  115. return make([]*Constraint, 0)
  116. }
  117. highVersion := ""
  118. lowVersion := ""
  119. result = RegFind(`(?i)^~(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?`+modifierRegex+`?$`, constraint)
  120. if result != nil {
  121. if len(result) > 4 && result[4] != "" {
  122. last, _ := strconv.Atoi(result[3])
  123. highVersion = result[1] + "." + result[2] + "." + strconv.Itoa(last+1) + ".0-dev"
  124. lowVersion = result[1] + "." + result[2] + "." + result[3] + "." + result[4]
  125. } else if len(result) > 3 && result[3] != "" {
  126. last, _ := strconv.Atoi(result[2])
  127. highVersion = result[1] + "." + strconv.Itoa(last+1) + ".0.0-dev"
  128. lowVersion = result[1] + "." + result[2] + "." + result[3] + ".0"
  129. } else {
  130. last, _ := strconv.Atoi(result[1])
  131. highVersion = strconv.Itoa(last+1) + ".0.0.0-dev"
  132. if len(result) > 2 && result[2] != "" {
  133. lowVersion = result[1] + "." + result[2] + ".0.0"
  134. } else {
  135. lowVersion = result[1] + ".0.0.0"
  136. }
  137. }
  138. if len(result) > 5 && result[5] != "" {
  139. lowVersion = lowVersion + "-" + expandStability(result[5])
  140. }
  141. if len(result) > 6 && result[6] != "" {
  142. lowVersion = lowVersion + result[6]
  143. }
  144. if len(result) > 7 && result[7] != "" {
  145. lowVersion = lowVersion + "-dev"
  146. }
  147. return []*Constraint{
  148. {">=", lowVersion},
  149. {"<", highVersion},
  150. }
  151. }
  152. result = RegFind(`^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[x*]$`, constraint)
  153. if result != nil {
  154. if len(result) > 3 && result[3] != "" {
  155. highVersion = result[1] + "." + result[2] + "." + result[3] + ".9999999"
  156. if result[3] == "0" {
  157. last, _ := strconv.Atoi(result[2])
  158. lowVersion = result[1] + "." + strconv.Itoa(last-1) + ".9999999.9999999"
  159. } else {
  160. last, _ := strconv.Atoi(result[3])
  161. lowVersion = result[1] + "." + result[2] + "." + strconv.Itoa(last-1) + ".9999999"
  162. }
  163. } else if len(result) > 2 && result[2] != "" {
  164. highVersion = result[1] + "." + result[2] + ".9999999.9999999"
  165. if result[2] == "0" {
  166. last, _ := strconv.Atoi(result[1])
  167. lowVersion = strconv.Itoa(last-1) + ".9999999.9999999.9999999"
  168. } else {
  169. last, _ := strconv.Atoi(result[2])
  170. lowVersion = result[1] + "." + strconv.Itoa(last-1) + ".9999999.9999999"
  171. }
  172. } else {
  173. highVersion = result[1] + ".9999999.9999999.9999999"
  174. if result[1] == "0" {
  175. return []*Constraint{{"<", highVersion}}
  176. } else {
  177. last, _ := strconv.Atoi(result[1])
  178. lowVersion = strconv.Itoa(last-1) + ".9999999.9999999.9999999"
  179. }
  180. }
  181. return []*Constraint{
  182. {">", lowVersion},
  183. {"<", highVersion},
  184. }
  185. }
  186. // match operators constraints
  187. result = RegFind(`^(<>|!=|>=?|<=?|==?)?\s*(.*)`, constraint)
  188. if result != nil {
  189. version := Normalize(result[2])
  190. if stabilityModifier != "" && parseStability(version) == "stable" {
  191. version = version + "-" + stabilityModifier
  192. } else if result[1] == "<" {
  193. match := RegFind(`(?i)-stable$`, result[2])
  194. if match == nil {
  195. version = version + "-dev"
  196. }
  197. }
  198. if len(result) > 1 && result[1] != "" {
  199. return []*Constraint{{result[1], version}}
  200. } else {
  201. return []*Constraint{{"=", version}}
  202. }
  203. }
  204. return []*Constraint{{constraint, stabilityModifier}}
  205. }
  206. // PCRegMap : PreCompiled Regex Map
  207. type PCRegMap struct {
  208. sync.RWMutex
  209. m map[string]*regexp.Regexp
  210. }
  211. // MustCompile : to replace regexp.MustCompile in RegFind.
  212. func (p *PCRegMap) MustCompile(pattern string) *regexp.Regexp {
  213. p.RLock()
  214. ret, exist := p.m[pattern]
  215. p.RUnlock()
  216. if exist {
  217. return ret
  218. }
  219. ret = regexp.MustCompile(pattern)
  220. p.Lock()
  221. p.m[pattern] = ret
  222. p.Unlock()
  223. return ret
  224. }
  225. var (
  226. regexpCache *PCRegMap
  227. )
  228. func init() {
  229. regexpCache = new(PCRegMap)
  230. regexpCache.m = make(map[string]*regexp.Regexp)
  231. }
  232. func RegFind(pattern, subject string) []string {
  233. reg := regexpCache.MustCompile(pattern)
  234. matched := reg.FindAllStringSubmatch(subject, -1)
  235. if matched != nil {
  236. return matched[0]
  237. }
  238. return nil
  239. }
  240. func RegSplit(pattern, subject string) []string {
  241. reg := regexp.MustCompile(pattern)
  242. indexes := reg.FindAllStringIndex(subject, -1)
  243. laststart := 0
  244. result := make([]string, len(indexes)+1)
  245. for i, element := range indexes {
  246. result[i] = subject[laststart:element[0]]
  247. laststart = element[1]
  248. }
  249. result[len(indexes)] = subject[laststart:len(subject)]
  250. return result
  251. }
上海开阖软件有限公司 沪ICP备12045867号-1