es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo volver a exponer una caja completa en línea.

Tengo un proc-macro que también expone algunos tipos, por lo que estoy utilizando la siguiente estructura de cajas:

  • foo_core exporta FooTrait.
  • foo_macro (depende de foo_core) exporta foo_macro, que genera algunas estructuras que implementan FooTrait.
  • foo (depende de foo_core y foo_macro) re-exporta FooTrait y foo_macro.

El problema con el que me encuentro es que al re-exportar foo_core, cambia su ruta.

foo_macro genera un código que se ve aproximadamente así:

struct Bar;
impl ::foo_core::FooTrait for Bar {
  // ...
}

Tengo una serie de pruebas trybuild en foo_macro que pasan, ya que foo_core es una caja disponible.

El problema surge cuando intento escribir pruebas en foo (o en las cajas que dependen únicamente de foo), porque obtengo un error que me dice que no se puede encontrar la caja foo_core.

Esto tiene cierto sentido para mí; el consumidor de foo no ha dependido explícitamente de foo_core, pero me gustaría que los usuarios de mi caja no tengan que agregar foo y foo_core (y asegurarse de que las versiones coincidan), en su lugar, me gustaría que puedan agregar solo foo y que funcione.

Actualmente, puedo acceder a foo_core a través de foo::foo_core::FooTrait, pero obviamente esto es diferente a cómo mi macro genera el código (::foo_core::FooTrait). ¿Existe alguna forma de hacer que foo re-exporte toda la caja foo_core?

Si eso no es posible, ¿cuál es la mejor manera de abordar este patrón? ¿Puedo hacer que foo_macro cambie su comportamiento según en qué caja esté definido? ¿O debería mover todas mis pruebas a foo en lugar de foo_macro?

https://stackoverflow.com/questions/64288426/is-there-a-way-to-have-a-public-trait-in-a-proc-macro-crate Una respuesta a esta pregunta menciona este problema, pero simplemente dice “debes usar los nombres completamente calificados”, pero mi comprensión era que esto es lo que significa el :: al comienzo de la ruta.

Esto parece ser un patrón bastante común, así que espero que haya una solución sencilla disponible.

Tags:  ,

Answer

  1. Avatar for davy.ai

    Para volver a exportar todo el paquete foo_core en foo, puedes utilizar la declaración pub use con un comodín (*) de la siguiente manera:

    // en foo/src/lib.rs
    pub use foo_core::*;
    pub use foo_macro::foo_macro;
    

    Esto hará que todos los elementos públicos en foo_core estén disponibles en foo.

    En cuanto a cambiar el comportamiento de foo_macro dependiendo del paquete en el que esté definido, puedes utilizar el paquete proc-macro-hack para definir una macro “hack” que se expanda a un código diferente dependiendo de si se invoca en el contexto de foo o no. Por ejemplo:

    // en foo_macro/src/lib.rs
    use proc_macro_hack::proc_macro_hack;
    
    #[proc_macro_hack]
    pub fn foo_macro_hack(input: TokenStream) -> TokenStream {
        #[cfg(feature = "foo")]
        let output = expand_in_foo(input);
    
        #[cfg(not(feature = "foo"))]
        let output = expand_not_in_foo(input);
    
        output.into()
    }
    
    #[cfg(feature = "foo")]
    fn expand_in_foo(input: TokenStream) -> TokenStream {
        // código que genera implementaciones de estructuras utilizando `::foo_core::FooTrait`
    }
    
    #[cfg(not(feature = "foo"))]
    fn expand_not_in_foo(input: TokenStream) -> TokenStream {
        // código que genera implementaciones de estructuras utilizando `foo_core::FooTrait`
    }
    

    Luego, en el archivo Cargo.toml de foo, puedes habilitar la función foo de la siguiente manera:

    [features]
    foo = ["foo_core", "foo_macro"]
    

    Finalmente, en tus pruebas, puedes activar la función foo de la siguiente manera:

    // en foo/tests/foo_tests.rs
    #[cfg(feature = "foo")]
    mod tests {
        // pruebas que dependen de todo el paquete `foo`, incluyendo `foo_core`
    }
    

    De esta manera, los usuarios de tu paquete pueden depender únicamente de foo y obtener tanto foo_core como foo_macro, y si necesitan personalizar el comportamiento de foo_macro, pueden hacerlo utilizando la función foo.

Comments are closed.