KLIB API: Write metadata through `KlibMetadataWriterImpl` (in tests)

The new `KlibMetadataWriterImpl` class is the replacement for
the obsolete KLIB writing API.

In this commit, `KlibMetadataWriterImpl` is integrated into the KLIB
mock DSL.

^KT-81411
diff --git a/compiler/util-klib/src/org/jetbrains/kotlin/library/components/KlibMetadataComponent.kt b/compiler/util-klib/src/org/jetbrains/kotlin/library/components/KlibMetadataComponent.kt
index d75a95c..082e098 100644
--- a/compiler/util-klib/src/org/jetbrains/kotlin/library/components/KlibMetadataComponent.kt
+++ b/compiler/util-klib/src/org/jetbrains/kotlin/library/components/KlibMetadataComponent.kt
@@ -18,7 +18,7 @@
 import org.jetbrains.kotlin.metadata.ProtoBuf
 
 /**
- * This component provides access to Klib metadata.
+ * This component provides read access to Klib metadata.
  */
 interface KlibMetadataComponent : KlibComponent {
     /** The metadata header in the raw form (bytes, yet to be deserialized to [KlibMetadataProtoBuf.Header]). */
@@ -37,6 +37,8 @@
     get() = getComponent(KlibMetadataComponent.ID)
 
 class KlibMetadataComponentLayout(root: KlibFile) : KlibComponent.Layout(root) {
+    constructor(root: String) : this(KlibFile(root))
+
     /** The metadata directory. */
     val metadataDir: KlibFile
         get() = root.child(KLIB_DEFAULT_COMPONENT_NAME).child(KLIB_METADATA_FOLDER_NAME)
diff --git a/compiler/util-klib/src/org/jetbrains/kotlin/library/impl/KlibMetadataWriterImpl.kt b/compiler/util-klib/src/org/jetbrains/kotlin/library/impl/KlibMetadataWriterImpl.kt
new file mode 100644
index 0000000..dca9da3
--- /dev/null
+++ b/compiler/util-klib/src/org/jetbrains/kotlin/library/impl/KlibMetadataWriterImpl.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.impl
+
+import org.jetbrains.kotlin.library.SerializedMetadata
+import org.jetbrains.kotlin.library.components.KlibMetadataComponent
+import org.jetbrains.kotlin.library.components.KlibMetadataComponentLayout
+import org.jetbrains.kotlin.konan.file.File as KlibFile
+
+/**
+ * On the contrary to [KlibMetadataComponent], which provides read access to metadata,
+ * [KlibMetadataWriterImpl] provides allows writing the metadata to the file system.
+ */
+internal class KlibMetadataWriterImpl(private val layout: KlibMetadataComponentLayout) {
+    fun writeMetadata(serializedMetadata: SerializedMetadata) {
+        layout.metadataDir.mkdirs()
+
+        layout.moduleHeaderFile.writeBytes(serializedMetadata.module)
+
+        serializedMetadata.fragmentNames.forEachIndexed { index, packageFqName ->
+            val packageFragmentDir: KlibFile = layout.getPackageFragmentsDir(packageFqName)
+            packageFragmentDir.mkdirs()
+
+            val shortPackageName: String = packageFqName.substringAfterLast(".")
+            val packageFragmentParts: List<ByteArray> = serializedMetadata.fragments[index]
+
+            val padding: Int = packageFragmentParts.size.toString().length
+            fun withPadding(packageFragmentPartIndex: Int) = String.format("%0${padding}d", packageFragmentPartIndex)
+
+            packageFragmentParts.forEachIndexed { packageFragmentPartIndex, packageFragmentPart ->
+                layout.getPackageFragmentFile(
+                    packageFqName = packageFqName,
+                    partName = "${withPadding(packageFragmentPartIndex)}_$shortPackageName"
+                ).writeBytes(packageFragmentPart)
+            }
+        }
+    }
+}
diff --git a/compiler/util-klib/testFixtures/org/jetbrains/kotlin/library/KlibMockDSL.kt b/compiler/util-klib/testFixtures/org/jetbrains/kotlin/library/KlibMockDSL.kt
index 8620b63..97c4833 100644
--- a/compiler/util-klib/testFixtures/org/jetbrains/kotlin/library/KlibMockDSL.kt
+++ b/compiler/util-klib/testFixtures/org/jetbrains/kotlin/library/KlibMockDSL.kt
@@ -15,14 +15,12 @@
 import org.jetbrains.kotlin.library.IrKotlinLibraryLayout.Companion.IR_SIGNATURES_FILE_NAME
 import org.jetbrains.kotlin.library.IrKotlinLibraryLayout.Companion.IR_STRINGS_FILE_NAME
 import org.jetbrains.kotlin.library.IrKotlinLibraryLayout.Companion.IR_TYPES_FILE_NAME
+import org.jetbrains.kotlin.library.components.KlibMetadataComponentLayout
 import org.jetbrains.kotlin.library.components.KlibMetadataConstants.KLIB_METADATA_FOLDER_NAME
-import org.jetbrains.kotlin.library.components.KlibMetadataConstants.KLIB_MODULE_METADATA_FILE_NAME
-import org.jetbrains.kotlin.library.components.KlibMetadataConstants.KLIB_NONROOT_PACKAGE_FRAGMENT_FOLDER_PREFIX
-import org.jetbrains.kotlin.library.components.KlibMetadataConstants.KLIB_ROOT_PACKAGE_FRAGMENT_FOLDER_NAME
-import org.jetbrains.kotlin.library.components.KlibMetadataConstants.KLIB_METADATA_FILE_EXTENSION
 import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
 import org.jetbrains.kotlin.library.impl.IrArrayWriter
 import org.jetbrains.kotlin.library.impl.KLIB_DEFAULT_COMPONENT_NAME
+import org.jetbrains.kotlin.library.impl.KlibMetadataWriterImpl
 import java.io.File
 import kotlin.collections.map
 import kotlin.random.Random
@@ -164,29 +162,10 @@
 
 fun KlibMockDSL.metadata(init: KlibMockDSL.() -> Unit = {}): Unit = dir(KLIB_METADATA_FOLDER_NAME, init)
 
-// TODO (KT-81411): rewrite it on the new metadata component layout
-fun KlibMockDSL.metadata(metadata: SerializedMetadata) = metadata {
-    file(KLIB_MODULE_METADATA_FILE_NAME, metadata.module)
-
-    metadata.fragmentNames.forEachIndexed { index, packageName ->
-        val fragmentDirName = if (packageName == "")
-            KLIB_ROOT_PACKAGE_FRAGMENT_FOLDER_NAME
-        else
-            "$KLIB_NONROOT_PACKAGE_FRAGMENT_FOLDER_PREFIX$packageName"
-        val shortPackageName = packageName.substringAfterLast(".")
-
-        dir(fragmentDirName) {
-            val fragmentParts = metadata.fragments[index]
-
-            val padding = fragmentParts.size.toString().length
-            fun withPadding(fragmentPartIndex: Int) = String.format("%0${padding}d", fragmentPartIndex)
-
-            fragmentParts.forEachIndexed { fragmentPartIndex, fragmentPart ->
-                val fragmentPartFileName = "${withPadding(fragmentPartIndex)}_$shortPackageName.$KLIB_METADATA_FILE_EXTENSION"
-                file(fragmentPartFileName, fragmentPart)
-            }
-        }
-    }
+fun KlibMockDSL.metadata(metadata: SerializedMetadata) {
+    val layout = KlibMetadataComponentLayout(rootDir.path)
+    val writer = KlibMetadataWriterImpl(layout)
+    writer.writeMetadata(metadata)
 }
 
 fun KlibMockDSL.ir(init: KlibMockDSL.() -> Unit = {}): Unit = dir(KLIB_IR_FOLDER_NAME, init)