<commit_before>package no.hoopla.serializer

import org.json4s.JsonDSL._
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.jackson.Serialization
import org.json4s.jackson.Serialization.write

object Serializer {
  private[this] type Visited = Map[String, Set[JValue]]
  private[this] type Included = List[JValue]
  private[this] type IncludedVisited = (List[JValue], Visited)

  private[this] implicit val formats: Formats = Serialization.formats(NoTypeHints)

  def serialize[T <: AnyRef](schema: Schema[T], obj: T) = {
    toJsonApi(schema, obj)
  }

  def serialize[T <: AnyRef](schema: Schema[T], iterable: Iterable[T]) = {
    toJsonApi(schema, iterable)
  }

  private[this] def toJsonApi(schema: SchemaBase, obj: AnyRef): JValue = {
    val data = parse(write(obj))
    val (included, _) = addAllIncluded(List[JValue](), Map[String, Set[JValue]](), schema.relationships, data)

    ("data" -> serializeSchemaData(schema, data)) ~
    ("included" -> included)
  }

  private[this] def addAllIncluded(included: Included, visited: Visited, relationships: List[Relationship], data: JValue): IncludedVisited =
    relationships.filter(_.included).foldLeft((included, visited)){ case ((inc, vis), relationship) =>
      addIncluded(inc, vis, relationship.schema, data \ relationship.attribute)
    }

  private[this] def addIncluded(included: Included, visited: Visited, relationshipSchema: SchemaBase, relationshipData: JValue): IncludedVisited =
    if (relationshipData.isInstanceOf[JArray]) {
      relationshipData.children.foldLeft(included, visited){ case ((inc, vis), childData) =>
        addIncluded(inc, vis, relationshipSchema, childData)
      }
    } else {
      val typeName = relationshipSchema.typeName
      val primaryKey = relationshipData \ relationshipSchema.primaryKey

      val visitedPrimaryKeys = visited.getOrElse(typeName, Set[JValue]())

      if (visitedPrimaryKeys.contains(primaryKey)) {
        (included, visited)
      } else {
        addAllIncluded(
          serializeSchemaData(relationshipSchema, relationshipData) :: included,
          visited.updated(typeName, visitedPrimaryKeys + primaryKey),
          relationshipSchema.relationships,
          relationshipData
        )
      }
    }

  /**
    Every resource object MUST contain an id member and a type member. The values of the id and type members MUST be strings.
   */
  private[this] def resourceIdentifierObject(schema: SchemaBase, data: JValue): JObject =
    ("type" -> schema.typeName) ~
    ("id" -> JString(compact(render(data \ schema.primaryKey))))


  private[this] def relationship(rel: Relationship, data: JValue): JValue =
    "data" -> relationshipData(rel.schema, data)


  private[this] def relationshipData(schema: SchemaBase, data: JValue): JValue =
    data match {
      case JArray(_) => data.children.map(resourceIdentifierObject(schema, _))
      case JNothing => JNull
      case _ => resourceIdentifierObject(schema, data)
    }

  private[this] def serializeSchemaData(schema:SchemaBase, data: JValue): JValue =
    data match {
      case JArray(_) => data.children.map(x => serializeSchemaData(schema, x))
      case JNothing => JNothing
      case _ =>
        resourceIdentifierObject(schema, data) ~
        ("attributes" -> schema.attributes.map(attr => attr -> data \ attr)) ~
        ("relationships" -> schema.relationships.map(rel => rel.attribute -> relationship(rel, data \ rel.attribute)))
    }
}
<commit_msg>Public method has explicit type<commit_after>package no.hoopla.serializer

import org.json4s.JsonDSL._
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.jackson.Serialization
import org.json4s.jackson.Serialization.write

object Serializer {
  private[this] type Visited = Map[String, Set[JValue]]
  private[this] type Included = List[JValue]
  private[this] type IncludedVisited = (List[JValue], Visited)

  private[this] implicit val formats: Formats = Serialization.formats(NoTypeHints)

  def serialize[T <: AnyRef](schema: Schema[T], obj: T): JValue = {
    toJsonApi(schema, obj)
  }

  def serialize[T <: AnyRef](schema: Schema[T], iterable: Iterable[T]): JValue = {
    toJsonApi(schema, iterable)
  }

  private[this] def toJsonApi(schema: SchemaBase, obj: AnyRef): JValue = {
    val data = parse(write(obj))
    val (included, _) = addAllIncluded(List[JValue](), Map[String, Set[JValue]](), schema.relationships, data)

    ("data" -> serializeSchemaData(schema, data)) ~
    ("included" -> included)
  }

  private[this] def addAllIncluded(included: Included, visited: Visited, relationships: List[Relationship], data: JValue): IncludedVisited =
    relationships.filter(_.included).foldLeft((included, visited)){ case ((inc, vis), relationship) =>
      addIncluded(inc, vis, relationship.schema, data \ relationship.attribute)
    }

  private[this] def addIncluded(included: Included, visited: Visited, relationshipSchema: SchemaBase, relationshipData: JValue): IncludedVisited =
    if (relationshipData.isInstanceOf[JArray]) {
      relationshipData.children.foldLeft(included, visited){ case ((inc, vis), childData) =>
        addIncluded(inc, vis, relationshipSchema, childData)
      }
    } else {
      val typeName = relationshipSchema.typeName
      val primaryKey = relationshipData \ relationshipSchema.primaryKey

      val visitedPrimaryKeys = visited.getOrElse(typeName, Set[JValue]())

      if (visitedPrimaryKeys.contains(primaryKey)) {
        (included, visited)
      } else {
        addAllIncluded(
          serializeSchemaData(relationshipSchema, relationshipData) :: included,
          visited.updated(typeName, visitedPrimaryKeys + primaryKey),
          relationshipSchema.relationships,
          relationshipData
        )
      }
    }

  /**
    Every resource object MUST contain an id member and a type member. The values of the id and type members MUST be strings.
   */
  private[this] def resourceIdentifierObject(schema: SchemaBase, data: JValue): JObject =
    ("type" -> schema.typeName) ~
    ("id" -> JString(compact(render(data \ schema.primaryKey))))


  private[this] def relationship(rel: Relationship, data: JValue): JValue =
    "data" -> relationshipData(rel.schema, data)


  private[this] def relationshipData(schema: SchemaBase, data: JValue): JValue =
    data match {
      case JArray(_) => data.children.map(resourceIdentifierObject(schema, _))
      case JNothing => JNull
      case _ => resourceIdentifierObject(schema, data)
    }

  private[this] def serializeSchemaData(schema:SchemaBase, data: JValue): JValue =
    data match {
      case JArray(_) => data.children.map(x => serializeSchemaData(schema, x))
      case JNothing => JNothing
      case _ =>
        resourceIdentifierObject(schema, data) ~
        ("attributes" -> schema.attributes.map(attr => attr -> data \ attr)) ~
        ("relationships" -> schema.relationships.map(rel => rel.attribute -> relationship(rel, data \ rel.attribute)))
    }
}

{'fraction_non_alphanumeric': 0.12, 'fraction_numerical': 0.0014285714285714286, 'mean_word_length': 3.997144896502498, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '131260', 'n_tokens_mistral': 2076, 'n_tokens_neox': 2008, 'n_words': 478}