Skip to content

Consider using macros #126

@Mubelotix

Description

@Mubelotix

Macros are a wonderful feature of the Rust language that could help us for 2 things :

Add an abstraction over the Document trait

We currently use a Document trait to make structures of users compatible with MeiliSearch.
It works great but has some unpleasant boilerplate code.

Examples

Currently

#[derive(Debug, Serialize, Deserialize)]
pub struct Crate {
    name: String,
    downloads: Option<usize>,
    description: String,
    keywords: Vec<String>,
    categories: Vec<String>,
    readme: String,
    version: String,
}

// Implement the Document trait so that we can use our struct with MeiliSearch
impl Document for Crate {
    type UIDType = String;

    fn get_uid(&self) -> &Self::UIDType {
        &self.name
    }
}

With macros

#[derive(Debug, Serialize, Deserialize, MeiliSearchDocument)]
pub struct Crate {
    #[primary_key]
    name: String,
    downloads: Option<usize>,
    description: String,
    keywords: Vec<String>,
    categories: Vec<String>,
    readme: String,
    version: String,
}

Simplify our tests

There are currently issues about tests in this library. They are many but they are not standardized enough. This is a pain point when it comes to maintain existing tests and adding new ones.
We could use macros to generate tests.

Examples

Currently (unit test)

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    struct Document {
        id: usize,
        value: String,
        kind: String,
    }

    impl document::Document for Document {
        type UIDType = usize;

        fn get_uid(&self) -> &Self::UIDType {
            &self.id
        }
    }

    #[allow(unused_must_use)]
    async fn setup_test_index<'a>(client: &'a Client<'a>, name: &'a str) -> Index<'a> {
        // try to delete
        client.delete_index(name).await;

        let index = client.create_index(name, None).await.unwrap();
        index.add_documents(&[
            Document { id: 0, kind: "text".into(), value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.".to_string() },
            Document { id: 1, kind: "text".into(), value: "dolor sit amet, consectetur adipiscing elit".to_string() },
            Document { id: 2, kind: "title".into(), value: "The Social Network".to_string() },
            Document { id: 3, kind: "title".into(), value: "Harry Potter and the Sorcerer's Stone".to_string() },
            Document { id: 4, kind: "title".into(), value: "Harry Potter and the Chamber of Secrets".to_string() },
            Document { id: 5, kind: "title".into(), value: "Harry Potter and the Prisoner of Azkaban".to_string() },
            Document { id: 6, kind: "title".into(), value: "Harry Potter and the Goblet of Fire".to_string() },
            Document { id: 7, kind: "title".into(), value: "Harry Potter and the Order of the Phoenix".to_string() },
            Document { id: 8, kind: "title".into(), value: "Harry Potter and the Half-Blood Prince".to_string() },
            Document { id: 9, kind: "title".into(), value: "Harry Potter and the Deathly Hallows".to_string() },
        ], None).await.unwrap();
        index.set_attributes_for_faceting(["kind"]).await.unwrap();
        sleep(Duration::from_secs(1));
        index
    }

    #[async_test]
    async fn test_query_string() {
        let client = Client::new("http://localhost:7700", "masterKey");
        let index = setup_test_index(&client, "test_query_string").await;

        let results: SearchResults<Document> =
            index.search().with_query("dolor").execute().await.unwrap();
        assert_eq!(results.hits.len(), 2);

        client.delete_index("test_query_string").await.unwrap();
    }

With macros (unit test)

    #[meilisearch_test]
    async fn test_query_string() {
        let results: SearchResults<Document> =
            index.search().with_query("dolor").execute().await.unwrap();
        assert_eq!(results.hits.len(), 2);
    }

Currently (doc test)
    /// ```
    /// # use meilisearch_sdk::{client::*, indexes::*};
    /// # futures::executor::block_on(async move {
    /// // create the client
    /// let client = Client::new("http://localhost:7700", "masterKey");
    ///
    /// let indexes: Vec<Index> = client.list_all_indexes().await.unwrap();
    /// println!("{:?}", indexes);
    /// # });
    /// ```

With macros (doc test)
    /// ```
    /// # #[meilisearch_doctest]
    /// # fn test() {
    /// let indexes: Vec<Index> = client.list_all_indexes().await.unwrap();
    /// println!("{:?}", indexes);
    /// # }
    /// ```

The problem with macros

Rust does not allow us to define macros in a regular crate. We would need to create a new crate that would contain macros. That could cause extra maintenance work in the future, but it should not be necessary to update macros often.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions