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

Infix file pattern #330

Merged
merged 24 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
71b7ac4
Bump version -> `0.92.7`
alexander-yevsyukov Feb 21, 2025
9ae4e6c
Require only Unix separators in `File`
alexander-yevsyukov Feb 21, 2025
e754025
Introduce `FilePattern.infix`
alexander-yevsyukov Feb 21, 2025
b5b683c
Fix non-Unix separators in stub paths
alexander-yevsyukov Feb 21, 2025
943bcef
Update dependency reports
alexander-yevsyukov Feb 21, 2025
b6dd482
Fix proto doc wording.
alexander-yevsyukov Feb 21, 2025
d78604e
Update build time
alexander-yevsyukov Feb 21, 2025
0c26f2c
Configure Dokka via script plugins
alexander-yevsyukov Feb 22, 2025
6e426f8
Fix Unix path conversion
alexander-yevsyukov Feb 22, 2025
57baad5
Restrict publishing graph check to the presence of task `publish`
alexander-yevsyukov Feb 22, 2025
79486ff
Update dependency reports
alexander-yevsyukov Feb 22, 2025
5f3e102
Restore Javadoc task setup
alexander-yevsyukov Feb 22, 2025
45c84c3
Reuse Unix path conversion
alexander-yevsyukov Feb 22, 2025
faa2b0b
Update build time
alexander-yevsyukov Feb 22, 2025
194ad11
Require only Unix separators in `Directory`
alexander-yevsyukov Feb 22, 2025
038b394
Clarify names for relative and absolute path functions
alexander-yevsyukov Feb 22, 2025
62de84d
Address warnings
alexander-yevsyukov Feb 22, 2025
006714e
Fix line length
alexander-yevsyukov Feb 22, 2025
a404a69
Update build time
alexander-yevsyukov Feb 22, 2025
bc667bc
Fix path conversion
alexander-yevsyukov Feb 22, 2025
39411f7
Add `Path.toAbsoluteDirectory()` extension
alexander-yevsyukov Feb 22, 2025
f6af6c3
Fix Unix separator conversion
alexander-yevsyukov Feb 22, 2025
3c2fc43
Fix Unix path conversion
alexander-yevsyukov Feb 22, 2025
20fc512
Fix import
alexander-yevsyukov Feb 22, 2025
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
14 changes: 12 additions & 2 deletions api/src/main/kotlin/io/spine/protodata/ast/FilePatternFactory.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024, TeamDev. All rights reserved.
* Copyright 2025, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,11 +41,21 @@ public object FilePatternFactory {
suffix = value
}

/**
* Creates a new [FilePattern] with the [infix][FilePattern.getInfix] field filled.
*/
public fun infix(value: String): FilePattern = filePattern {
value.checkNotBlank("infix")
infix = value
}

/**
* Creates a new [FilePattern] with the [prefix][FilePattern.getPrefix] field filled.
*/
@Deprecated(message = "Please use `infix` instead.", ReplaceWith("infix(value)"))
public fun prefix(value: String): FilePattern = filePattern {
value.checkNotBlank("prefix")
@Suppress("DEPRECATION") // Supporting for backward compatibility.
prefix = value
}

Expand All @@ -59,7 +69,7 @@ public object FilePatternFactory {

private fun String.checkNotBlank(name: String) {
require(isNotBlank()) {
"File pattern $name cannot be empty or blank: `$this`."
"The file pattern `$name` cannot be empty or blank. Encountered: `$this`."
}
}
}
8 changes: 5 additions & 3 deletions api/src/main/kotlin/io/spine/protodata/ast/FilePatterns.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024, TeamDev. All rights reserved.
* Copyright 2025, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,17 +29,19 @@
package io.spine.protodata.ast

import io.spine.protodata.ast.FilePattern.KindCase.KIND_NOT_SET
import io.spine.protodata.ast.FilePattern.KindCase.PREFIX
import io.spine.protodata.ast.FilePattern.KindCase.REGEX
import io.spine.protodata.ast.FilePattern.KindCase.INFIX
import io.spine.protodata.ast.FilePattern.KindCase.SUFFIX

/**
* Tells if this patterns matches the given [file].
*/
public fun FilePattern.matches(file: File): Boolean {
val path = file.path
@Suppress("DEPRECATION") // Support the `PREFIX` kind for backward compatibility.
return when (kindCase) {
PREFIX -> path.startsWith(prefix)
io.spine.protodata.ast.FilePattern.KindCase.PREFIX -> path.startsWith(prefix)
INFIX -> path.contains(infix)
SUFFIX -> path.endsWith(suffix)
REGEX -> path.matches(Regex(regex))
KIND_NOT_SET -> false
Expand Down
20 changes: 19 additions & 1 deletion api/src/main/kotlin/io/spine/protodata/ast/FilesAndPaths.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

@file:Suppress("TooManyFunctions") // ... we need for relative and absolute paths.

package io.spine.protodata.ast

import io.spine.protodata.util.Format
Expand All @@ -33,14 +35,20 @@ import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.name
import kotlin.io.path.nameWithoutExtension
import kotlin.io.path.pathString

/**
* Converts the given path to a [File] message containing an absolute version of this path.
*/
public fun Path.toProto(): File = file {
public fun Path.toAbsoluteFile(): File = file {
path = absolutePathString().toUnix()
}

/**
* Converts this path to a [File] message with conversion to Unix path separators.
*/
public fun Path.toProto(): File = file { path = this@toProto.pathString.toUnix() }

private fun String.toUnix(): String = replace('\\', '/')

/**
Expand All @@ -53,9 +61,19 @@ public fun java.io.File.unixPath(): String = path.toUnix()
*/
public fun Path.toDirectory(): Directory = toFile().toDirectory()

/**
* Converts this path to a [Directory] with the absolute path.
*/
public fun Path.toAbsoluteDirectory(): Directory = toAbsolutePath().toDirectory()

/**
* Converts this instance of [java.io.File] to a [File] message with an absolute path.
*/
public fun java.io.File.toAbsoluteFile(): File = toPath().toAbsoluteFile()

/**
* Converts this path to a [File] message with conversion to Unix path separators.
*/
public fun java.io.File.toProto(): File = toPath().toProto()

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
package io.spine.protodata.render

import io.spine.core.userId
import io.spine.protodata.ast.file
import io.spine.protodata.ast.toAbsoluteFile
import io.spine.protodata.ast.toProto
import io.spine.protodata.render.CoordinatesFactory.Companion.endOfFile
import io.spine.protodata.render.event.insertionPointPrinted
import io.spine.text.TextCoordinates
Expand Down Expand Up @@ -168,7 +169,7 @@ public abstract class InsertionPointPrinter<L: Language>(

private fun reportPoint(sourceFile: SourceFile<*>, pointLabel: String, comment: String) {
val event = insertionPointPrinted {
file = file { path = sourceFile.relativePath.toString() }
file = sourceFile.relativePath.toProto()
label = pointLabel
representationInCode = comment
}
Expand Down
3 changes: 2 additions & 1 deletion api/src/main/kotlin/io/spine/protodata/render/SourceFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
import io.spine.protodata.ast.file
import io.spine.protodata.ast.toProto
import io.spine.protodata.util.Cache
import io.spine.server.query.select
import io.spine.text.Text
Expand Down Expand Up @@ -262,7 +263,7 @@ private constructor(
*/
public fun atInline(insertionPoint: InsertionPoint): SourceAtPoint {
val points = sources.querying.select<InsertedPoints>()
.findById(file { path = relativePath.toString() })
.findById(relativePath.toProto())
val point = points?.pointList?.firstOrNull { it.label == insertionPoint.label }
return if (point != null) {
SpecificPoint(this@SourceFile, point)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
package io.spine.protodata.settings

import io.spine.protodata.util.ensureExistingDirectory
import io.spine.protodata.ast.toProto
import io.spine.protodata.ast.toAbsoluteFile
import io.spine.protodata.settings.event.SettingsFileDiscovered
import io.spine.protodata.settings.event.settingsFileDiscovered
import io.spine.protodata.util.Format
Expand Down Expand Up @@ -122,7 +122,7 @@ public class SettingsDirectory(
public fun emitEvents(): List<SettingsFileDiscovered> =
files().map {
settingsFileDiscovered {
file = it.toProto()
file = it.toAbsoluteFile()
}
}

Expand Down
10 changes: 7 additions & 3 deletions api/src/main/proto/spine/protodata/directory.proto
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024, TeamDev. All rights reserved.
* Copyright 2025, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -36,8 +36,12 @@ option java_outer_classname = "DirectoryProto";
option java_multiple_files = true;

// A path to a directory in an arbitrary OS.
//
// The Protobuf compiler only works with the Unix-style file separator (`/`), regardless of
// the current operating system. This value holds this exact format of the path.
//
message Directory {

// The path to the directory, could be relative or absolute.
string path = 1 [(required) = true];
// The path to the directory, could be relative or absolute, with the slash (`/`) separator.
string path = 1 [(required) = true, (pattern).regex = "^[^\\\\]*$"];
}
4 changes: 2 additions & 2 deletions api/src/main/proto/spine/protodata/file.proto
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ option java_multiple_files = true;
//
message File {

// The path to the file, separating directories with slash (`/`) symbol.
string path = 1 [(required) = true];
// The path to the file, separating directories with the slash (`/`) symbol.
string path = 1 [(required) = true, (pattern).regex = "^[^\\\\]*$"];
}
12 changes: 10 additions & 2 deletions api/src/main/proto/spine/protodata/file_pattern.proto
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024, TeamDev. All rights reserved.
* Copyright 2025, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -44,8 +44,16 @@ message FilePattern {
// The path must end with this string.
string suffix = 1;

// The path must contain this infix.
string infix = 4;

// The path must start with this string.
string prefix = 2;
//
// Deprecated: File paths managed by Spine Compiler are primarily absolute.
// That is why this kind of pattern would not work in most cases.
// Please use the `infix` property instead.
//
string prefix = 2 [deprecated = true];

// The path must match this regular expression.
string regex = 3;
Expand Down
4 changes: 2 additions & 2 deletions api/src/test/kotlin/io/spine/protodata/CompilationSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import io.spine.logging.testing.tapConsole
import io.spine.protodata.Compilation.ERROR_PREFIX
import io.spine.protodata.Compilation.WARNING_PREFIX
import io.spine.protodata.ast.Span
import io.spine.protodata.ast.toProto
import io.spine.protodata.ast.toAbsoluteFile
import io.spine.testing.TestValues
import java.io.File
import java.nio.file.Paths
Expand Down Expand Up @@ -146,7 +146,7 @@ internal class CompilationSpec {

@Test
fun `provide the 'check' utility function`() {
val file = File("some/path/goes/here.proto").toProto()
val file = File("some/path/goes/here.proto").toAbsoluteFile()
val span = Span.getDefaultInstance()
val msg = TestValues.randomString()
val error = assertThrows<Compilation.Error> {
Expand Down
18 changes: 18 additions & 0 deletions api/src/test/kotlin/io/spine/protodata/ast/FilePatternsSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.google.protobuf.Timestamp
import io.kotest.matchers.shouldBe
import io.spine.protodata.ast.FilePatternFactory.prefix
import io.spine.protodata.ast.FilePatternFactory.regex
import io.spine.protodata.ast.FilePatternFactory.infix
import io.spine.protodata.ast.FilePatternFactory.suffix
import io.spine.validate.ValidationError
import org.junit.jupiter.api.DisplayName
Expand All @@ -52,6 +53,13 @@ internal class FilePatternsSpec {
assertThrowing { suffix(" ") }
}

@Test
fun infix() {
assertThrowing { infix("") }
assertThrowing { infix(" ") }
}

@Suppress("DEPRECATION") // Supporting for backward combability.
@Test
fun prefix() {
assertThrowing { prefix("") }
Expand All @@ -76,12 +84,22 @@ internal class FilePatternsSpec {

@Test
fun prefix() {
@Suppress("DEPRECATION") // Supporting for backward compatibility.
prefix("google/protobuf/any").run {
matches(messageTypeOf<Any>()) shouldBe true
matches(messageTypeOf<Timestamp>()) shouldBe false
}
}

@Test
fun infix() {
infix("protodata/file").run {
matches(messageTypeOf<FilePattern>()) shouldBe true
matches(messageTypeOf<File>()) shouldBe true
matches(messageTypeOf<Directory>()) shouldBe false
}
}

@Test
fun suffix() {
suffix("y.proto").run {
Expand Down
47 changes: 47 additions & 0 deletions api/src/test/kotlin/io/spine/protodata/ast/FileSpec.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2025, TeamDev. All rights reserved.
*
* 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.protodata.ast

import io.spine.validate.ValidationException
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows

@DisplayName("`File` should")
internal class FileSpec {

@Test
fun `prohibit non-Unix path separators`() {
assertThrows<ValidationException> {
file { path = "foo\\bar.proto" }
}
assertDoesNotThrow {
file { path = "foo/bar.proto" }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import io.spine.protodata.given.StubDeclaration
import io.spine.protodata.given.copy
import io.spine.protodata.given.stubDeclaration
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -97,4 +96,4 @@ private fun StubDeclaration.withAbsoluteFile(path: File): StubDeclaration =
replaceIfNotAbsoluteAlready(path) { copy { file = path } }

private fun absoluteFile(path: String): File =
file { this.path = Path(".").resolve(path).absolutePathString() }
Path(".").resolve(path).toAbsolutePath().toAbsoluteFile()
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import io.spine.protodata.ast.event.fileOptionDiscovered
import io.spine.protodata.ast.file
import io.spine.protodata.ast.produceOptionEvents
import io.spine.protodata.ast.toJava
import io.spine.protodata.ast.toProto
import io.spine.protodata.ast.toAbsoluteFile
import io.spine.protodata.backend.DescriptorFilter
import io.spine.protodata.protobuf.file
import io.spine.protodata.protobuf.toHeader
Expand Down Expand Up @@ -106,7 +106,7 @@ private class ProtoFileEvents(
val relativePath = hdr.file.toJava()
val fullPath = typeSystem.compiledProtoFiles.find(relativePath)
if (fullPath != null) {
hdr.copy { file = fullPath.toProto() }
hdr.copy { file = fullPath.toAbsoluteFile() }
} else {
hdr
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import io.spine.protodata.ast.ProtobufSourceFile
import io.spine.protodata.ast.doc
import io.spine.protodata.ast.option
import io.spine.protodata.ast.span
import io.spine.protodata.ast.toProto
import io.spine.protodata.ast.toAbsoluteFile
import io.spine.protodata.ast.toType
import io.spine.protodata.backend.event.CompilerEvents
import io.spine.protodata.context.CodegenContext
Expand Down Expand Up @@ -170,7 +170,7 @@ class CodeGenerationContextSpec {
fun `with files marked for generation`() {
val fullPath = typeSystem.findAbsolute(DoctorProto.getDescriptor())
val assertSourceFile = ctx.assertEntity<ProtoSourceFileView, _>(
fullPath!!.toProto()
fullPath!!.toAbsoluteFile()
)
assertSourceFile.exists()

Expand Down Expand Up @@ -216,7 +216,7 @@ class CodeGenerationContextSpec {
fun `with respect for custom options among the source files`() {
val filePath = typeSystem.findAbsolute(PhDProto.getDescriptor())
val phdFile = ctx.assertEntity<ProtoSourceFileView, _>(
filePath!!.toProto()
filePath!!.toAbsoluteFile()
).actual()!!.state() as ProtobufSourceFile
val paperType = phdFile.typeMap.values.find { it.name.simpleName == "Paper" }!!
val keywordsField = paperType.fieldList.find { it.name.value == "keywords" }!!
Expand Down
Loading
Loading