1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.libraries.pcc.chronicle.api
18 
19 import kotlin.reflect.KClass
20 
21 /**
22  * Builds a [DataTypeDescriptor] from the given [name] and using the supplied [block].
23  *
24  * Example:
25  *
26  * ```kotlin
27  *  val myType = dataTypeDescriptor("Person", Person::class) {
28  *   "name" to FieldType.String
29  *   "age" to FieldType.Short
30  *   "father" to FieldType.Reference("Person")
31  *   "pet" to dataTypeDescriptor("Dog", Dog::class) { }
32  * }
33  * ```
34  */
35 @ChronicleDsl
dataTypeDescriptornull36 fun dataTypeDescriptor(
37   name: String,
38   cls: KClass<*>,
39   block: DataTypeDescriptor.Builder.() -> Unit = {}
40 ): DataTypeDescriptor {
41   return DataTypeDescriptor.Builder(name, cls).apply(block).build()
42 }
43 
44 /**
45  * Defines a Chronicle-managed data type. A [DataTypeDescriptor] allows Chronicle to perform
46  * field-by-field data flow analysis for policy enforcement.
47  */
48 data class DataTypeDescriptor(
49   val name: String,
50   val fields: Map<String, FieldType>,
51   val innerTypes: Set<DataTypeDescriptor> = emptySet(),
52   val cls: KClass<*>
53 ) {
54   class Builder(val name: String, val cls: KClass<*>) {
55     private val fields = mutableMapOf<String, FieldType>()
56     private val innerTypes = mutableSetOf<DataTypeDescriptor>()
57 
58     /** Adds a field to the [DataTypeDescriptor] being built. */
tonull59     infix fun String.to(type: FieldType) {
60       fields[this] = type
61     }
62 
63     /**
64      * Constructs a nested [DataTypeDescriptor] and returns it as a [FieldType.Nested] which can be
65      * used along with [to] to map it to a field name.
66      */
67     @ChronicleDsl
dataTypeDescriptornull68     fun dataTypeDescriptor(
69       name: String,
70       cls: KClass<*>,
71       block: Builder.() -> Unit = {},
72     ): FieldType {
73       val dtd = Builder(name, cls).apply(block).build()
<lambda>null74       require(innerTypes.none { it.name == dtd.name && it != dtd }) {
75         "Duplicate inner type declared with name: ${dtd.name} and different contents"
76       }
77       innerTypes.add(dtd)
78       return FieldType.Nested(dtd.name)
79     }
80 
81     /** Builds a [DataTypeDescriptor] from the current state of the [Builder]. */
buildnull82     fun build(): DataTypeDescriptor = DataTypeDescriptor(name, fields, innerTypes, cls)
83   }
84 }
85 
86 /** Sealed class of options for field values in [DataTypeDescriptor]s. */
87 sealed class FieldType {
88   /** Represents a field value as a [Boolean] primitive. */
89   object Boolean : FieldType()
90 
91   /** Represents a field value as a [Byte] primitive. */
92   object Byte : FieldType()
93 
94   /** Represents a field value as a [ByteArray] primitive. */
95   object ByteArray : FieldType()
96 
97   /** Represents a field value as a [Short] primitive. */
98   object Short : FieldType()
99 
100   /** Represents a field value as a [Integer] primitive. */
101   object Integer : FieldType()
102 
103   /** Represents a field value as a [Long] primitive. */
104   object Long : FieldType()
105 
106   /** Represents a field value as a [Float] primitive. */
107   object Float : FieldType()
108 
109   /** Represents a field value as a [Double] primitive. */
110   object Double : FieldType()
111 
112   /** Represents a field value as a [String] primitive. */
113   object String : FieldType()
114 
115   /** Represents a field value as a [Char] primitive. */
116   object Char : FieldType()
117 
118   /** Represents a field value as a [java.time.Instant]. */
119   object Instant : FieldType()
120 
121   /** Represents a field value as a [java.time.Duration]. */
122   object Duration : FieldType()
123 
124   /** Represents a field value as an [Enum] object. */
125   data class Enum(
126     val name: kotlin.String,
127     val possibleValues: kotlin.collections.List<kotlin.String>
128   ) : FieldType()
129 
130   /** Represents a field value as an array of [itemFieldType] -typed objects. */
131   data class Array(val itemFieldType: FieldType) : FieldType()
132 
133   /** Represents a field value as a list of [itemFieldType] -typed objects. */
134   data class List(val itemFieldType: FieldType) : FieldType()
135 
136   /**
137    * Represents a field value as a non-primitive value, another [DataTypeDescriptor]-defined type.
138    */
139   data class Nested(val name: kotlin.String) : FieldType()
140 
141   /**
142    * Represents a field value as a nullable potentially containing a [itemFieldType]-typed object.
143    */
144   data class Nullable(val itemFieldType: FieldType) : FieldType()
145 
146   /**
147    * Represents a field value as a reference to a non-primitive value, another [DataTypeDescriptor]
148    * -defined type.
149    */
150   data class Reference(val name: kotlin.String) : FieldType()
151 
152   /**
153    * Represents a field value as a given opaque type, belonging to a small, predetermined set of
154    * possible opaque types. Only the fully-qualified name is used to define the type, with no
155    * [DataTypeDescriptor] definition.
156    */
157   data class Opaque(val name: kotlin.String) : FieldType()
158 
159   /** Represents a field value as a tuple with [itemFieldTypes] -typed objects. */
160   data class Tuple(val itemFieldTypes: kotlin.collections.List<FieldType>) : FieldType()
161 }
162