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

grist.Models.scala Maven / Gradle / Ivy

The newest version!
package grist

import scala.util.Success

import io.circe.generic.semiauto.{deriveCodec, deriveDecoder, deriveEncoder}
import io.circe.*

object Models {
  case class Record(id: Int, fields: Map[String, Json])
  object Record {
    implicit val codecModelsRecord: Codec[Record] = deriveCodec[Record]
  }

  case class Records(records: List[Record])
  object Records {
    implicit val codecModelsRecords: Codec[Records] = deriveCodec
  }

  case class RecordWithoutId(fields: Map[String, Json])
  object RecordWithoutId {
    implicit val codecRecordWithoutId: Codec[RecordWithoutId] = deriveCodec
  }

  case class RecordsWithoutId(records: List[RecordWithoutId])
  object RecordsWithoutId {
    implicit val codecRecordsWithoutId: Codec[RecordsWithoutId] = deriveCodec
  }

  case class RecordWithoutFields(id: Int)
  object RecordWithoutFields {
    implicit val codecRecordWithoutFields: Codec[RecordWithoutFields] = deriveCodec
  }

  case class RecordsWithoutFields(records: List[RecordWithoutFields])
  object RecordsWithoutFields {
    implicit val codecRecordsWithoutFields: Codec[RecordsWithoutFields] = deriveCodec
  }

  case class Table(id: String, fields: Map[String, Json])
  object Table {
    implicit val codecTable: Codec[Table] = deriveCodec
  }

  case class Tables(tables: List[Table])
  object Tables {
    implicit val codecTables: Codec[Tables] = deriveCodec
  }

  sealed trait FieldType
  // noinspection ScalaUnusedSymbol
  object FieldType {
    case object Any                       extends FieldType
    case object Text                      extends FieldType
    case object Numeric                   extends FieldType
    case object Int                       extends FieldType
    case object Bool                      extends FieldType
    case object Date                      extends FieldType
    case class DateTime(timezone: String) extends FieldType
    case object Choice                    extends FieldType
    case object ChoiceList                extends FieldType
    case class Ref(tableId: String)       extends FieldType
    case class RefList(tableId: String)   extends FieldType
    case object Attachments               extends FieldType

    implicit val decodeFieldType: Decoder[FieldType]          = Decoder[String].emap {
      case "Any"                 => Right(Any)
      case "Text"                => Right(Text)
      case "Numeric"             => Right(Numeric)
      case "Int"                 => Right(Int)
      case "Bool"                => Right(Bool)
      case "Date"                => Right(Date)
      case s"DateTime:$timezone" => Right(DateTime(timezone))
      case "Choice"              => Right(Choice)
      case s"ChoiceList"         => Right(ChoiceList)
      case s"Ref:$tableId"       => Right(Ref(tableId))
      case s"RefList:$tableId"   => Right(RefList(tableId))
      case "Attachments"         => Right(Attachments)
      case other                 => Left(s"Unknown field type: $other")
    }
    implicit val encodeFieldType: io.circe.Encoder[FieldType] =
      io.circe.Encoder[String].contramap {
        case Any                => "Any"
        case Text               => "Text"
        case Numeric            => "Numeric"
        case Int                => "Int"
        case Bool               => "Bool"
        case Date               => "Date"
        case DateTime(timezone) => s"DateTime:$timezone"
        case Choice             => "Choice"
        case ChoiceList         => "ChoiceList"
        case Ref(tableId)       => s"Ref:$tableId"
        case RefList(tableId)   => s"RefList:$tableId"
        case Attachments        => "Attachments"
      }
  }

  sealed trait RecalcWhen
  // noinspection ScalaUnusedSymbol
  object RecalcWhen    {
    private case object OnNewRecordsOrRecalcDepsChange extends RecalcWhen
    private case object Never                          extends RecalcWhen
    private case object OnNewRecordsOrDataChange       extends RecalcWhen

    implicit val decodeRecalcWhen: Decoder[RecalcWhen]          = Decoder[Int].emap {
      case 0     => Right(OnNewRecordsOrRecalcDepsChange)
      case 1     => Right(Never)
      case 2     => Right(OnNewRecordsOrDataChange)
      case other =>
        Left(s"Unknown recalc when: $other")
    }
    implicit val encodeRecalcWhen: io.circe.Encoder[RecalcWhen] =
      io.circe.Encoder[Int].contramap {
        case OnNewRecordsOrRecalcDepsChange => 0
        case Never                          => 1
        case OnNewRecordsOrDataChange       => 2
      }
  }

  case class WidgetOptions(
    widget: Option[String],
    isCustomDateFormat: Option[Boolean],
    isCustomTimeFormat: Option[Boolean],
    alignment: Option[WidgetOptions.Alignment],
    dateFormat: Option[String],
    timeFormat: Option[String],
    choices: Option[Set[String]],
    choiceOptions: Option[Map[String, Json]],
    wrap: Option[Boolean],
    decimals: Option[Int],
    maxDecimals: Option[Int],
    rulesOptions: Option[Seq[Json]],
    fontBold: Option[Boolean],
    height: Option[Int]
  )
  object WidgetOptions {
    sealed abstract class Alignment(val value: String)
    // noinspection ScalaUnusedSymbol
    object Alignment {
      private case object Left  extends Alignment("left")
      private case object Right extends Alignment("right")
      private val all                                  = Set(Left, Right)
      implicit val decodeAlignment: Decoder[Alignment] = Decoder[String].emap { value =>
        all.find(_.value == value).toRight(s"Unknown alignment: $value")
      }
      implicit val encodeAlignment: Encoder[Alignment] = Encoder[String].contramap(_.value)
    }
    implicit val decodeWidgetOptions: Decoder[Option[WidgetOptions]] =
      Decoder[String].emapTry {
        case ""     => Success(None)
        case string =>
          parser
            .parse(string)
            .flatMap { json =>
              val keys  = json.asObject.map(_.keys.toSet).getOrElse(Set.empty)
              val extra =
                keys -- Set(
                  "widget",
                  "isCustomDateFormat",
                  "isCustomTimeFormat",
                  "alignment",
                  "dateFormat",
                  "timeFormat",
                  "choices",
                  "choiceOptions",
                  "wrap",
                  "decimals",
                  "maxDecimals",
                  "rulesOptions",
                  "fontBold",
                  "height"
                )
              if (extra.nonEmpty)
                Console.err.println(s"WidgetOptions: Warning, unknown keys: ${extra.mkString(", ")}, json: $json")
              deriveDecoder[WidgetOptions]
                .decodeJson(json)
                .map(Some(_))
            }
            .toTry
      }
    implicit val encodeWidgetOptions: Encoder[WidgetOptions] =
      Encoder[String].contramap { widgetOptions =>
        deriveEncoder[WidgetOptions].apply(widgetOptions).noSpaces
      }
  }

  case class GetFields(
    `type`: FieldType,
    label: String,
    formula: String,
    isFormula: Boolean,
    widgetOptions: Option[WidgetOptions],
    untieColIdFromLabel: Boolean,
    recalcWhen: RecalcWhen,
    visibleCol: Int,
    recalcDeps: Option[RefList],
    colRef: Int
  )
  object GetFields     {
    implicit val decodeGetFields: Decoder[GetFields] = deriveDecoder
  }

  case class Column(id: String, fields: GetFields)
  object Column {
    implicit val decodeColumn: Decoder[Column] = deriveDecoder
  }

  case class Columns(columns: List[Column])
  object Columns {
    implicit val decodeColumns: Decoder[Columns] = deriveDecoder
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy