diff --git a/.scalafix.conf b/.scalafix.conf index a3edab9..681759e 100644 --- a/.scalafix.conf +++ b/.scalafix.conf @@ -1,8 +1,15 @@ rules = [ + # Built-in syntactic rules DisableSyntax LeakingImplicitClassVal NoValInForComprehension RedundantSyntax + ProcedureSyntax + # Built-in semantic rules + ExplicitResultTypes + NoAutoTupling + OrganizeImports + RemoveUnused ] DisableSyntax.noVars = true @@ -19,4 +26,4 @@ DisableSyntax.noFinalize = true DisableSyntax.noValPatterns = true DisableSyntax.noUniversalEquality = false OrganizeImports.targetDialect = Scala3 -OrganizeImports.removeUnused = false \ No newline at end of file +OrganizeImports.removeUnused = false diff --git a/build.sbt b/build.sbt index fdc2d98..67bcb8c 100644 --- a/build.sbt +++ b/build.sbt @@ -51,7 +51,7 @@ lazy val root = (project in file(".")) Compile / console / scalacOptions --= Seq("-Xfatal-warnings"), Test / scalacOptions --= Seq("-Wnonunit-statement"), mimaPreviousArtifacts := Set("info.fingo" %% "spata" % "3.2.0"), - semanticdbEnabled := false, + semanticdbEnabled := true, autoAPIMappings := true ) diff --git a/src/main/scala/info/fingo/spata/CSVConfig.scala b/src/main/scala/info/fingo/spata/CSVConfig.scala index 39a5fbc..f6eb28e 100644 --- a/src/main/scala/info/fingo/spata/CSVConfig.scala +++ b/src/main/scala/info/fingo/spata/CSVConfig.scala @@ -7,6 +7,7 @@ package info.fingo.spata import cats.effect.Sync import info.fingo.spata.util.Logger + import scala.annotation.targetName /** CSV configuration used to create [[CSVParser]] or [[CSVRenderer]]. diff --git a/src/main/scala/info/fingo/spata/CSVParser.scala b/src/main/scala/info/fingo/spata/CSVParser.scala index 0113b6e..bd09c8a 100644 --- a/src/main/scala/info/fingo/spata/CSVParser.scala +++ b/src/main/scala/info/fingo/spata/CSVParser.scala @@ -5,15 +5,24 @@ */ package info.fingo.spata -import scala.util.Try -import cats.effect.{Async => CEAsync, Sync} -import fs2.{text, Pipe, Pull, Stream} -import info.fingo.spata.parser.{CharParser, FieldParser, RecordParser} -import info.fingo.spata.parser.RecordParser.RecordResult +import cats.effect.Async as CEAsync +import cats.effect.Sync +import fs2.Pipe +import fs2.Pull +import fs2.Stream +import fs2.text import info.fingo.spata.CSVParser.Callback -import info.fingo.spata.error.{CSVException, ParsingErrorCode, StructureException} +import info.fingo.spata.error.CSVException +import info.fingo.spata.error.ParsingErrorCode +import info.fingo.spata.error.StructureException +import info.fingo.spata.parser.CharParser +import info.fingo.spata.parser.FieldParser +import info.fingo.spata.parser.RecordParser +import info.fingo.spata.parser.RecordParser.RecordResult import info.fingo.spata.util.Logger +import scala.util.Try + /** A utility for parsing comma-separated values (CSV) sources. * The source is assumed to be [[https://tools.ietf.org/html/rfc4180 RFC 4180]] conform, * although some aspects of its format are configurable. diff --git a/src/main/scala/info/fingo/spata/CSVRenderer.scala b/src/main/scala/info/fingo/spata/CSVRenderer.scala index 4e051f2..54ca2d9 100644 --- a/src/main/scala/info/fingo/spata/CSVRenderer.scala +++ b/src/main/scala/info/fingo/spata/CSVRenderer.scala @@ -5,8 +5,14 @@ */ package info.fingo.spata -import fs2.{text, Chunk, Pipe, Pull, RaiseThrowable, Stream} -import info.fingo.spata.error.{FieldInfo, HeaderError} +import fs2.Chunk +import fs2.Pipe +import fs2.Pull +import fs2.RaiseThrowable +import fs2.Stream +import fs2.text +import info.fingo.spata.error.FieldInfo +import info.fingo.spata.error.HeaderError /** A utility for rendering data to CSV representation. * diff --git a/src/main/scala/info/fingo/spata/Content.scala b/src/main/scala/info/fingo/spata/Content.scala index f5bb6bc..370fadb 100644 --- a/src/main/scala/info/fingo/spata/Content.scala +++ b/src/main/scala/info/fingo/spata/Content.scala @@ -7,7 +7,8 @@ package info.fingo.spata import cats.effect.Sync import fs2.Stream -import info.fingo.spata.error.{FieldInfo, StructureException} +import info.fingo.spata.error.FieldInfo +import info.fingo.spata.error.StructureException import info.fingo.spata.parser.RecordParser.* import info.fingo.spata.util.Logger diff --git a/src/main/scala/info/fingo/spata/Header.scala b/src/main/scala/info/fingo/spata/Header.scala index 01a40e5..3e7c367 100644 --- a/src/main/scala/info/fingo/spata/Header.scala +++ b/src/main/scala/info/fingo/spata/Header.scala @@ -5,7 +5,9 @@ */ package info.fingo.spata -import info.fingo.spata.error.{FieldInfo, ParsingErrorCode, StructureException} +import info.fingo.spata.error.FieldInfo +import info.fingo.spata.error.ParsingErrorCode +import info.fingo.spata.error.StructureException /** CSV header with names of each field. * diff --git a/src/main/scala/info/fingo/spata/Record.scala b/src/main/scala/info/fingo/spata/Record.scala index 1d06982..f8c2e74 100644 --- a/src/main/scala/info/fingo/spata/Record.scala +++ b/src/main/scala/info/fingo/spata/Record.scala @@ -5,10 +5,17 @@ */ package info.fingo.spata -import java.util.NoSuchElementException -import info.fingo.spata.converter.{FromProduct, FromTuple, ToProduct, ToTuple} +import info.fingo.spata.converter.FromProduct +import info.fingo.spata.converter.FromTuple +import info.fingo.spata.converter.ToProduct +import info.fingo.spata.converter.ToTuple import info.fingo.spata.error.* -import info.fingo.spata.text.{FormattedStringParser, ParseResult, StringParser, StringRenderer} +import info.fingo.spata.text.FormattedStringParser +import info.fingo.spata.text.ParseResult +import info.fingo.spata.text.StringParser +import info.fingo.spata.text.StringRenderer + +import java.util.NoSuchElementException /** CSV record representation. * A record is basically a map from string to string. diff --git a/src/main/scala/info/fingo/spata/converter/FromProduct.scala b/src/main/scala/info/fingo/spata/converter/FromProduct.scala index bc3944b..cf63a4e 100644 --- a/src/main/scala/info/fingo/spata/converter/FromProduct.scala +++ b/src/main/scala/info/fingo/spata/converter/FromProduct.scala @@ -5,8 +5,10 @@ */ package info.fingo.spata.converter +import info.fingo.spata.Header +import info.fingo.spata.Record import info.fingo.spata.text.StringRenderer -import info.fingo.spata.{Header, Record} + import scala.deriving.Mirror /** Converter from a tuple to a record. diff --git a/src/main/scala/info/fingo/spata/converter/ToProduct.scala b/src/main/scala/info/fingo/spata/converter/ToProduct.scala index 8603adf..5de2575 100644 --- a/src/main/scala/info/fingo/spata/converter/ToProduct.scala +++ b/src/main/scala/info/fingo/spata/converter/ToProduct.scala @@ -5,12 +5,15 @@ */ package info.fingo.spata.converter -import scala.deriving.Mirror -import scala.compiletime.{constValue, erasedValue, summonInline} import info.fingo.spata.Decoded import info.fingo.spata.Record import info.fingo.spata.text.StringParser +import scala.compiletime.constValue +import scala.compiletime.erasedValue +import scala.compiletime.summonInline +import scala.deriving.Mirror + /** Converter from a record to a tuple. * * This trait defines behavior to be implemented by concrete, given converters. diff --git a/src/main/scala/info/fingo/spata/error/CSVException.scala b/src/main/scala/info/fingo/spata/error/CSVException.scala index 093e03e..79717c9 100644 --- a/src/main/scala/info/fingo/spata/error/CSVException.scala +++ b/src/main/scala/info/fingo/spata/error/CSVException.scala @@ -5,11 +5,13 @@ */ package info.fingo.spata.error -import java.util.NoSuchElementException -import ParsingErrorCode.ErrorCode import info.fingo.spata.Position import info.fingo.spata.text.StringParser +import java.util.NoSuchElementException + +import ParsingErrorCode.ErrorCode + /** Base exception reported by CSV handling methods. * May be thrown, raised through [[fs2.Stream#raiseError]] or returned as `Left`, depending on context. * diff --git a/src/main/scala/info/fingo/spata/io/Reader.scala b/src/main/scala/info/fingo/spata/io/Reader.scala index beec3df..7a08e60 100644 --- a/src/main/scala/info/fingo/spata/io/Reader.scala +++ b/src/main/scala/info/fingo/spata/io/Reader.scala @@ -5,15 +5,26 @@ */ package info.fingo.spata.io -import java.io.InputStream -import java.nio.file.{Files, Path, StandardOpenOption} -import scala.io.{BufferedSource, Codec, Source} -import cats.effect.{Async, Sync} +import cats.effect.Async +import cats.effect.Sync import cats.syntax.all.* -import fs2.io.file.{Files => FFiles, Flags, Path => FPath} -import fs2.{io, text, Pipe, Stream} -import info.fingo.spata.util.Logger +import fs2.Pipe import fs2.RaiseThrowable +import fs2.Stream +import fs2.io +import fs2.io.file.Files as FFiles +import fs2.io.file.Flags +import fs2.io.file.Path as FPath +import fs2.text +import info.fingo.spata.util.Logger + +import java.io.InputStream +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import scala.io.BufferedSource +import scala.io.Codec +import scala.io.Source /** Reader interface with reading operations from various sources. * The I/O operations are wrapped in effect `F` (e.g. [[cats.effect.IO]]), allowing deferred computation. diff --git a/src/main/scala/info/fingo/spata/io/Writer.scala b/src/main/scala/info/fingo/spata/io/Writer.scala index 5889989..067d364 100644 --- a/src/main/scala/info/fingo/spata/io/Writer.scala +++ b/src/main/scala/info/fingo/spata/io/Writer.scala @@ -5,15 +5,25 @@ */ package info.fingo.spata.io -import java.io.OutputStream -import java.nio.file.{Files, Path, StandardOpenOption} -import scala.io.Codec -import cats.effect.{Async, Sync} +import cats.effect.Async +import cats.effect.Sync import cats.syntax.all.* -import fs2.{io, text, Chunk, Pipe, Stream} -import fs2.io.file.{Files => FFiles, Flags, Path => FPath} +import fs2.Chunk +import fs2.Pipe +import fs2.Stream +import fs2.io +import fs2.io.file.Files as FFiles +import fs2.io.file.Flags +import fs2.io.file.Path as FPath +import fs2.text import info.fingo.spata.util.Logger +import java.io.OutputStream +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import scala.io.Codec + /** Writer interface with writing operations to various destinations. * The I/O operations are wrapped in effect `F` (e.g. [[cats.effect.IO]]), allowing deferred computation. * diff --git a/src/main/scala/info/fingo/spata/parser/CharParser.scala b/src/main/scala/info/fingo/spata/parser/CharParser.scala index 6503bd2..fc22e3e 100644 --- a/src/main/scala/info/fingo/spata/parser/CharParser.scala +++ b/src/main/scala/info/fingo/spata/parser/CharParser.scala @@ -5,7 +5,10 @@ */ package info.fingo.spata.parser -import fs2.{Chunk, Pipe, Pull, Stream} +import fs2.Chunk +import fs2.Pipe +import fs2.Pull +import fs2.Stream import info.fingo.spata.error.ParsingErrorCode.* /* A finite-state transducer to converter plain source characters into context-dependent symbols, diff --git a/src/main/scala/info/fingo/spata/parser/ChunkAwareParser.scala b/src/main/scala/info/fingo/spata/parser/ChunkAwareParser.scala index 308435b..f9fbbe4 100644 --- a/src/main/scala/info/fingo/spata/parser/ChunkAwareParser.scala +++ b/src/main/scala/info/fingo/spata/parser/ChunkAwareParser.scala @@ -5,7 +5,10 @@ */ package info.fingo.spata.parser -import fs2.{Chunk, Pipe, Pull, Stream} +import fs2.Chunk +import fs2.Pipe +import fs2.Pull +import fs2.Stream /* Trait for state carriers which shows if processing has been finished */ private[spata] trait State: diff --git a/src/main/scala/info/fingo/spata/parser/FieldParser.scala b/src/main/scala/info/fingo/spata/parser/FieldParser.scala index b76fbbb..bbbcb70 100644 --- a/src/main/scala/info/fingo/spata/parser/FieldParser.scala +++ b/src/main/scala/info/fingo/spata/parser/FieldParser.scala @@ -5,9 +5,12 @@ */ package info.fingo.spata.parser -import scala.annotation.tailrec -import fs2.{Chunk, Pipe} +import fs2.Chunk +import fs2.Pipe import info.fingo.spata.error.ParsingErrorCode.* + +import scala.annotation.tailrec + import FieldParser.* /* Carrier for counters, partial field content and information about finished parsing. */ diff --git a/src/main/scala/info/fingo/spata/parser/RecordParser.scala b/src/main/scala/info/fingo/spata/parser/RecordParser.scala index afadf14..ccc99a2 100644 --- a/src/main/scala/info/fingo/spata/parser/RecordParser.scala +++ b/src/main/scala/info/fingo/spata/parser/RecordParser.scala @@ -5,9 +5,12 @@ */ package info.fingo.spata.parser -import scala.collection.immutable.VectorBuilder +import fs2.Chunk +import fs2.Pipe + import scala.annotation.tailrec -import fs2.{Chunk, Pipe} +import scala.collection.immutable.VectorBuilder + import RecordParser.* import FieldParser.* diff --git a/src/main/scala/info/fingo/spata/schema/CSVSchema.scala b/src/main/scala/info/fingo/spata/schema/CSVSchema.scala index 8d8e549..66eee6a 100644 --- a/src/main/scala/info/fingo/spata/schema/CSVSchema.scala +++ b/src/main/scala/info/fingo/spata/schema/CSVSchema.scala @@ -5,17 +5,22 @@ */ package info.fingo.spata.schema -import scala.annotation.unused -import scala.reflect.{classTag, ClassTag} -import cats.data.{NonEmptyList, Validated} -import cats.data.Validated.{Invalid, Valid} -import fs2.{Pipe, Stream} +import cats.data.NonEmptyList +import cats.data.Validated +import cats.data.Validated.Invalid +import cats.data.Validated.Valid +import fs2.Pipe +import fs2.Stream import info.fingo.spata.Record import info.fingo.spata.schema.error.TypeError import info.fingo.spata.schema.validator.Validator import info.fingo.spata.text.StringParser import info.fingo.spata.util.Logger +import scala.annotation.unused +import scala.reflect.ClassTag +import scala.reflect.classTag + /** CSV schema definition and validation utility. * * Schema declares fields which are expected in CSV stream - their names and types. diff --git a/src/main/scala/info/fingo/spata/schema/TypedRecord.scala b/src/main/scala/info/fingo/spata/schema/TypedRecord.scala index 7323328..5ba0668 100644 --- a/src/main/scala/info/fingo/spata/schema/TypedRecord.scala +++ b/src/main/scala/info/fingo/spata/schema/TypedRecord.scala @@ -5,12 +5,13 @@ */ package info.fingo.spata.schema -import scala.deriving.Mirror -import scala.annotation.unused -import scala.compiletime.{constValue, erasedValue} import info.fingo.spata.converter.ToProduct import info.fingo.spata.schema.TypedRecord.* +import scala.annotation.unused +import scala.compiletime.constValue +import scala.deriving.Mirror + /** CSV record representation with type-safe access to its values. * Typed records are created as result of schema validation. * diff --git a/src/main/scala/info/fingo/spata/schema/error/SchemaError.scala b/src/main/scala/info/fingo/spata/schema/error/SchemaError.scala index bf8c2e7..5619852 100644 --- a/src/main/scala/info/fingo/spata/schema/error/SchemaError.scala +++ b/src/main/scala/info/fingo/spata/schema/error/SchemaError.scala @@ -5,7 +5,10 @@ */ package info.fingo.spata.schema.error -import info.fingo.spata.error.{ContentError, DataError, HeaderError, IndexError} +import info.fingo.spata.error.ContentError +import info.fingo.spata.error.DataError +import info.fingo.spata.error.HeaderError +import info.fingo.spata.error.IndexError /** Error for schema validation. */ sealed trait SchemaError: diff --git a/src/main/scala/info/fingo/spata/schema/validator/Validator.scala b/src/main/scala/info/fingo/spata/schema/validator/Validator.scala index 4548c14..1eba745 100644 --- a/src/main/scala/info/fingo/spata/schema/validator/Validator.scala +++ b/src/main/scala/info/fingo/spata/schema/validator/Validator.scala @@ -5,12 +5,14 @@ */ package info.fingo.spata.schema.validator -import scala.util.matching.Regex import cats.data.Validated -import cats.data.Validated.{Invalid, Valid} +import cats.data.Validated.Invalid +import cats.data.Validated.Valid import info.fingo.spata.schema.error.ValidationError import info.fingo.spata.util.classLabel +import scala.util.matching.Regex + /** Validator for typed CVS field value. * * It is used as part of schema validation, after successful conversion of CSV string value to desired type. @@ -47,7 +49,7 @@ trait Validator[A]: * @param value the validated value * @return error message parametrized with value */ - def errorMessage(value: A) = s"Invalid value [$value] reported by $name" + def errorMessage(value: A): String = s"Invalid value [$value] reported by $name" /* Main validation method, used by schema validation. Must not alter the value. */ final private[schema] def apply(value: A): Validated[ValidationError, A] = @@ -244,4 +246,4 @@ object FiniteValidator: */ def apply(): Validator[Double] = new Validator[Double]: def isValid(value: Double): Boolean = value.isFinite - override def errorMessage(value: Double) = s"Number [$value] is not finite" + override def errorMessage(value: Double): String = s"Number [$value] is not finite" diff --git a/src/main/scala/info/fingo/spata/text/StringParser.scala b/src/main/scala/info/fingo/spata/text/StringParser.scala index d9ba7d5..1f80183 100644 --- a/src/main/scala/info/fingo/spata/text/StringParser.scala +++ b/src/main/scala/info/fingo/spata/text/StringParser.scala @@ -5,9 +5,15 @@ */ package info.fingo.spata.text -import java.text.{DecimalFormat, NumberFormat, ParseException, ParsePosition} -import java.time.format.{DateTimeFormatter, DateTimeParseException} -import java.time.{LocalDate, LocalDateTime, LocalTime} +import java.text.DecimalFormat +import java.text.NumberFormat +import java.text.ParseException +import java.text.ParsePosition +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeParseException import scala.util.control.NonFatal /** Parser from `String` to desired type. @@ -144,7 +150,7 @@ object StringParser: * @return parser which accepts empty input */ given optionParser[A](using parser: StringParser[A]): StringParser[Option[A]] with - def apply(str: String) = if str == null || str.isBlank then None else Some(parser(str)) + def apply(str: String): Option[A] = if str == null || str.isBlank then None else Some(parser(str)) /** Parser for optional values with support for different formats. * Allows conversion of any simple parser to return `Option[A]` instead of `A`, avoiding error for empty string. diff --git a/src/main/scala/info/fingo/spata/text/StringRenderer.scala b/src/main/scala/info/fingo/spata/text/StringRenderer.scala index b75edcc..53d3239 100644 --- a/src/main/scala/info/fingo/spata/text/StringRenderer.scala +++ b/src/main/scala/info/fingo/spata/text/StringRenderer.scala @@ -5,7 +5,8 @@ */ package info.fingo.spata.text -import java.text.{DecimalFormat, NumberFormat} +import java.text.DecimalFormat +import java.text.NumberFormat import java.time.format.DateTimeFormatter import java.time.temporal.Temporal @@ -107,7 +108,7 @@ object StringRenderer: * @return renderer which accepts optional values */ given optionRenderer[A](using renderer: StringRenderer[A]): StringRenderer[Option[A]] with - def apply(value: Option[A]) = value.map(renderer.apply).getOrElse("") + def apply(value: Option[A]): String = value.map(renderer.apply).getOrElse("") /** Renderer for optional values with support for different formats. * Allows conversion of any simple renderer to accept `Option[A]` instead of `A`, @@ -125,7 +126,7 @@ object StringRenderer: /** Renderer for string. Return the original string or empty one for null. */ given stringRenderer: StringRenderer[String] with - def apply(value: String) = Option(value).getOrElse("") + def apply(value: String): String = Option(value).getOrElse("") /** Renderer for integer values. */ given intRenderer: StringRenderer[Int] with diff --git a/src/main/scala/info/fingo/spata/util/Logger.scala b/src/main/scala/info/fingo/spata/util/Logger.scala index 569e603..65b24ce 100644 --- a/src/main/scala/info/fingo/spata/util/Logger.scala +++ b/src/main/scala/info/fingo/spata/util/Logger.scala @@ -7,8 +7,8 @@ package info.fingo.spata.util import cats.effect.Sync import fs2.Stream +import org.slf4j.Logger as JLogger import org.slf4j.helpers.NOPLogger -import org.slf4j.{Logger => JLogger} /** Logger delegating spata logging operations to provided SLF4J logger. * It defaults to no-op logger and may be activated by creating a given instance with regular SLF4J logger.