-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
8 changed files
with
920 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
//> using scala 3.3.1 | ||
//> using options -java-output-version:11 -encoding:utf-8 | ||
//> using options -deprecation -feature -Werror -Wunused:all | ||
|
||
//> using dep org.virtuslab::besom-json:0.1.1-SNAPSHOT | ||
//> using dep org.virtuslab::besom-core:0.1.1-SNAPSHOT | ||
//> using dep org.virtuslab::scala-yaml:0.0.8 | ||
|
||
//> using test.dep "org.scalameta::munit:1.0.0-M10" | ||
|
||
//> using publish.name "besom-auto" | ||
//> using publish.organization "org.virtuslab" | ||
//> using publish.url "https://github.com/VirtusLab/besom" | ||
//> using publish.vcs "github:VirtusLab/besom" | ||
//> using publish.license "Apache-2.0" | ||
//> using publish.repository "central" | ||
//> using publish.developer "lbialy|Łukasz Biały|https://github.com/lbialy" | ||
//> using publish.developer "pawelprazak|Paweł Prażak|https://github.com/pawelprazak" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package besom.auto | ||
|
||
/** Stack is an isolated, independently configurable instance of a Pulumi program. Stack exposes methods for the full pulumi lifecycle | ||
* (up/preview/refresh/destroy), as well as managing configuration. Multiple Stacks are commonly used to denote different phases of | ||
* development (such as development, staging and production) or feature branches (such as feature-x-dev, jane-feature-x-dev). | ||
* | ||
* @param workspace | ||
* the workspace associated with the stack | ||
* @param stackName | ||
* the name of the stack | ||
*/ | ||
case class Stack(workspace: Workspace, stackName: String) | ||
|
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package besom.model | ||
|
||
import scala.compiletime.* | ||
import scala.compiletime.ops.string.* | ||
import scala.language.implicitConversions | ||
|
||
/** Name is an identifier. */ | ||
opaque type Name <: String = String | ||
object Name: | ||
private val NameFirstCharRegexpPattern = "[A-Za-z0-9_.-]" | ||
private val NameRestCharRegexpPattern = "[A-Za-z0-9_.-]*" | ||
private[model] val NameRegexpPattern = NameFirstCharRegexpPattern + NameRestCharRegexpPattern | ||
|
||
private val NameRegexp = NameRegexpPattern.r | ||
private[model] val NameFirstCharRegexp = ("^" + NameFirstCharRegexpPattern + "$").r | ||
private[model] val NameRestCharRegexp = ("^" + NameRestCharRegexpPattern + "$").r | ||
|
||
/** IsName checks whether a string is a legal Name. */ | ||
def isName(s: String): Boolean = s.nonEmpty && NameRegexp.findFirstIn(s).isDefined | ||
|
||
/** Parse a string into a [[Name]]. | ||
* @param s | ||
* is a string to parse | ||
* @return | ||
* a [[Name]] if the string is valid, otherwise a compile time error occurs | ||
*/ | ||
inline def apply(s: String): Name = | ||
requireConst(s) | ||
inline if !constValue[Matches[s.type, "[A-Za-z0-9_.-][A-Za-z0-9_.-]*"]] then | ||
error("Invalid Name string. Must match '[A-Za-z0-9_.-][A-Za-z0-9_.-]*'.") | ||
else s | ||
|
||
implicit inline def str2Name(inline s: String): Name = Name(s) | ||
|
||
private[besom] def unsafeOf(s: String): Name = s | ||
|
||
extension (name: Name) | ||
/** Turns a [[Name]] into a qualified name, this is legal, since Name's is a proper subset of QName's grammar. | ||
* @return | ||
* the [[Name]] as a [[QName]] | ||
*/ | ||
def asQName: QName = QName.unsafeOf(name) | ||
|
||
end Name | ||
|
||
/** QName is a qualified identifier. The "/" character optionally delimits different pieces of the name. Each element conforms to Name | ||
* regexp pattern. For example, "pulumi/pulumi/stack". | ||
*/ | ||
|
||
opaque type QName <: String = String | ||
object QName: | ||
/** Parse a string into a [[QName]]. | ||
* @param s | ||
* is a string to parse | ||
* @return | ||
* a [[QName]] if the string is valid, otherwise a compile time error occurs | ||
*/ | ||
inline def apply(s: String): QName = | ||
requireConst(s) | ||
inline if !constValue[Matches[s.type, "([A-Za-z0-9_.-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_.-][A-Za-z0-9_.-]*"]] then | ||
error("Invalid QName string. Must match '([A-Za-z0-9_.-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_.-][A-Za-z0-9_.-]*'.") | ||
else s | ||
|
||
implicit inline def str2QName(inline s: String): QName = QName(s) | ||
|
||
private[besom] def unsafeOf(s: String): QName = s | ||
|
||
/** QNameDelimiter is what delimits Namespace and Name parts. */ | ||
private val QNameDelimiter = "/" | ||
private val QNameRegexpPattern = "(" + Name.NameRegexpPattern + "\\" + QNameDelimiter + ")*" + Name.NameRegexpPattern | ||
private val QNameRegexp = QNameRegexpPattern.r | ||
|
||
/** IsQName checks whether a string is a legal QName. */ | ||
def isQName(s: String): Boolean = s.nonEmpty && QNameRegexp.findFirstIn(s).isDefined | ||
|
||
/** Converts an arbitrary string into a [[QName]], converting the string to a valid [[QName]] if necessary. The conversion is | ||
* deterministic, but also lossy. | ||
*/ | ||
def convert(s: String): QName = | ||
val output = s.split(QNameDelimiter).filter(_.nonEmpty).map { segment => | ||
val chars = segment.toCharArray | ||
if (!Name.NameFirstCharRegexp.matches(chars.head.toString)) chars.update(0, '_') | ||
for (i <- 1 until chars.length) { | ||
if (!Name.NameRestCharRegexp.matches(chars(i).toString)) chars.update(i, '_') | ||
} | ||
new String(chars) | ||
} | ||
val result = output.mkString(QNameDelimiter) | ||
if (result.isEmpty) QName.unsafeOf("_") else QName.unsafeOf(result) | ||
|
||
end convert | ||
|
||
extension (qname: QName) | ||
/** Extracts the [[Name]] portion of a [[QName]] (dropping any namespace). */ | ||
def name: Name = | ||
val ix = qname.lastIndexOf(QNameDelimiter) | ||
val nmn = if ix == -1 then qname else qname.substring(ix + 1) | ||
assert(Name.isName(nmn), s"QName $qname has invalid name $nmn") | ||
nmn | ||
|
||
/** Extracts the namespace portion of a [[QName]] (dropping the name), this may be empty. */ | ||
def namespace: QName = | ||
val ix = qname.lastIndexOf(QNameDelimiter) | ||
val qn = if ix == -1 then "" else qname.substring(0, ix) | ||
assert(isQName(qn), s"QName $qname has invalid namespace $qn") | ||
QName.unsafeOf(qn) | ||
|
||
end QName | ||
|
||
/** PackageName is a qualified name referring to an imported package. */ | ||
type PackageName = QName | ||
|
||
/** ModuleName is a qualified name referring to an imported module from a package. */ | ||
type ModuleName = QName | ||
|
||
/** ModuleMemberName is a simple name representing the module member's identifier. */ | ||
type ModuleMemberName = Name | ||
|
||
/** ClassMemberName is a simple name representing the class member's identifier. */ | ||
type ClassMemberName = Name | ||
|
||
/** TypeName is a simple name representing the type's name, without any package/module qualifiers. */ | ||
type TypeName = Name |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package besom.model | ||
|
||
import besom.test.CompileAssertions | ||
|
||
class NamesTest extends munit.FunSuite with CompileAssertions: | ||
|
||
import QName.* // force use of QName extensions instead of TestOptionsConversions.name | ||
|
||
test("validation - all alpha"): | ||
compiles("""import besom.model._ | ||
val n = Name("simple") | ||
""") | ||
|
||
test("validation - mixed-case alpha"): | ||
compiles("""import besom.model._ | ||
val n = Name("SiMplE") | ||
""") | ||
|
||
test("validation - alphanumeric"): | ||
compiles("""import besom.model._ | ||
val n = Name("simple0") | ||
""") | ||
|
||
test("validation - mixed-case alphanumeric"): | ||
compiles("""import besom.model._ | ||
val n = Name("SiMpLe0") | ||
""") | ||
|
||
test("validation - permit underscore"): | ||
compiles("""import besom.model._ | ||
val n = Name("_") | ||
""") | ||
|
||
test("validation - mixed-case alphanumeric/underscore"): | ||
compiles("""import besom.model._ | ||
val n = Name("s1MPl3_") | ||
""") | ||
compiles("""import besom.model._ | ||
val n = Name("_s1MPl3") | ||
""") | ||
|
||
test("validation - permit hyphens"): | ||
compiles("""import besom.model._ | ||
val n = Name("hy-phy") | ||
""") | ||
|
||
test("validation - start with ."): | ||
compiles("""import besom.model._ | ||
val n = Name(".dotstart") | ||
""") | ||
|
||
test("validation - start with -"): | ||
compiles("""import besom.model._ | ||
val n = Name("-hyphenstart") | ||
""") | ||
|
||
test("validation - start with numbers"): | ||
compiles("""import besom.model._ | ||
val n = Name("0num") | ||
""") | ||
|
||
test("validation - start with numbers"): | ||
compiles("""import besom.model._ | ||
val n = Name("9num") | ||
""") | ||
|
||
test("validation - multi-part name"): | ||
compiles("""import besom.model._ | ||
val n = QName("namespace/complex") | ||
""") | ||
failsToCompile("""import besom.model._ | ||
val n = Name("namespace/complex") | ||
""") | ||
|
||
test("validation - multi-part, alphanumeric, etc. name"): | ||
compiles("""import besom.model._ | ||
val n = QName("_naMeSpace0/coMpl3x32") | ||
""") | ||
failsToCompile("""import besom.model._ | ||
val n = Name("_naMeSpace0/coMpl3x32") | ||
""") | ||
|
||
test("validation - even more complex parts"): | ||
compiles("""import besom.model._ | ||
val n = QName("n_ameSpace3/moRenam3sp4ce/_Complex5") | ||
""") | ||
failsToCompile("""import besom.model._ | ||
val n = Name("n_ameSpace3/moRenam3sp4ce/_Complex5") | ||
""") | ||
|
||
test("validation - bad characters"): | ||
failsToCompile("""import besom.model._ | ||
val n = QName("s!mple") | ||
""") | ||
failsToCompile("""import besom.model._ | ||
val n = Name("s!mple") | ||
""") | ||
failsToCompile("""import besom.model._ | ||
val n = QName("namesp@ce/complex") | ||
""") | ||
failsToCompile("""import besom.model._ | ||
val n = Name("namesp@ce/complex") | ||
""") | ||
failsToCompile("""import besom.model._ | ||
val n = QName("namespace/morenamespace/compl#x") | ||
""") | ||
failsToCompile("""import besom.model._ | ||
val n = Name("namespace/morenamespace/compl#x") | ||
""") | ||
|
||
test("parsing - simple name"): | ||
assertEquals(Name("simple"), "simple") | ||
assertEquals(QName("namespace/complex").name, "complex") | ||
assertEquals(QName("ns1/ns2/ns3/ns4/complex").name, "complex") | ||
assertEquals(QName("_/_/_/_/a0/c0Mpl3x_").name, "c0Mpl3x_") | ||
|
||
test("parsing - simple namespace"): | ||
assertEquals(QName("namespace/complex").namespace, "namespace") | ||
assertEquals(QName("ns1/ns2/ns3/ns4/complex").namespace, "ns1/ns2/ns3/ns4") | ||
assertEquals(QName("_/_/_/_/a0/c0Mpl3x_").namespace, "_/_/_/_/a0") | ||
|
||
test("convert to QName"): | ||
assertEquals(QName.convert("foo/bar"), "foo/bar") | ||
assertEquals(QName.convert("https:"), "https_") | ||
assertEquals(QName.convert("https://"), "https_") | ||
assertEquals(QName.convert(""), "_") | ||
assertEquals(QName.convert("///"), "_") | ||
|
||
end NamesTest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package besom.test | ||
|
||
trait CompileAssertions: | ||
self: munit.FunSuite => | ||
|
||
inline def failsToCompile(inline code: String): Unit = | ||
assert( | ||
!scala.compiletime.testing.typeChecks(code), | ||
s"Code compiled correctly when expecting type errors:${System.lineSeparator()}$code" | ||
) | ||
|
||
inline def compiles(inline code: String): Unit = | ||
assert(scala.compiletime.testing.typeChecks(code), s"Code failed to compile:${System.lineSeparator()}$code") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters