Accès protégé aux champs

On souhaite, pour des buts pédagogiques plus qu'utilitaires, implémenter une macro de type derive Opaque. Celle-ci permet de définir des accesseurs sécurisés pour les champs concernés d'une structure :

#[derive(Opaque)]
struct SecureSettings {
  #[key] secret_key: u64,
  regular_field: String,
  #[opaque] protected_field: String,
  #[opaque] other_protected_field: u32,
}

Notre macro va ajouter automatiquement un accesseur aux champs marqués #[opaque]. Cet accesseur prendra un paramètre key de type référence sur le type du champ marqué avec l'attribut #[key] et renverra une Option contenant le champ demandé uniquement si la clé passée est correcte. Le code généré ressemblera au suivant :

impl SecureSettings {
  fn get_protected_field(&self, key: &u64) -> Option<&String> {
    (key == &self.key).then(|| &self.protected_field)
  }
  fn get_other_protected_field(&self, key: &u64) -> Option<&u32> {
    (key == &self.key).then(|| &self.other_protected_field)
  }
}

Implémentation

  1. Écrivez dans le crate macros le squelette de la macro de type derive pour Opaque. Cette macro prendra en attributs supplémentaires key et opaque qui serviront à qualiier des champs. Pour l'instant, ne renvoyez rien d'utile.

  2. Vérifiez que l'argument passé à la macro est bien une structure contenant des champs nommés et indiquez un message d'erreur approprié si ce n'est pas le cas.

  3. Identifiez le champ marqué avec #[key] qui doit être unique ainsi que les champs marqués avec #[opaque]. Le champ contenant la clé ne peut pas être également #[opaque].

  4. Après avoir mis dans des vecteurs le nom de chaque champ opaque, son type et l'identifiant à utiliser pour son accesseur, générez le code. Vous aurez également besoin d'avoir dans des variables accessibles lors de l'expansion le nom et le type du champ contenant la clé.

  5. Testez, sans oublier les tests avec trybuild pour vérifier la précision des messages d'erreur.