#tokio #sync #async #sgr

macro synca

Write asynchronous code, and synca will create a synchronous version

9 releases (4 breaking)

0.5.3 Jan 29, 2024
0.5.2 Jan 29, 2024
0.4.1 Jan 26, 2024
0.3.0 Jan 14, 2024
0.1.0 Jan 13, 2024

#1762 in Procedural macros

Download history 9/week @ 2024-09-22 10/week @ 2024-09-29

83 downloads per month
Used in pg_sgr_from_row

MIT/Apache

43KB
1K SLoC

SyncA

SyncA is a framework for creating crates with both synchronous and asynchronous versions.

Docs

Motivation

When we write a library, we cannot control the environment in which our code will be used.

Often this leads to the fact that there is only a synchronous version, or two different crates. If the problems of the first solution are obvious, then the second leads to a violation of the DRY principle.

SyncA solves the problem by creating copies of modules.

Concept

The attribute synca macro is applied to the module that is to be copied. As an argument, it accepts modules that should be created based on the template.

In the body of the module it is possible to describe code modifiers.

Code Modifiers

  • sync - converts module code into a synchronous version
  • replace - replace types and attributes

Example

#[synca::synca(
  #[cfg(feature = "tokio")]
  pub mod tokio { },
  #[cfg(feature = "sync")]
  pub mod sync { 
    sync!();
    replace!(
      tokio_postgres::Client => postgres::Client,
      tokio_postgres::Error => postgres::Error,
      tokio_postgres::NoTls => postgres::NoTls,
      #[tokio::test] => #[test]
    );
  }
)] 
mod my_mod { 
  struct MyStruct {
    client: tokio_postgres::Client
  } 
  
  pub async fn get_name(client: &mut tokio_postgres::Client) -> String {
    let row = self.client.query_one("SQL", &[]).await.unwrap();

    row.get("name")
  }

  #[cfg(test)]
  mod tests {
    use super::get_name;

    #[tokio::test]
    pub async fn get_name_test() {
      assert_eq!(get_name(&mut client()).await, "My name");
    }

    fn client() -> tokio_postgres::Client { 
      #[synca::cfg(tokio)]
      let (client, connection) = tokio_postgres::connect("CONNECTION_STRING", tokio_postgres::NoTls).await?;
      #[synca::cfg(tokio)]
      tokio::spawn(async move {
        if let Err(e) = connection.await {
          eprintln!("connection error: {}", e);
        }
      });

      #[synca::cfg(sync)]
      let client = postgres::Client::connect(&conn_str, postgres::NoTls)?;

      client
    }
  }
}

Generated code

#[cfg(feature = "tokio")]
pub mod tokio { 
  struct MyStruct {
    client: tokio_postgres::Client
  } 
  
  pub async fn get_name(client: &mut tokio_postgres::Client) -> String {
    let row = self.client.query_one("SQL", &[]).await.unwrap();

    row.get("name")
  }

  #[cfg(test)]
  mod tests {
    use super::get_name;

    #[tokio::test]
    pub async fn get_name_test() {
      assert_eq!(get_name(&mut client()).await, "My name");
    }

    fn client() -> tokio_postgres::Client { 
      let (client, connection) = tokio_postgres::connect("CONNECTION_STRING", tokio_postgres::NoTls).await?;
      tokio::spawn(async move {
        if let Err(e) = connection.await {
          eprintln!("connection error: {}", e);
        }
      });

      #[сfg(all(feature = "tokio", not(feature = "tokio")))]
      let client = postgres::Client::connect(&conn_str, postgres::NoTls)?;

      client
    }
  }
}

#[cfg(feature = "sync")]
mod sync { 
  struct MyStruct {
    client: postgres::Client
  } 
  
  pub fn get_name(client: &mut postgres::Client) -> String {
    let row = self.client.query_one("SQL", &[]).unwrap();

    row.get("name")
  }

  #[cfg(test)]
  mod tests {
    use super::get_name;

    #[test]
    pub async fn get_name_test() {
      assert_eq!(get_name(&mut client()), "My name");
    }

    fn client() -> postgres::Client {  
      #[сfg(all(feature = "tokio", not(feature = "tokio")))]
      let (client, connection) = tokio_postgres::connect("CONNECTION_STRING", tokio_postgres::NoTls).await?;
      #[сfg(all(feature = "tokio", not(feature = "tokio")))]
      tokio::spawn(async move {
        if let Err(e) = connection.await {
          eprintln!("connection error: {}", e);
        }
      });

      let client = postgres::Client::connect(&conn_str, postgres::NoTls)?;

      client
    }
  }
}

Dependencies

~230–670KB
~16K SLoC