
.13.e3.source-code.version.scala Maven / Gradle / Ivy
/*
Copyright 2010 Aaron J. Radke
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cc.drx
object Version extends Applicable[String,Version]{
//puntuation | space | [digit look behind] & [letter look ahead] | [letter look behind] & [digit look ahead]
val sep = """(\s*[\.\_\-\/\,\;]\s*)|\s+|(?<=\d)+(?=\p{L})|(?<=\p{L})+(?=\d)""".r
implicit object ParsableVersion extends Parsable[Version]{ def apply(v:String):Version = Version(v) }
final class VersionStringContext(val sc:StringContext) extends AnyVal{
def version(args:Any*):Version = Version(sc.s(args:_*))
}
def apply(major:Int, minor:Int, patch:Int):Version = Version(s"$major.$minor.$patch")
//assuming initial release with no previous specified date
def kryptonInit(init:Date=Date.now):Version = Version(init.krypton take 1)
// def krypton(last:Date, next:Date):Version = krypton(last.krypton take 8, next)
def kryptonNext(lastVer:Version, next:Date=Date.now):Version = {
val nextVer = next.krypton take 8
val same = lastVer.name lcp nextVer
Version(same + nextVer.drop(same.size).take(1))
}
def kryptonMap(xs:Iterable[Date]):Iterable[Version] = {
xs.scanLeft(Version("0")){kryptonNext _ }.tail //pull in the tupled version of krypton
}
}
/**Version represents a string name that may look like a version, its main feature provides a nice human sorting rather than full string bases sorting*/
case class Version(val name: String) extends AnyVal with Ordered[Version]{
// def value:String = name //TODO change name to value
// import scala.math.Ordered.orderingToOrdered //FIXME is this import really not needed
override def toString = name
def /(minor:Int):Version = Version(name + "." + minor)
def /(minor:String):Version = Version(name + "." + minor)
def *(alt:String):Version = Version(name + "-" + alt)
//less than 6 is a convenient size that tracks timesstamps and datestamps in iso form
def isDigit(s:String):Boolean = s.nonEmpty && (s.size < 6) && s.forall{_.isDigit}
def compare(that:Version):Int = {
@tailrec def cmp(a:Seq[String], b:Seq[String]):Int = {
//println(s"$a , $b")
//--both non empty
if(a.nonEmpty && b.nonEmpty){
//if head is the same keep comparing
if(a.head == b.head) cmp(a.tail, b.tail)
//compare by numeric if all digits
else if(isDigit(a.head) && isDigit(b.head)) a.head.toInt compare b.head.toInt
//if a only a digit then let 'a' be bigger
else if(isDigit(a.head)) 1
//if b only is a digit then let 'b' be bigger
else if(isDigit(b.head)) -1
//use the normal string comparison function
else a.head compare b.head
}
//-- 'b' is larger if (implicityly no 'a') && ('b' is a number),
//-- 'a' is larger if 'b' contains more
else if(b.nonEmpty) if(isDigit(b.head)) -1 else 1
//-- 'a' is larger if (implicitly no 'b') && ('a' is a number),
//-- 'b' is larger if 'a' contains more
else if(a.nonEmpty) if(isDigit(a.head)) 1 else -1
// otherwise assume equality //false case
else 0 //both empty so equal
}
if(this.name == that.name) 0 else cmp(this.terms, that.terms)
}
def terms = (Version.sep split name).toIndexedSeq
def isSnapshot = name.toLowerCase contains "snapshot"
def isFinal = name forall {d => d.isDigit || d == '.'}
def isIntermediate = name exists {d => d.isLetter }
def take(depth:Int):Version = Version(terms take depth mkString ".")
def similar(that:Version,depth:Int) = this.take(depth) == that.take(depth)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy