Protected Field Access
For pedagogical purposes rather than practical ones, we want to implement a derive-like macro called Opaque
. This macro allows us to define secure accessors for the specified fields of a structure:
#[derive(Opaque)]
struct SecureSettings {
#[key] secret_key: u64,
regular_field: String,
#[opaque] protected_field: String,
#[opaque] other_protected_field: u32,
}
Our macro will automatically add an accessor for fields marked with #[opaque]
. This accessor will take a key
parameter, which should be of the same type as the field marked with the #[key]
attribute, and will return an Option
containing the requested field only if the passed key is correct. The generated code will look like this:
impl SecureSettings {
fn get_protected_field(&self, key: &u64) -> Option<&String> {
(key == &self.secret_key).then(|| &self.protected_field)
}
fn get_other_protected_field(&self, key: &u64) -> Option<&u32> {
(key == &self.secret_key).then(|| &self.other_protected_field)
}
}
Implementation
Exercise 7.a: Write a skeleton for the Opaque
derive-like macro in the macros
crate. This macro should take additional key
and opaque
attributes to mark fields. For now, don't return anything useful.
Exercise 7.b: Verify that the argument passed to the macro is indeed a structure containing named fields, and provide an appropriate error message if not.
Exercise 7.c: Identify the field marked with #[key]
, which should be unique, as well as the fields marked with #[opaque]
. The field containing the key cannot also be #[opaque]
.
Exercise 7.d: After storing the name, type, and identifier to be used for each opaque field in vectors, generate the code. You will also need to have variables accessible during expansion for the name and type of the field containing the key.
Exercise 7.e: Test, including tests with trybuild
to verify the accuracy of error messages.