Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept empty versions #86

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions dependency/shared/src/main/scala/dependency/DependencyLike.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,16 @@ final case class DependencyLike[+A <: NameAttributes, +E <: NameAttributes](
.mkString
private def paramsString: String =
excludeString ++ userParamsString
def render: String =
s"${module.render}:$version$paramsString"
def render: String = {
val b = new StringBuilder
b ++= module.render
if (version.nonEmpty) {
b += ':'
b ++= version
}
b ++= paramsString
b.result()
}
override def toString: String =
render
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ object DependencyParser {
case (module, remaining) =>
val (version, configOpt, params) = splitRemainingPart(remaining, acceptInlineConfiguration)
assert(acceptInlineConfiguration || configOpt.isEmpty)
val version0 =
if (acceptInlineConfiguration && version == " ") ""
else version

val (excludeParams, remainingParams) = params.partition(_.startsWith("exclude="))
val maybeExclusions = excludeParams
Expand All @@ -38,21 +41,18 @@ object DependencyParser {
}

for {
_ <- ModuleParser.validateValue(version, "version")
_ <- ModuleParser.validateValue(version0, "version")
exclusions <- maybeExclusions
} yield {
val userParams = remainingParams.iterator.map(parseParam).toSeq ++ configOpt.toSeq.map("$inlineConfiguration" -> Some(_))
DependencyLike(module, version, exclusions, userParams)
DependencyLike(module, version0, exclusions, userParams)
}
}

private def attrSeparator = ","
private def argSeparator = ":"

private def splitRemainingPart(input: String, acceptInlineConfiguration: Boolean): (String, Option[String], Seq[String]) = {

def simpleSplit(s: String): (String, Seq[String]) =
s.split(attrSeparator) match {
s.split(ModuleParser.paramSeparator) match {
case Array(coordsEnd, attrs @ _*) => (coordsEnd, attrs)
}

Expand All @@ -72,7 +72,7 @@ object DependencyParser {
}

if (acceptInlineConfiguration)
versionPart.split(argSeparator, 2) match {
versionPart.split(ModuleParser.argSeparator, 2) match {
case Array(ver, config) => (ver, Some(config), attrs0)
case Array(ver) => (ver, None, attrs0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package parser

object ModuleParser {

private[parser] def paramSeparator = ","
private[parser] def argSeparator = ":"

private implicit class EitherWithFilter[L, R](private val e: Either[L, R]) extends AnyVal {
def withFilter(f: R => Boolean): Either[L, R] =
if (e.forall(f)) e else throw new MatchError(e)
Expand All @@ -19,7 +22,7 @@ object ModuleParser {
*/
def parse(input: String): Either[String, AnyModule] = {

val parts = input.split(":", -1).map(Some(_).filter(_.nonEmpty))
val parts = input.split(argSeparator, -1).map(Some(_).filter(_.nonEmpty))

val values = parts match {
case Array(Some(org), Some(name)) => Right((org, name, NoAttributes))
Expand All @@ -40,7 +43,11 @@ object ModuleParser {

def parsePrefix(input: String): Either[String, (AnyModule, String)] = {

val parts = input.split(":", -1).map(Some(_).filter(_.nonEmpty))
val (input0, paramsOpt) = input.split(paramSeparator, 2) match {
case Array(input0) => (input0, None)
case Array(input0, params) => (input0, Some(params))
}
val parts = input0.split(argSeparator, -1).map(Some(_).filter(_.nonEmpty))

val values = parts match {
case Array(Some(org), Some(name), rest @ _*) => Right((org, name, NoAttributes, rest))
Expand All @@ -56,7 +63,7 @@ object ModuleParser {
_ <- validateValue(org, "organization")
(name, attributes) <- parseNamePart(name0)
_ <- validateValue(name, "module name")
} yield (ModuleLike(org, name, nameAttributes, attributes), rest.map(_.getOrElse("")).mkString(":"))
} yield (ModuleLike(org, name, nameAttributes, attributes), rest.map(_.getOrElse("")).mkString(argSeparator) + paramsOpt.fold("")(paramSeparator + _))
}

private def parseNamePart(input: String): Either[String, (String, Map[String, String])] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package dependency
package parser

import com.eed3si9n.expecty.Expecty.expect

class DependencyParserNoVersionTests extends munit.FunSuite {

test("simple") {
val res = DependencyParser.parse("org:name")
val expected = Right(Dependency("org", "name", ""))
expect(res == expected)
}

test("simple with colon") {
val res = DependencyParser.parse("org:name:")
val expected = Right(Dependency("org", "name", ""))
expect(res == expected)
}

test("scala") {
val res = DependencyParser.parse("org::name")
val expected = Right(ScalaDependency("org", "name", ""))
expect(res == expected)
}

test("scala full cross-version") {
val res = DependencyParser.parse("org:::name")
val expected = Right(ScalaDependency(ScalaModule("org", "name", fullCrossVersion = true), ""))
expect(res == expected)
}

test("scala platform") {
val res = DependencyParser.parse("org::name::")
val expected = Right(ScalaDependency(ScalaModule("org", "name", fullCrossVersion = false, platform = true), ""))
expect(res == expected)
}

test("scala with attributes") {
val res = DependencyParser.parse("org::name;scala=2.12;sbt=1.0")
val expected = Right(ScalaDependency(ScalaModule("org", "name").copy(attributes = Map("scala" -> "2.12", "sbt" -> "1.0")), ""))
expect(res == expected)
}

test("attributes") {
val res = DependencyParser.parse("org:name;scala=2.12;sbt=1.0")
val expected = Right(Dependency(Module("org", "name").copy(attributes = Map("scala" -> "2.12", "sbt" -> "1.0")), ""))
expect(res == expected)
}

test("exclude") {
val res = DependencyParser.parse("org:name,exclude=fu%ba")
val expected = Right(Dependency("org", "name", "").copy(exclude = CovariantSet(Module("fu", "ba"))))
expect(res == expected)
}

test("scala exclude") {
val res = DependencyParser.parse("org:name,exclude=fu%%ba")
val expected = Right(Dependency("org", "name", "").copy(exclude = CovariantSet(ScalaModule("fu", "ba"))))
expect(res == expected)
}

test("several exclude") {
val res = DependencyParser.parse("org:name,exclude=fu%ba,exclude=aa%%aa-1")
val expected = Right(Dependency("org", "name", "").copy(exclude = CovariantSet(Module("fu", "ba"), ScalaModule("aa", "aa-1"))))
expect(res == expected)
}

test("param") {
val res = DependencyParser.parse("org:name,something=ba")
val expected = Right(Dependency("org", "name", "").copy(userParams = Seq("something" -> Some("ba"))))
expect(res == expected)
}

test("no-value param") {
val res = DependencyParser.parse("org:name,something")
val expected = Right(Dependency("org", "name", "").copy(userParams = Seq("something" -> None)))
expect(res == expected)
}

test("multiple same key params") {
val res = DependencyParser.parse("org:name,something=a,something,something=b")
val expected = Right(Dependency("org", "name", "").copy(
userParams = Seq(
"something" -> Some("a"),
"something" -> None,
"something" -> Some("b")
)
))
expect(res == expected)
}

test("scala + params + exclusions") {
val res = DependencyParser.parse("org:::name::,intransitive,exclude=foo%*,exclude=comp%%*,url=aaaa")
val expected = Right(
ScalaDependency(ScalaModule("org", "name", fullCrossVersion = true, platform = true), "")
.copy(
exclude = CovariantSet(
Module("foo", "*"),
ScalaModule("comp", "*")
),
userParams = Seq(
"intransitive" -> None,
"url" -> Some("aaaa")
)
)
)
expect(res == expected)
}

test("inline config") {
val res = DependencyParser.parse("org:name: :runtime")
val expected = Right(
Dependency("org", "name", "").copy(
userParams = Seq(
"$inlineConfiguration" -> Some("runtime")
)
)
)
expect(res == expected)
}
test("inline config with param") {
val res = DependencyParser.parse("org:name: :runtime,something=ba")
val expected = Right(
Dependency("org", "name", "").copy(
userParams = Seq(
"something" -> Some("ba"),
"$inlineConfiguration" -> Some("runtime")
)
)
)
expect(res == expected)
}

test("reject slash in org") {
val res = DependencyParser.parse("o/rg::name")
expect(res.isLeft)
}
test("reject backslash in org") {
val res = DependencyParser.parse("o\\rg::name")
expect(res.isLeft)
}
test("reject slash in name") {
val res = DependencyParser.parse("org::/name")
expect(res.isLeft)
}
test("reject backslash in name") {
val res = DependencyParser.parse("org::\\name")
expect(res.isLeft)
}
}
Loading