blob: d9c5efd3034c29acff784fa3bda97f6dc63719b9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.openwhisk.core.entity
import spray.json.deserializationError
import spray.json.JsString
import spray.json.JsValue
import spray.json.RootJsonFormat
import scala.util.Try
/**
* State needed to implement semantic versioning.
*
* @see http://semver.org
*
* It is a value type (hence == is .equals, immutable and cannot be assigned null).
* The constructor is private so that argument requirements are checked and normalized
* before creating a new instance.
*
* @param (major, minor, patch) for the semantic version
*/
protected[core] class SemVer private (private val version: (Int, Int, Int)) extends AnyVal {
protected[core] def major = version._1
protected[core] def minor = version._2
protected[core] def patch = version._3
protected[core] def upMajor = SemVer(major + 1, minor, patch)
protected[core] def upMinor = SemVer(major, minor + 1, patch)
protected[core] def upPatch = SemVer(major, minor, patch + 1)
protected[entity] def toJson = JsString(toString)
override def toString = s"$major.$minor.$patch"
}
protected[core] object SemVer {
protected[core] val REGEX = """(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)"""
/** Default semantic version */
protected[core] def apply() = new SemVer(0, 0, 1)
/**
* Creates a semantic version.
*
* @param major the major >= 0
* @param minor the minor >= 0
* @param patch the patch >= 0
* @return SemVer instance
* @throws IllegalArgumentException if the parameters results in an invalid semantic version
*/
protected[core] def apply(major: Int, minor: Int, patch: Int): SemVer = {
if ((major == 0 && minor == 0 && patch == 0) || (major < 0 || minor < 0 || patch < 0)) {
throw new IllegalArgumentException(s"bad semantic version $major.$minor.$patch must not be negative")
} else new SemVer(major, minor, patch)
}
/**
* Parses a string representation of a semantic version and creates
* a instance of SemVer. If the string is not properly formatted, an
* IllegalSemVer exception is thrown.
*
* @param str to parse to extract semantic version
* @return SemVer instance
* @thrown IllegalArgumentException if string is not a valid semantic version
*/
protected[entity] def apply(str: String): SemVer = {
try {
val parts = if (str != null && str.nonEmpty) str.split('.') else Array[String]()
val major = if (parts.size >= 1) parts(0).toInt else 0
val minor = if (parts.size >= 2) parts(1).toInt else 0
val patch = if (parts.size >= 3) parts(2).toInt else 0
SemVer(major, minor, patch)
} catch {
case _: Throwable => throw new IllegalArgumentException(s"bad semantic version $str")
}
}
implicit val serdes = new RootJsonFormat[SemVer] {
def write(v: SemVer) = v.toJson
def read(value: JsValue) =
Try {
val JsString(v) = value
SemVer(v)
} getOrElse deserializationError("semantic version malformed")
}
}