Skip to content

Commit

Permalink
Respect the sensitive trait on enums
Browse files Browse the repository at this point in the history
This commit fixes #1745. It allows enums to respect the sensitive trait.
When annotated as such, an enum will not display its data and instead
will show the redacted text upon debug print.
  • Loading branch information
ysaito1001 committed Nov 14, 2022
1 parent daa611c commit 7e6fdf5
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata
import software.amazon.smithy.rust.codegen.core.smithy.ifClient
import software.amazon.smithy.rust.codegen.core.util.REDACTION
import software.amazon.smithy.rust.codegen.core.util.doubleQuote
import software.amazon.smithy.rust.codegen.core.util.dq
import software.amazon.smithy.rust.codegen.core.util.getTrait
import software.amazon.smithy.rust.codegen.core.util.orNull
import software.amazon.smithy.rust.codegen.core.util.shouldRedact

/** Model that wraps [EnumDefinition] to calculate and cache values required to generate the Rust enum source. */
class EnumMemberModel(private val definition: EnumDefinition, private val symbolProvider: RustSymbolProvider) {
Expand Down Expand Up @@ -92,7 +94,12 @@ open class EnumGenerator(
) {
protected val symbol: Symbol = symbolProvider.toSymbol(shape)
protected val enumName: String = symbol.name
protected val meta = symbol.expectRustMetadata()
private val isSensitive = shape.shouldRedact(model)
protected val meta = if (isSensitive) {
symbol.expectRustMetadata().withoutDerives(RuntimeType.Debug)
} else {
symbol.expectRustMetadata()
}
protected val sortedMembers: List<EnumMemberModel> =
enumTrait.values.sortedBy { it.value }.map { EnumMemberModel(it, symbolProvider) }
protected open var target: CodegenTarget = CodegenTarget.CLIENT
Expand Down Expand Up @@ -128,6 +135,10 @@ open class EnumGenerator(
} else {
renderUnnamedEnum()
}

if (isSensitive) {
renderDebugImplForSensitiveEnum()
}
}

private fun renderUnnamedEnum() {
Expand Down Expand Up @@ -230,6 +241,19 @@ open class EnumGenerator(
}
}

/**
* Manually implement the Debug trait for the enum if marked as sensitive
*
* It prints the redacted text regardless of the variant it is asked to print.
*/
private fun renderDebugImplForSensitiveEnum() {
writer.rustBlock("impl #T for $enumName", RuntimeType.Debug) {
writer.rustBlock("fn fmt(&self, f: &mut #1T::Formatter<'_>) -> #1T::Result", RuntimeType.stdfmt) {
rust("""write!(f, "{}", $REDACTION)""")
}
}
}

protected open fun renderFromForStr() {
writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.From) {
rustBlock("fn from(s: &str) -> Self") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest
import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
import software.amazon.smithy.rust.codegen.core.util.REDACTION
import software.amazon.smithy.rust.codegen.core.util.expectTrait
import software.amazon.smithy.rust.codegen.core.util.lookup
import software.amazon.smithy.rust.codegen.core.util.orNull
Expand Down Expand Up @@ -426,4 +427,133 @@ class EnumGeneratorTest {
val variant3AsVariant3 = "SomeEnum::Variant3"
expectMatchExpressionCompiles(modelV2, "test#SomeEnum", variant3AsVariant3)
}

@Test
fun `impl debug for non-sensitive enum should implement the derived debug trait`() {
val model = """
namespace test
@enum([
{ name: "Foo", value: "Foo" },
{ name: "Bar", value: "Bar" },
])
string SomeEnum
""".asSmithyModel()

val shape = model.lookup<StringShape>("test#SomeEnum")
val trait = shape.expectTrait<EnumTrait>()
val provider = testSymbolProvider(model)
val project = TestWorkspace.testProject(provider)
project.withModule(RustModule.Model) {
val generator = EnumGenerator(model, provider, this, shape, trait)
generator.render()
unitTest(
"impl_debug_for_non_sensitive_enum_should_implement_the_derived_debug_trait",
"""
assert_eq!(format!("{:?}", SomeEnum::Foo), "Foo");
assert_eq!(format!("{:?}", SomeEnum::Bar), "Bar");
assert_eq!(
format!("{:?}", SomeEnum::from("Baz")),
"Unknown(UnknownVariantValue(\"Baz\"))");
""",
)
}
project.compileAndTest()
}

@Test
fun `impl debug for sensitive enum should redact text`() {
val model = """
namespace test
@sensitive
@enum([
{ name: "Foo", value: "Foo" },
{ name: "Bar", value: "Bar" },
])
string SomeEnum
""".asSmithyModel()

val shape = model.lookup<StringShape>("test#SomeEnum")
val trait = shape.expectTrait<EnumTrait>()
val provider = testSymbolProvider(model)
val project = TestWorkspace.testProject(provider)
project.withModule(RustModule.Model) {
val generator = EnumGenerator(model, provider, this, shape, trait)
generator.render()
unitTest(
"impl_debug_for_sensitive_enum_should_redact_text",
"""
assert_eq!(format!("{:?}", SomeEnum::Foo), $REDACTION);
assert_eq!(format!("{:?}", SomeEnum::Bar), $REDACTION);
""",
)
}
project.compileAndTest()
}

@Test
fun `impl debug for non-sensitive unnamed enum should implement the derived debug trait`() {
val model = """
namespace test
@enum([
{ value: "Foo" },
{ value: "Bar" },
])
string SomeEnum
""".asSmithyModel()

val shape = model.lookup<StringShape>("test#SomeEnum")
val trait = shape.expectTrait<EnumTrait>()
val provider = testSymbolProvider(model)
val project = TestWorkspace.testProject(provider)
project.withModule(RustModule.Model) {
val generator = EnumGenerator(model, provider, this, shape, trait)
generator.render()
unitTest(
"impl_debug_for_non_sensitive_unnamed_enum_should_implement_the_derived_debug_trait",
"""
for variant in SomeEnum::values() {
assert_eq!(
format!("{:?}", SomeEnum(variant.to_string())),
format!("SomeEnum(\"{}\")", variant.to_owned())
);
}
""",
)
}
project.compileAndTest()
}

@Test
fun `impl debug for sensitive unnamed enum should redact text`() {
val model = """
namespace test
@sensitive
@enum([
{ value: "Foo" },
{ value: "Bar" },
])
string SomeEnum
""".asSmithyModel()

val shape = model.lookup<StringShape>("test#SomeEnum")
val trait = shape.expectTrait<EnumTrait>()
val provider = testSymbolProvider(model)
val project = TestWorkspace.testProject(provider)
project.withModule(RustModule.Model) {
val generator = EnumGenerator(model, provider, this, shape, trait)
generator.render()
unitTest(
"impl_debug_for_sensitive_unnamed_enum_should_redact_text",
"""
for variant in SomeEnum::values() {
assert_eq!(
format!("{:?}", SomeEnum(variant.to_string())),
$REDACTION
);
}
""",
)
}
project.compileAndTest()
}
}

0 comments on commit 7e6fdf5

Please sign in to comment.