Skip to content

Commit 19d1ecb

Browse files
committed
convert public case classes in laika.rewrite
1 parent fc65b74 commit 19d1ecb

13 files changed

+273
-210
lines changed

core/shared/src/main/scala/laika/rewrite/TemplateRewriter.scala

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,32 @@ private[laika] object TemplateRewriter extends TemplateRewriter
153153
* the output of documents to certain target formats.
154154
* It is not always identical to the fileSuffix used for the specific format.
155155
*/
156-
case class OutputContext(fileSuffix: String, formatSelector: String)
156+
sealed abstract class OutputContext {
157+
158+
/** The suffix to be used for file names for this output format.
159+
*/
160+
def fileSuffix: String
161+
162+
/** Identifier that matches configured formats in `TargetFormats`,
163+
* used to filter content for specific output formats only.
164+
*
165+
* @return
166+
*/
167+
def formatSelector: String
168+
}
157169

158170
object OutputContext {
159-
def apply(format: String): OutputContext = apply(format, format)
171+
172+
private final case class Impl(fileSuffix: String, formatSelector: String)
173+
extends OutputContext
174+
175+
private[laika] def apply(fileSuffix: String, formatSelector: String): OutputContext =
176+
Impl(fileSuffix, formatSelector)
160177

161178
def apply(format: RenderFormat[_]): OutputContext =
162-
apply(format.fileSuffix, format.description.toLowerCase)
179+
Impl(format.fileSuffix, format.description.toLowerCase)
163180

164181
def apply(format: TwoPhaseRenderFormat[_, _]): OutputContext =
165-
apply(format.interimFormat.fileSuffix, format.description.toLowerCase)
182+
Impl(format.interimFormat.fileSuffix, format.description.toLowerCase)
166183

167184
}

core/shared/src/main/scala/laika/rewrite/Version.scala

Lines changed: 177 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package laika.rewrite
1818

19-
import cats.syntax.all._
19+
import cats.syntax.all.*
2020
import cats.data.NonEmptyChain
2121
import laika.ast.Path
2222
import laika.config.{
@@ -29,23 +29,57 @@ import laika.config.{
2929
}
3030

3131
/** Configuration for a single version of the documentation.
32-
*
33-
* @param displayValue the description of the version to use in any UI (e.g. version dropdowns)
34-
* @param pathSegment the string to use as a path segments in URLs pointing to this version
35-
* @param fallbackLink the link target to use when switching to this version from a page that does not exist in this version
36-
* @param label an optional label that will be used in the UI (e.g. `Dev` or `Stable`)
37-
* @param canonical indicates whether this is the canonical version
3832
*/
39-
case class Version(
40-
displayValue: String,
41-
pathSegment: String,
42-
fallbackLink: String = "index.html",
43-
label: Option[String] = None,
44-
canonical: Boolean = false
45-
)
33+
sealed abstract class Version {
34+
35+
/** The description of the version to use in any UI (for example in version dropdowns).
36+
*/
37+
def displayValue: String
38+
39+
/** The string to use as a path segments in URLs pointing to this version.
40+
*/
41+
def pathSegment: String
42+
43+
/** The link target to use when switching to this version from a page that does not exist in this version.
44+
*
45+
* Default: `/index.html`
46+
*/
47+
def fallbackLink: String
48+
49+
/** An optional label that will be used in the UI (e.g. `Dev` or `Stable`).
50+
*/
51+
def label: Option[String]
52+
53+
/** Indicates whether this is the canonical version.
54+
*
55+
* When using the Helium theme setting this flag results in canonical link references
56+
* getting inserted into the HTML `head` section of the generated output.
57+
*/
58+
def canonical: Boolean
59+
60+
def withFallbackLink(value: String): Version
61+
def withLabel(value: String): Version
62+
def setCanonical: Version
63+
}
4664

4765
object Version {
4866

67+
def apply(displayValue: String, pathSegment: String): Version =
68+
Impl(displayValue, pathSegment, "index.html", None, canonical = false)
69+
70+
private final case class Impl(
71+
displayValue: String,
72+
pathSegment: String,
73+
fallbackLink: String,
74+
label: Option[String],
75+
canonical: Boolean
76+
) extends Version {
77+
override def productPrefix = "Version"
78+
def withFallbackLink(value: String): Version = copy(fallbackLink = value)
79+
def withLabel(value: String): Version = copy(label = Some(value))
80+
def setCanonical: Version = copy(canonical = true)
81+
}
82+
4983
implicit val decoder: ConfigDecoder[Version] = ConfigDecoder.config.flatMap { config =>
5084
for {
5185
displayName <- config.get[String]("displayValue")
@@ -54,7 +88,7 @@ object Version {
5488
fallbackLink <- config.get[String]("fallbackLink", "index.html")
5589
label <- config.getOpt[String]("label")
5690
} yield {
57-
Version(displayName, pathSegment, fallbackLink, label, canonical)
91+
Impl(displayName, pathSegment, fallbackLink, label, canonical)
5892
}
5993
}
6094

@@ -73,51 +107,127 @@ object Version {
73107
/** Global configuration for versioned documentation.
74108
*
75109
* The order in the `Seq` properties will be used for any list views in the UI (e.g. for the version chooser dropdown).
76-
*
77-
* @param currentVersion the version that the sources of a transformation produce
78-
* @param olderVersions list of older versions that have previously been rendered (may be empty)
79-
* @param newerVersions list of newer versions that have previously been rendered (may be empty)
80-
* @param renderUnversioned indicates whether unversioned documents should be rendered
81-
* (setting this to false may be useful when re-rendering older versions)
82-
* @param scannerConfig optional configuration for scanning and indexing existing versions,
83-
* used by the Helium version switcher dropdown and by the preview server.
84110
*/
85-
case class Versions(
86-
currentVersion: Version,
87-
olderVersions: Seq[Version],
88-
newerVersions: Seq[Version] = Nil,
89-
renderUnversioned: Boolean = true,
90-
scannerConfig: Option[VersionScannerConfig] = None
91-
) {
111+
sealed abstract class Versions {
92112

93-
lazy val allVersions: Seq[Version] = newerVersions ++: currentVersion +: olderVersions
113+
/** The version that the sources of a transformation produce. */
114+
def currentVersion: Version
115+
116+
/** List of older versions that have previously been rendered (may be empty). */
117+
def olderVersions: Seq[Version]
118+
119+
/** List of newer versions that have previously been rendered (may be empty). */
120+
def newerVersions: Seq[Version]
121+
122+
/** Indicates whether unversioned documents should be rendered
123+
* (setting this to false may be useful when re-rendering older versions).
124+
*/
125+
def renderUnversioned: Boolean
126+
127+
/** Optional configuration for scanning and indexing existing versions,
128+
* used by the Helium version switcher dropdown and by the preview server..
129+
*/
130+
def scannerConfig: Option[VersionScannerConfig]
94131

95-
/** Configures the version scanner to use during transformations.
96-
* These settings enable scanning and indexing existing versions during a transformation,
132+
/** Specifies an absolute file path that points to a directory containing previously
133+
* rendered Laika output.
134+
* This enables scanning and indexing existing versions during a transformation,
97135
* used by the Helium version switcher dropdown and by the preview server.
136+
*
137+
* This is optional, without infos about existing versions, the menu will simply switch
138+
* to the landing page of the respective versions.
139+
*
140+
* See [[VersionScannerConfig]] for details.
141+
*
142+
* @param rootDirectory the directory to scan
143+
* @param exclude virtual paths inside that directory that should be ignored
98144
*/
99-
def withVersionScanner(rootDirectory: String, exclude: Seq[Path]): Versions =
100-
copy(scannerConfig = Some(VersionScannerConfig(rootDirectory, exclude)))
145+
def withVersionScanner(rootDirectory: String, exclude: Seq[Path] = Nil): Versions
101146

102147
/** Validates this configuration instance and either returns a `Left` with a
103148
* list of errors encountered or a `Right` containing this instance.
104149
*/
105-
def validated: Either[NonEmptyChain[String], Versions] = {
150+
def validated: Either[NonEmptyChain[String], Versions]
151+
152+
def withNewerVersions(versions: Version*): Versions
153+
def withOlderVersions(versions: Version*): Versions
154+
155+
def withRenderUnversioned(value: Boolean): Versions
156+
157+
lazy val allVersions: Seq[Version] = newerVersions ++: currentVersion +: olderVersions
158+
}
159+
160+
object Versions {
161+
162+
def forCurrentVersion(version: Version): Versions =
163+
Impl(version, Nil, Nil, renderUnversioned = true, None)
164+
165+
private final case class Impl(
166+
currentVersion: Version,
167+
olderVersions: Seq[Version],
168+
newerVersions: Seq[Version] = Nil,
169+
renderUnversioned: Boolean = true,
170+
scannerConfig: Option[VersionScannerConfig] = None
171+
) extends Versions {
172+
173+
override def productPrefix = "Versions"
174+
175+
def withVersionScanner(rootDirectory: String, exclude: Seq[Path] = Nil): Versions =
176+
copy(scannerConfig = Some(VersionScannerConfig(rootDirectory, exclude)))
106177

107-
val dupSegments = allVersions.groupBy(_.pathSegment).filter(_._2.size > 1).keys.toList.sorted
108-
val dupSegmentsMsg =
109-
if (dupSegments.isEmpty) None
110-
else Some(s"Path segments used for more than one version: ${dupSegments.mkString(", ")}")
178+
def validated: Either[NonEmptyChain[String], Versions] = {
111179

112-
val dupCanonical = allVersions.filter(_.canonical).map(_.displayValue).toList.sorted
113-
val dupCanonicalMsg =
114-
if (dupCanonical.size < 2) None
115-
else Some(s"More than one version marked as canonical: ${dupCanonical.mkString(", ")}")
180+
val dupSegments = allVersions.groupBy(_.pathSegment).filter(_._2.size > 1).keys.toList.sorted
181+
val dupSegmentsMsg =
182+
if (dupSegments.isEmpty) None
183+
else Some(s"Path segments used for more than one version: ${dupSegments.mkString(", ")}")
116184

117-
NonEmptyChain.fromSeq(dupSegmentsMsg.toList ++ dupCanonicalMsg.toList) match {
118-
case Some(chain) => Left(chain)
119-
case None => Right(this)
185+
val dupCanonical = allVersions.filter(_.canonical).map(_.displayValue).toList.sorted
186+
val dupCanonicalMsg =
187+
if (dupCanonical.size < 2) None
188+
else Some(s"More than one version marked as canonical: ${dupCanonical.mkString(", ")}")
189+
190+
NonEmptyChain.fromSeq(dupSegmentsMsg.toList ++ dupCanonicalMsg.toList) match {
191+
case Some(chain) => Left(chain)
192+
case None => Right(this)
193+
}
120194
}
195+
196+
def withNewerVersions(versions: Version*): Versions = copy(newerVersions = versions)
197+
198+
def withOlderVersions(versions: Version*): Versions = copy(olderVersions = versions)
199+
200+
def withRenderUnversioned(value: Boolean): Versions = copy(renderUnversioned = value)
201+
}
202+
203+
implicit val key: DefaultKey[Versions] = DefaultKey(LaikaKeys.versions)
204+
205+
implicit val decoder: ConfigDecoder[Versions] = ConfigDecoder.config.flatMap { config =>
206+
for {
207+
currentVersion <- config.get[Version]("currentVersion")
208+
olderVersions <- config.get[Seq[Version]]("olderVersions", Nil)
209+
newerVersions <- config.get[Seq[Version]]("newerVersions", Nil)
210+
renderUnversioned <- config.get[Boolean]("renderUnversioned", false)
211+
versionScanner <- config.getOpt[VersionScannerConfig]("scannerConfig")
212+
result <- Impl(
213+
currentVersion,
214+
olderVersions,
215+
newerVersions,
216+
renderUnversioned,
217+
versionScanner
218+
)
219+
.validated.leftMap(err => ConfigErrors(err.map(ValidationError(_))))
220+
} yield result
221+
}
222+
223+
implicit val encoder: ConfigEncoder[Versions] = ConfigEncoder[Versions] { versions =>
224+
ConfigEncoder.ObjectBuilder.empty
225+
.withValue("currentVersion", versions.currentVersion)
226+
.withValue("olderVersions", versions.olderVersions)
227+
.withValue("newerVersions", versions.newerVersions)
228+
.withValue("renderUnversioned", versions.renderUnversioned)
229+
.withValue("scannerConfig", versions.scannerConfig)
230+
.build
121231
}
122232

123233
}
@@ -138,22 +248,36 @@ case class Versions(
138248
* The specified root directory is expected to match the structure of versioned documentation as rendered by Laika.
139249
* This means that the root directory is expected to have immediate sub-directories with names that correspond
140250
* to the `pathSegment` property of the configuration for that version.
141-
*
142-
* @param rootDirectory file system path that represents the root of existing versions.
143-
* @param exclude paths to be skipped when scanning the output directory for existing versions (e.g. for API docs),
144-
* interpreted from the root directory of each version.
145251
*/
146-
case class VersionScannerConfig(rootDirectory: String, exclude: Seq[Path] = Nil)
252+
sealed abstract class VersionScannerConfig {
253+
254+
/** File system path that represents the root of existing versions.
255+
*/
256+
def rootDirectory: String
257+
258+
/** Paths to be skipped when scanning the output directory for existing versions (for example for API docs),
259+
* interpreted from the root directory of each version.
260+
*/
261+
def exclude: Seq[Path]
262+
263+
}
147264

148265
object VersionScannerConfig {
149266

267+
def apply(rootDirectory: String, exclude: Seq[Path] = Nil): VersionScannerConfig =
268+
Impl(rootDirectory, exclude)
269+
270+
private final case class Impl(rootDirectory: String, exclude: Seq[Path] = Nil) extends VersionScannerConfig {
271+
override def productPrefix = "VersionScannerConfig"
272+
}
273+
150274
implicit val decoder: ConfigDecoder[VersionScannerConfig] = ConfigDecoder.config.flatMap {
151275
config =>
152276
for {
153277
rootDirectory <- config.get[String]("rootDirectory")
154278
exclude <- config.get[Seq[Path]]("exclude", Nil)
155279
} yield {
156-
VersionScannerConfig(rootDirectory, exclude)
280+
Impl(rootDirectory, exclude)
157281
}
158282
}
159283

@@ -166,37 +290,3 @@ object VersionScannerConfig {
166290
}
167291

168292
}
169-
170-
object Versions {
171-
172-
implicit val key: DefaultKey[Versions] = DefaultKey(LaikaKeys.versions)
173-
174-
implicit val decoder: ConfigDecoder[Versions] = ConfigDecoder.config.flatMap { config =>
175-
for {
176-
currentVersion <- config.get[Version]("currentVersion")
177-
olderVersions <- config.get[Seq[Version]]("olderVersions", Nil)
178-
newerVersions <- config.get[Seq[Version]]("newerVersions", Nil)
179-
renderUnversioned <- config.get[Boolean]("renderUnversioned", false)
180-
versionScanner <- config.getOpt[VersionScannerConfig]("scannerConfig")
181-
result <- Versions(
182-
currentVersion,
183-
olderVersions,
184-
newerVersions,
185-
renderUnversioned,
186-
versionScanner
187-
)
188-
.validated.leftMap(err => ConfigErrors(err.map(ValidationError(_))))
189-
} yield result
190-
}
191-
192-
implicit val encoder: ConfigEncoder[Versions] = ConfigEncoder[Versions] { versions =>
193-
ConfigEncoder.ObjectBuilder.empty
194-
.withValue("currentVersion", versions.currentVersion)
195-
.withValue("olderVersions", versions.olderVersions)
196-
.withValue("newerVersions", versions.newerVersions)
197-
.withValue("renderUnversioned", versions.renderUnversioned)
198-
.withValue("scannerConfig", versions.scannerConfig)
199-
.build
200-
}
201-
202-
}

0 commit comments

Comments
 (0)