Skip to content

Commit

Permalink
Merge pull request #127 from eed3si9n/wip/modifier
Browse files Browse the repository at this point in the history
Implement modifier directive
  • Loading branch information
eed3si9n committed Jan 23, 2019
2 parents 64a728d + c11cb75 commit c38efd7
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 6 deletions.
3 changes: 2 additions & 1 deletion library/src/main/scala/sbt/contraband/JavaCodeGen.scala
Expand Up @@ -68,11 +68,12 @@ class JavaCodeGen(lazyInterface: String, optionalInterface: String,
val extendsCode = parent map (p => s"extends ${fullyQualifiedName(p)}") getOrElse "implements java.io.Serializable"
val toStringImpl: List[String] = toToStringImpl(r)
val lfs = localFields(r, parentsInSchema)
val mod = toModifier(r.directives).getOrElse("public final")

val code =
s"""${genPackage(r)}
|${genDoc(doc)}
|public final class $name $extendsCode {
|$mod class $name $extendsCode {
| ${extra mkString EOL}
| ${genFactoryMethods(r, parent)}
| ${genFields(lfs)}
Expand Down
3 changes: 2 additions & 1 deletion library/src/main/scala/sbt/contraband/ScalaCodeGen.scala
Expand Up @@ -83,10 +83,11 @@ class ScalaCodeGen(javaLazy: String, javaOptional: String, instantiateJavaOption
val doc = toDoc(r.comments)
val extra = toExtra(r)
val since = getSince(r.directives)
val mod = toModifier(r.directives).getOrElse("final")
val code =
s"""${genPackage(r)}
|${genDoc(doc ++ argsDoc)}
|final class ${r.name}$privateCtr($ctorParameters) $extendsCode {
|$mod class ${r.name}$privateCtr($ctorParameters) $extendsCode {
| ${extra mkString EOL}
| ${genAlternativeConstructors(since, allFields, scalaPrivateConstructor, intfLang) mkString EOL}
| ${lazyMembers}
Expand Down
5 changes: 4 additions & 1 deletion library/src/main/scala/sbt/contraband/ast/SchemaAst.scala
Expand Up @@ -185,6 +185,7 @@ object Directive {
def fullCodec(value: String): Directive = Directive("fullCodec", Argument(None, StringValue(value)) :: Nil)
def codecTypeField(value: String): Directive = Directive("codecTypeField", Argument(None, StringValue(value)) :: Nil)
def generateCodec(value: Boolean): Directive = Directive("generateCodec", Argument(None, BooleanValue(value)) :: Nil)
def modifier(value: String): Directive = Directive("modifier", Argument(None, StringValue(value)) :: Nil)
}


Expand Down Expand Up @@ -426,6 +427,9 @@ object AstUtil {
}
}

def toModifier(dirs: List[Directive]): Option[String] =
scanSingleStringDirective(dirs, "modifier")

def toSince(dirs: List[Directive]): Option[VersionNumber] =
scanSingleStringDirective(dirs, "since") map (VersionNumber(_))

Expand All @@ -438,7 +442,6 @@ object AstUtil {
scanSingleStringDirective(dirs, "codecNamespace")
}


def toFullCodec(d: Document): Option[String] = {
val dirs = d.directives ++ (d.packageDecl map {_.directives}).toList.flatten
scanSingleStringDirective(dirs, "fullCodec")
Expand Down
10 changes: 9 additions & 1 deletion library/src/main/scala/sbt/contraband/parser/JsonParser.scala
Expand Up @@ -90,6 +90,12 @@ trait JsonParser[T] {
case _ => Some(ast.Directive.since(VersionNumber.empty.toString))
}

def ModifierDirective(json: JValue): Option[ast.Directive] =
(json ->? "modifier") match {
case Some(x) => Some(ast.Directive.modifier(x))
case _ => None
}

def CodecPackageDirective(json: JValue): Option[ast.Directive] =
(json ->? "codecNamespace") orElse (json ->? "codecPackage") match {
case Some(x) => Some(ast.Directive.codecPackage(x))
Expand Down Expand Up @@ -223,7 +229,9 @@ object JsonParser {
}
val fs = json ->* "fields" map FieldDefinition.parse
val intfs = (superIntf map { i => toNamedType(i, None) }).toList
val directives = TargetDirective(json).toList ++ SinceDirective(json).toList ++
val directives = TargetDirective(json).toList ++
SinceDirective(json).toList ++
ModifierDirective(json).toList ++
GenerateCodecDirective(json).toList
ast.ObjectTypeDefinition(json -> "name",
json ->? "namespace",
Expand Down
2 changes: 2 additions & 0 deletions library/src/test/scala/GCodeGenSpec.scala
Expand Up @@ -20,6 +20,7 @@ abstract class GCodeGenSpec(language: String) extends FlatSpec with Matchers wit
it should "grow a record from 0 to 1 field" in recordGrowZeroToOneField
it should "grow a record from 0 to 1 to 2 fields" in recordGrowZeroToOneToTwoFields
it should "generate correct types" in recordPrimitives
it should "generate with different modifier" in recordWithModifier

"generate(Schema)" should "generate a complete schema" in schemaGenerateComplete
it should "generate and indent a complete schema" in schemaGenerateCompletePlusIndent
Expand All @@ -37,6 +38,7 @@ abstract class GCodeGenSpec(language: String) extends FlatSpec with Matchers wit
def recordGrowZeroToOneField: Unit
def recordGrowZeroToOneToTwoFields: Unit
def recordPrimitives: Unit
def recordWithModifier: Unit

def schemaGenerateComplete: Unit
def schemaGenerateCompletePlusIndent: Unit
Expand Down
10 changes: 10 additions & 0 deletions library/src/test/scala/GraphQLExample.scala
Expand Up @@ -34,6 +34,16 @@ type TypeExample {
field: StringStringMap!
}"""

val modifierExample = """
package com.example @target(Scala)
type ModifierExample
@modifier("sealed")
{
field: Int!
}
"""

val intfExample = """
package com.example @target(Scala)
@codecPackage("generated")
Expand Down
32 changes: 32 additions & 0 deletions library/src/test/scala/GraphQLScalaCodeGenSpec.scala
Expand Up @@ -177,6 +177,38 @@ class GraphQLScalaCodeGenSpec extends FlatSpec with Matchers with Inside with Eq
|""".stripMargin.unindent)
}

it should "generate with modifier" in {
val Success(ast) = SchemaParser.parse(modifierExample)
// println(ast)
val code = mkScalaCodeGen.generate(ast)

code.head._2.unindent should equalLines (
"""package com.example
|sealed class ModifierExample private (
|val field: Int) extends Serializable {
| override def equals(o: Any): Boolean = o match {
| case x: ModifierExample => (this.field == x.field)
| case _ => false
| }
| override def hashCode: Int = {
| 37 * (37 * (17 + "com.example.ModifierExample".##) + field.##)
| }
| override def toString: String = {
| "ModifierExample(" + field + ")"
| }
| private[this] def copy(field: Int = field): ModifierExample = {
| new ModifierExample(field)
| }
| def withField(field: Int): ModifierExample = {
| copy(field = field)
| }
|}
|object ModifierExample {
| def apply(field: Int): ModifierExample = new ModifierExample(field)
|}
|""".stripMargin.unindent)
}

"generate(Interface)" should "generate an interface with one child" in {
val Success(ast) = SchemaParser.parse(intfExample)
val code = mkScalaCodeGen.generate(ast)
Expand Down
3 changes: 3 additions & 0 deletions library/src/test/scala/JsonCodecCodeGenSpec.scala
Expand Up @@ -331,6 +331,9 @@ implicit lazy val primitiveTypesExample2Format: JsonFormat[_root_.primitiveTypes
}""".stripMargin.unindent)
}

override def recordWithModifier: Unit = {
}

override def schemaGenerateTypeReferences = {
val schema = JsonParser.Document.parse(primitiveTypesExample)
val gen = new CodecCodeGen(codecParents, instantiateJavaLazy, javaOption, scalaArray, formatsForType, schema :: Nil)
Expand Down
46 changes: 46 additions & 0 deletions library/src/test/scala/JsonJavaCodeGenSpec.scala
Expand Up @@ -519,6 +519,52 @@ class JsonJavaCodeGenSpec extends GCodeGenSpec("Java") {
))
}

override def recordWithModifier: Unit = {
val record = JsonParser.ObjectTypeDefinition.parse(modifierExample)
val code = mkJavaCodeGen generate record

code mapValues (_.unindent) should equalMapLines (
ListMap(
new File("modifierExample.java") ->
"""sealed class modifierExample implements java.io.Serializable {
|
| public static modifierExample create(int _field) {
| return new modifierExample(_field);
| }
| public static modifierExample of(int _field) {
| return new modifierExample(_field);
| }
| private int field;
| protected modifierExample(int _field) {
| super();
| field = _field;
| }
| public int field() {
| return this.field;
| }
| public modifierExample withField(int field) {
| return new modifierExample(field);
| }
| public boolean equals(Object obj) {
| if (this == obj) {
| return true;
| } else if (!(obj instanceof modifierExample)) {
| return false;
| } else {
| modifierExample o = (modifierExample)obj;
| return (this.field() == o.field());
| }
| }
| public int hashCode() {
| return 37 * (37 * (17 + "modifierExample".hashCode()) + (new Integer(field())).hashCode());
| }
| public String toString() {
| return "modifierExample(" + "field: " + field() + ")";
| }
|}""".stripMargin.unindent
))
}

override def schemaGenerateTypeReferences = {
val schema = JsonParser.Document.parse(primitiveTypesExample)
val code = mkJavaCodeGen generate schema
Expand Down
32 changes: 31 additions & 1 deletion library/src/test/scala/JsonScalaCodeGenSpec.scala
Expand Up @@ -334,7 +334,37 @@ object primitiveTypesExample2 {
def apply(smallBoolean: Boolean, bigBoolean: Boolean): primitiveTypesExample2 = new primitiveTypesExample2(smallBoolean, bigBoolean)
}""".stripMargin.unindent)
}


override def recordWithModifier: Unit = {
val record = JsonParser.ObjectTypeDefinition.parse(modifierExample)
val code = mkScalaCodeGen generate record

code.head._2.unindent should equalLines (
"""sealed class modifierExample private (
|val field: Int) extends Serializable {
| override def equals(o: Any): Boolean = o match {
| case x: modifierExample => (this.field == x.field)
| case _ => false
| }
| override def hashCode: Int = {
| 37 * (37 * (17 + "modifierExample".##) + field.##)
| }
| override def toString: String = {
| "modifierExample(" + field + ")"
| }
| private[this] def copy(field: Int = field): modifierExample = {
| new modifierExample(field)
| }
| def withField(field: Int): modifierExample = {
| copy(field = field)
| }
|}
|object modifierExample {
| def apply(field: Int): modifierExample = new modifierExample(field)
|}
|""".stripMargin.unindent)
}

override def schemaGenerateTypeReferences = {
val schema = JsonParser.Document.parse(primitiveTypesExample)
val code = mkScalaCodeGen generate schema
Expand Down
13 changes: 13 additions & 0 deletions library/src/test/scala/JsonSchemaExample.scala
Expand Up @@ -235,6 +235,19 @@ object JsonSchemaExample {
]
}"""

val modifierExample = """{
"name": "modifierExample",
"target": "Scala",
"type": "record",
"modifier": "sealed",
"fields": [
{
"name": "field",
"type": "int"
}
]
}"""

val generateArgDocExample = """{
"codecNamespace": "generated",
"types": [
Expand Down
Expand Up @@ -19,3 +19,8 @@ type ScalaKeywordExample {
type: String
private: Boolean
}

type Dis @modifier("sealed") {
#x def x: Int = 1
#xcompanioninterface com.example.Dis
}
Expand Up @@ -4,5 +4,5 @@ object Example extends App {
val martin = Person("Martin", Some(24))

println("Who is Martin? " + martin)

Dis.x.toString
}
1 change: 1 addition & 0 deletions plugin/src/sbt-test/sbt-contraband/simple-new-schema/test
@@ -1 +1,2 @@
> compile
> run

0 comments on commit c38efd7

Please sign in to comment.