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

com.mobimeo.gtfs.file.GtfsFile.scala Maven / Gradle / Ivy

There is a newer version: 0.5.0
Show newest version
/*
 * Copyright 2021 Mobimeo GmbH
 *
 * 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 com.mobimeo.gtfs.file

import cats.effect._
import cats.syntax.all._

import com.mobimeo.gtfs._

import fs2._
import fs2.io.file._
import fs2.data.csv._
import fs2.data.csv.lowlevel._

import scala.jdk.CollectionConverters._

import java.nio.file.{FileSystem, FileSystems}

import java.net.URI

/** Represents a GTFS file. Can be used to access the content of the different files in it.
  *
  * Use the smart constructor in the companion object to acquire a `Resource` over a GTFS file. The file will be closed
  * once the resource is released.
  */
class GtfsFile[F[_]] private (val file: Path, fs: FileSystem)(implicit F: Sync[F], files: Files[F])
    extends Gtfs[F, CsvRowDecoder[*, String], CsvRowEncoder[*, String]] {
  self =>

  object has extends GtfsHas[F] {
    def file(name: String): F[Boolean] =
      files.exists(Path.fromNioPath(fs.getPath(s"/$name")))
  }

  object delete extends GtfsDelete[F] {
    def file(name: String): F[Unit] =
      files.deleteIfExists(Path.fromNioPath(fs.getPath(s"/$name"))).void
  }

  object read extends GtfsRead[F, CsvRowDecoder[*, String]] {

    /** Gives access to the raw content of CSV file `name`.
      *
      * For instance `rawFile("calendar.txt")`.
      */
    def rawFile(name: String): Stream[F, CsvRow[String]] =
      Stream.force(has.file(name).map { exists =>
        if (exists)
          files
            .readAll(Path.fromNioPath(fs.getPath(s"/$name")), 1024, Flags.Read)
            .through(text.utf8.decode)
            .through(rows())
            .through(headers[F, String])
        else
          Stream.empty
      })

    /** Gives access to the raw content of CSV file `name`. */
    def rawFile(name: StandardName): Stream[F, CsvRow[String]] =
      rawFile(name.entryName)

    def file[R](name: String)(implicit decoder: CsvRowDecoder[R, String]): Stream[F, R] =
      rawFile(name).through(decodeRow)

    def rawStops: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Stops)

    def rawRoutes: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Routes)

    def rawTrips: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Trips)

    def rawStopTimes: Stream[F, CsvRow[String]] =
      rawFile(StandardName.StopTimes)

    def rawAgencies: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Agency)

    def rawCalendar: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Calendar)

    def rawCalendarDates: Stream[F, CsvRow[String]] =
      rawFile(StandardName.CalendarDates)

    def rawFareAttributes: Stream[F, CsvRow[String]] =
      rawFile(StandardName.FareAttributes)

    def rawFareRules: Stream[F, CsvRow[String]] =
      rawFile(StandardName.FareRules)

    def rawShapes: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Shapes)

    def rawFrequencies: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Frequencies)

    def rawTransfers: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Transfers)

    def rawPathways: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Pathways)

    def rawLevels: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Levels)

    def rawFeedInfo: Stream[F, CsvRow[String]] =
      rawFile(StandardName.FeedInfo)

    def rawTranslations: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Translations)

    def rawAttributions: Stream[F, CsvRow[String]] =
      rawFile(StandardName.Attributions)

  }

  object write extends GtfsWrite[F, CsvRowEncoder[*, String]] {

    /** Gives access to the pipe to save in file `name`.
      *
      * For instance `rawFile("agency.txt")`.
      */
    def rawFile(name: String): Pipe[F, CsvRow[String], Nothing] =
      s =>
        Stream
          .resource(
            files.tempFile
          )
          .flatMap { tempFile =>
            // save the rows in the temp file first
            s.through(encodeRowWithFirstHeaders)
              .through(toRowStrings())
              .through(text.utf8.encode)
              .through(files.writeAll(tempFile)) ++
              // once temp file is saved, copy it to the destination file in GTFS
              Stream.exec(
                files.copy(tempFile, Path.fromNioPath(fs.getPath(s"/$name")), CopyFlags(CopyFlag.ReplaceExisting)).void
              )
          }

    /** Gives access to the pipe to save in file `name`. */
    def rawFile(name: StandardName): Pipe[F, CsvRow[String], Nothing] =
      rawFile(name.entryName)

    def file[T](name: String)(implicit encoder: CsvRowEncoder[T, String]): Pipe[F, T, Nothing] =
      _.through(encodeRow).through(rawFile(name))

    def rawStops: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Stops)

    def rawRoutes: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Routes)

    def rawTrips: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Trips)

    def rawStopTimes: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.StopTimes)

    def rawAgencies: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Agency)

    def rawCalendar: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Calendar)

    def rawCalendarDates: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.CalendarDates)

    def rawFareAttributes: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.FareAttributes)

    def rawFareRules: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.FareRules)

    def rawShapes: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Shapes)

    def rawFrequencies: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Frequencies)

    def rawTransfers: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Transfers)

    def rawPathways: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Pathways)

    def rawLevels: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Levels)

    def rawFeedInfo: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.FeedInfo)

    def rawTranslations: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Translations)

    def rawAttributions: Pipe[F, CsvRow[String], Nothing] =
      rawFile(StandardName.Attributions)

  }

  /** Creates a GTFS target which originally consists of the content of this one. The file is copied when the resource
    * is acquired.
    *
    * This can be used when the result of transforming this GTFS file content is toRowStrings to be saved to a new file.
    */
  def copyTo(file: Path, flags: CopyFlags = CopyFlags.empty): Resource[F, GtfsFile[F]] =
    Resource.eval(files.copy(self.file, file, flags)) >> GtfsFile(file)

}

object GtfsFile {

  private[gtfs] def makeFs[F[_]](
      file: Path,
      create: Boolean
  )(implicit F: Sync[F], files: Files[F]): Resource[F, FileSystem] =
    Resource.make(
      files.exists(file).flatMap { exists =>
        F.blocking(
          FileSystems
            .newFileSystem(
              URI.create("jar:file:" + file.absolute),
              Map("create" -> String.valueOf(create && !exists)).asJava
            )
        )
      }
    )(fs => F.blocking(fs.close()))

  /** Creates a GTFS object, giving access to all files within the GTFS file. */
  def apply[F[_]](file: Path, create: Boolean = false)(implicit F: Sync[F], files: Files[F]): Resource[F, GtfsFile[F]] =
    makeFs(file, create).map(new GtfsFile(file, _))

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy