1 /*
2  * Copyright (C) 2023 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 package com.android.intentresolver.validation
17 
18 import android.util.Log
19 import com.android.intentresolver.validation.Importance.CRITICAL
20 import com.android.intentresolver.validation.Importance.WARNING
21 import kotlin.reflect.KClass
22 
23 sealed interface Finding {
24     val importance: Importance
25     val message: String
26 }
27 
28 enum class Importance {
29     CRITICAL,
30     WARNING,
31 }
32 
33 val Finding.logcatPriority
34     get() =
35         when (importance) {
36             CRITICAL -> Log.ERROR
37             WARNING -> Log.WARN
38         }
39 
lognull40 fun Finding.log(tag: String) {
41     Log.println(logcatPriority, tag, message)
42 }
43 
<lambda>null44 private fun formatMessage(key: String? = null, msg: String) = buildString {
45     key?.also { append("['$key']: ") }
46     append(msg)
47 }
48 
49 data class IgnoredValue(
50     val key: String,
51     val reason: String,
52 ) : Finding {
53     override val importance = WARNING
54 
55     override val message: String
56         get() = formatMessage(key, "Ignored. $reason")
57 }
58 
59 data class NoValue(
60     val key: String,
61     override val importance: Importance,
62     val allowedType: KClass<*>,
63 ) : Finding {
64 
65     override val message: String
66         get() =
67             formatMessage(
68                 key,
69                 if (importance == CRITICAL) {
70                     "expected value of ${allowedType.simpleName}, " + "but no value was present"
71                 } else {
72                     "no ${allowedType.simpleName} value present"
73                 }
74             )
75 }
76 
77 data class WrongElementType(
78     val key: String,
79     override val importance: Importance,
80     val container: KClass<*>,
81     val actualType: KClass<*>,
82     val expectedType: KClass<*>
83 ) : Finding {
84     override val message: String
85         get() =
86             formatMessage(
87                 key,
88                 "${container.simpleName} expected with elements of " +
89                     "${expectedType.simpleName} " +
90                     "but found ${actualType.simpleName} values instead"
91             )
92 }
93 
94 data class ValueIsWrongType(
95     val key: String,
96     override val importance: Importance,
97     val actualType: KClass<*>,
98     val allowedTypes: List<KClass<*>>,
99 ) : Finding {
100 
101     override val message: String
102         get() =
103             formatMessage(
104                 key,
105                 "expected value of ${allowedTypes.map(KClass<*>::simpleName)} " +
106                     "but was ${actualType.simpleName}"
107             )
108 }
109 
110 data class UncaughtException(val thrown: Throwable, val key: String? = null) : Finding {
111     override val importance: Importance
112         get() = CRITICAL
113     override val message: String
114         get() =
115             formatMessage(
116                 key,
117                 "An unhandled exception was caught during validation: " +
118                     thrown.stackTraceToString()
119             )
120 }
121