diff --git a/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/KotlinCodeGenerator.kt b/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/KotlinCodeGenerator.kt index 6e1de95ea..08efb1780 100644 --- a/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/KotlinCodeGenerator.kt +++ b/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/KotlinCodeGenerator.kt @@ -269,6 +269,11 @@ class KotlinCodeGenerator( val containRegexProperty = properties.values.any { it.isRegex() } + fun PClass.Property.isBase64(): Boolean = + (this.type as? PType.Class)?.pClass?.info == PClassInfo.Base64 + + val containBase64Property = properties.values.any { it.isBase64() } + fun generateConstructor(): FunSpec { val builder = FunSpec.constructorBuilder() for ((name, property) in allProperties) { diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java index 5035a057e..422279e99 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java @@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.LoopNode; +import org.apache.commons.codec.binary.Base64; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.regex.*; @@ -148,6 +149,13 @@ protected boolean eval(String self) { } } + public abstract static class isBase64 extends ExternalPropertyNode { + @Specialization + protected boolean eval(String self) { + return Base64.isArrayByteBase64(self.getBytes()); + } + } + public abstract static class chars extends ExternalPropertyNode { @Specialization @TruffleBoundary diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/string.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/string.pkl index c36b0e0c8..1f6ebaeae 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/api/string.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/string.pkl @@ -57,6 +57,10 @@ facts { !str1.endsWith("efx") } + ["isBase64"] { + quickBrownFox.base64.isBase64 + } + ["isRegex"] { str1.isRegex #"\w*[0-9]"#.isRegex diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/stringUnicode.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/stringUnicode.pkl index 9ccfd03b1..57528ebb8 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/api/stringUnicode.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/stringUnicode.pkl @@ -25,6 +25,10 @@ facts { str2.base64.base64Decoded == str2 } + ["isBase64"] { + str1.isBase64 + } + ["contains()"] { str1.contains(str2) str1.contains(str1) @@ -304,12 +308,12 @@ examples { str1.sha1 str2.sha1 } - + ["sha256"] { str1.sha256 str2.sha256 } - + ["sha256Int"] { str1.sha256Int str2.sha256Int @@ -319,7 +323,7 @@ examples { str1.md5 str2.md5 } - + ["base64"] { str1.base64 str2.base64 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/string.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/string.pcf index 643897696..8ae5df5fa 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/api/string.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/string.pcf @@ -47,6 +47,9 @@ facts { true true } + ["isBase64"] { + true + } ["toBoolean()"] { true true @@ -175,8 +178,8 @@ examples { } ["trimStart()"] { """ - abcdefg \t - + abcdefg \t + """ } ["trimEnd()"] { diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/stringUnicode.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/stringUnicode.pcf index 39fda2628..e98f1b9c1 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/api/stringUnicode.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/stringUnicode.pcf @@ -40,6 +40,9 @@ facts { true true } + ["isBase64"] { + true + } ["capitalize"] { true true @@ -126,8 +129,8 @@ examples { } ["trimStart()"] { """ - 😀😈😍😎😡🤢🤣 \t - + 😀😈😍😎😡🤢🤣 \t + """ } ["trimEnd()"] { diff --git a/stdlib/base.pkl b/stdlib/base.pkl index cffae7cb8..7a8a82b5c 100644 --- a/stdlib/base.pkl +++ b/stdlib/base.pkl @@ -1123,6 +1123,9 @@ external class String extends Any { /// Tells if this string is a valid regular expression according to [Regex]. external isRegex: Boolean + /// Tells if this string is a valid base64 string according to [Base64]. + external isBase64: Boolean + /// The [MD5](https://en.wikipedia.org/wiki/MD5) /// hash of this string's UTF-8 byte sequence /// as hexadecimal string. @@ -1368,7 +1371,7 @@ external class String extends Any { external function split(pattern: String|Regex): List /// Splits this string matches of [pattern], up to [limit] substrings. - /// + /// /// Returns a [List] with at most [limit] elements. /// If the limit has been reached, the last entry will contain the un-split remainder of this string. /// @@ -1379,7 +1382,7 @@ external class String extends Any { /// "a.b.c".splitLimit(".", 50) == List("a", "b", "c") /// "a.b:c".splitLimit(Regex("[.:]"), 3) == List("a", "b", "c") /// ``` - @Since { version = "0.27.0" } + @Since { version = "0.27.0" } external function splitLimit(pattern: String|Regex, limit: Int(this > 0)): List /// Converts the first character of this string to title case. @@ -3047,7 +3050,7 @@ external class Set extends Collection { /// The intersection of this set and [other]. external function intersect(other: Set): Set - + /// The difference of this set and [other]. external function difference(other: Set): Set }