All Downloads are FREE. Search and download functionalities are using the official Maven repository.

laika.io.runtime.DirectoryScanner.scala Maven / Gradle / Ivy

/*
 * Copyright 2012-2020 the original author or authors.
 *
 * 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
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package laika.io.runtime

import cats.effect.Sync
import cats.effect.kernel.Async
import cats.implicits._
import fs2.io.file.Files
import laika.ast.DocumentType.Static
import laika.ast.{Path, TextDocumentType}
import laika.io.model._

import scala.io.Codec

/** Scans a directory in the file system and transforms it into a generic InputCollection
  * that can serve as input for parallel parsers or transformers.
  * 
  * @author Jens Halm
  */
object DirectoryScanner {

  /** Scans the specified directory passing all child paths to the given function.
    */
  def scanDirectory[F[_]: Async, A] (directory: FilePath)(f: Seq[FilePath] => F[A]): F[A] = 
    fs2.io.file.Files[F]
      .list(directory.toFS2Path)
      .map(FilePath.fromFS2Path)
      .compile
      .toList
      .flatMap(f)
  
  /** Scans the specified directory and transforms it into a generic InputCollection.
    */
  def scanDirectories[F[_]: Async] (input: DirectoryInput): F[InputTree[F]] = {
    join(input.directories.map(d => scanDirectory[F, InputTree[F]](d)(asInputCollection(input.mountPoint, input))))
      .map(_.copy(sourcePaths = input.directories))
  }
  
  private def join[F[_]: Sync] (collections: Seq[F[InputTree[F]]]): F[InputTree[F]] = collections
    .toVector
    .sequence
    .map(_.reduceLeftOption(_ ++ _).getOrElse(InputTree.empty))

  private def asInputCollection[F[_]: Async] (path: Path, input: DirectoryInput)(entries: Seq[FilePath]): F[InputTree[F]] = {

    def toCollection (filePath: FilePath): F[InputTree[F]] = {

      val childPath = path / filePath.name
      implicit val codec: Codec = input.codec

      input.fileFilter.filter(filePath).ifM(
        InputTree.empty[F].pure[F],
        Files[F].isDirectory(filePath.toFS2Path).ifM(
          scanDirectory(filePath)(asInputCollection(childPath, input)),
          input.docTypeMatcher(childPath) match {
            case docType: TextDocumentType => InputTree[F](Seq(TextInput.fromFile(filePath, childPath, docType)), Nil, Nil).pure[F]
            case Static(formats)           => InputTree[F](Nil, Seq(BinaryInput.fromFile(filePath, childPath, formats)), Nil).pure[F]
            case _                         => InputTree.empty[F].pure[F]
          }
        )
      )
    }

    join(entries.map(toCollection))
  }
  
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy