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.