Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 3 additions & 45 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,61 +57,19 @@ extern "C" fn __spin_sdk_hash() {}
/// Helpers for building Spin `wasi-http` components.
pub mod http;

/// MQTT messaging.
#[allow(missing_docs)]
pub mod mqtt {
pub use super::wit::v2::mqtt::{Connection, Error, Payload, Qos};
}
pub mod mqtt;

/// Redis storage and messaging.
#[allow(missing_docs)]
pub mod redis {
use std::hash::{Hash, Hasher};

pub use super::wit::v2::redis::{Connection, Error, Payload, RedisParameter, RedisResult};

impl PartialEq for RedisResult {
fn eq(&self, other: &Self) -> bool {
use RedisResult::*;
match (self, other) {
(Nil, Nil) => true,
(Status(a), Status(b)) => a == b,
(Int64(a), Int64(b)) => a == b,
(Binary(a), Binary(b)) => a == b,
_ => false,
}
}
}

impl Eq for RedisResult {}

impl Hash for RedisResult {
fn hash<H: Hasher>(&self, state: &mut H) {
use RedisResult::*;

match self {
Nil => (),
Status(s) => s.hash(state),
Int64(v) => v.hash(state),
Binary(v) => v.hash(state),
}
}
}
}
pub mod redis;

/// Spin 2 Postgres relational database storage. Applications that do not require
/// Spin 2 support should use the `pg3` module instead.
pub mod pg;

/// Postgres relational database storage.
pub mod pg3;

/// MySQL relational database storage.
pub mod mysql;

#[doc(inline)]
/// Component configuration variables.
pub use wit::v2::variables;
pub mod variables;

#[doc(hidden)]
pub use wit_bindgen;
65 changes: 65 additions & 0 deletions src/mqtt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! MQTT message publishing.
//!
//! To receive MQTT messages, use the MQTT trigger.
//!
//! # Examples
//!
//! Send an MQTT message.
//!
//! ```no_run
//! use spin_sdk::mqtt::{Connection, Qos};
//!
//! # fn ensure_pet_picture(_: &[u8]) -> anyhow::Result<()> { Ok(()) }
//! # fn use_mqtt(request: spin_sdk::http::Request) -> anyhow::Result<()> {
//! let user = spin_sdk::variables::get("mqtt_username")?;
//! let password = spin_sdk::variables::get("mqtt_password")?;
//!
//! let conn = Connection::open(
//! "mqtt://localhost:1883?client_id=123",
//! &user,
//! &password,
//! 30 /* seconds */
//! )?;
//!
//! let payload = request.body().to_vec();
//! ensure_pet_picture(&payload)?;
//!
//! conn.publish("pet-pictures", &payload, Qos::AtLeastOnce)?;
//! # Ok(())
//! # }
//! ```

/// An open connection to an MQTT queue.
///
/// The address must be in URL form, and must include a `client_id`:
/// `mqtt://hostname?client_id=...`
///
/// # Examples
///
/// Send an MQTT message.
///
/// ```no_run
/// use spin_sdk::mqtt::{Connection, Qos};
///
/// # fn ensure_pet_picture(_: &[u8]) -> anyhow::Result<()> { Ok(()) }
/// # fn use_mqtt(request: spin_sdk::http::Request) -> anyhow::Result<()> {
/// let user = spin_sdk::variables::get("mqtt_username")?;
/// let password = spin_sdk::variables::get("mqtt_password")?;
///
/// let conn = Connection::open(
/// "mqtt://localhost:1883?client_id=123",
/// &user,
/// &password,
/// 30 /* seconds */
/// )?;
///
/// let payload = request.body().to_vec();
/// ensure_pet_picture(&payload)?;
///
/// conn.publish("pet-pictures", &payload, Qos::AtLeastOnce)?;
/// # Ok(())
/// # }
/// ```
pub use super::wit::v2::mqtt::Connection;

pub use super::wit::v2::mqtt::{Error, Payload, Qos};
108 changes: 106 additions & 2 deletions src/mysql.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
//! Conversions between Rust, WIT and **MySQL** types.
//! MySQL relational database storage.
//!
//! You can use the [`Decode`] trait to convert a [`DbValue`] to a
//! suitable Rust type. The following table shows available conversions.
//!
//! # Types
//!
Expand All @@ -18,8 +21,109 @@
//! | `String` | str(string) | VARCHAR, CHAR, TEXT |
//! | `Vec<u8>` | binary(list\<u8\>) | VARBINARY, BINARY, BLOB |

/// An open connection to a MySQL database.
///
/// # Examples
///
/// Load a set of rows from a local PostgreSQL database, and iterate over them.
///
/// ```no_run
/// use spin_sdk::mysql::{Connection, Decode, ParameterValue};
///
/// # fn main() -> anyhow::Result<()> {
/// # let min_age = 0;
/// let db = Connection::open("mysql://root:my_password@localhost/mydb")?;
///
/// let query_result = db.query(
/// "SELECT * FROM users WHERE age < ?",
/// &[ParameterValue::Int32(20)]
/// )?;
///
/// let name_index = query_result.columns.iter().position(|c| c.name == "name").unwrap();
///
/// for row in &query_result.rows {
/// let name = String::decode(&row[name_index])?;
/// println!("Found user {name}");
/// }
/// # Ok(())
/// # }
/// ```
///
/// Perform an aggregate (scalar) operation over a table. The result set
/// contains a single column, with a single row.
///
/// ```no_run
/// use spin_sdk::mysql::{Connection, Decode};
///
/// # fn main() -> anyhow::Result<()> {
/// let db = Connection::open("mysql://root:my_password@localhost/mydb")?;
///
/// let query_result = db.query("SELECT COUNT(*) FROM users", &[])?;
///
/// assert_eq!(1, query_result.columns.len());
/// assert_eq!("COUNT(*)", query_result.columns[0].name);
/// assert_eq!(1, query_result.rows.len());
///
/// let count = i64::decode(&query_result.rows[0][0])?;
/// # Ok(())
/// # }
/// ```
///
/// Delete rows from a MySQL table. This uses [Connection::execute()]
/// instead of the `query` method.
///
/// ```no_run
/// use spin_sdk::mysql::{Connection, ParameterValue};
///
/// # fn main() -> anyhow::Result<()> {
/// let db = Connection::open("mysql://root:my_password@localhost/mydb")?;
///
/// let rows_affected = db.execute(
/// "DELETE FROM users WHERE name = ?",
/// &[ParameterValue::Str("Baldrick".to_owned())]
/// )?;
/// # Ok(())
/// # }
/// ```
#[doc(inline)]
pub use super::wit::v2::mysql::Connection;

/// The result of a database query.
///
/// # Examples
///
/// Load a set of rows from a local PostgreSQL database, and iterate over them
/// selecting one field from each. The columns collection allows you to find
/// column indexes for column names; you can bypass this lookup if you name
/// specific columns in the query.
///
/// ```no_run
/// use spin_sdk::mysql::{Connection, Decode, ParameterValue};
///
/// # fn main() -> anyhow::Result<()> {
/// # let min_age = 0;
/// let db = Connection::open("mysql://root:my_password@localhost/mydb")?;
///
/// let query_result = db.query(
/// "SELECT * FROM users WHERE age >= ?",
/// &[ParameterValue::Int32(min_age)]
/// )?;
///
/// let name_index = query_result.columns.iter().position(|c| c.name == "name").unwrap();
///
/// for row in &query_result.rows {
/// let name = String::decode(&row[name_index])?;
/// println!("Found user {name}");
/// }
/// # Ok(())
/// # }
/// ```
#[doc(inline)]
pub use super::wit::v2::mysql::{Connection, Error as MysqlError};
pub use super::wit::v2::mysql::RowSet;

#[doc(inline)]
pub use super::wit::v2::mysql::Error as MysqlError;

#[doc(inline)]
pub use super::wit::v2::rdbms_types::*;

Expand Down
3 changes: 3 additions & 0 deletions src/pg.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Spin 2 Postgres relational database storage. Applications that do not require
//! Spin 2 support should use the [`pg3`](crate::pg3) module instead.
//!
//! Conversions between Rust, WIT and **Postgres** types.
//!
//! # Types
Expand Down
110 changes: 107 additions & 3 deletions src/pg3.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
//! Conversions between Rust, WIT and **Postgres** types.
//! Postgres relational database storage.
//!
//! You can use the [`into()`](std::convert::Into) method to convert
//! a Rust value into a [`ParameterValue`]. You can use the
//! [`Decode`] trait to convert a [`DbValue`] to a suitable Rust type.
//! The following table shows available conversions.
//!
//! # Types
//!
Expand All @@ -17,18 +22,117 @@
//! | `chrono::NaiveDateTime` | datetime(tuple<s32, u8, u8, u8, u8, u8, u32>) | TIMESTAMP |
//! | `chrono::Duration` | timestamp(s64) | BIGINT |

/// An open connection to a PostgreSQL database.
///
/// # Examples
///
/// Load a set of rows from a local PostgreSQL database, and iterate over them.
///
/// ```no_run
/// use spin_sdk::pg3::{Connection, Decode};
///
/// # fn main() -> anyhow::Result<()> {
/// # let min_age = 0;
/// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb")?;
///
/// let query_result = db.query(
/// "SELECT * FROM users WHERE age >= $1",
/// &[min_age.into()]
/// )?;
///
/// let name_index = query_result.columns.iter().position(|c| c.name == "name").unwrap();
///
/// for row in &query_result.rows {
/// let name = String::decode(&row[name_index])?;
/// println!("Found user {name}");
/// }
/// # Ok(())
/// # }
/// ```
///
/// Perform an aggregate (scalar) operation over a table. The result set
/// contains a single column, with a single row.
///
/// ```no_run
/// use spin_sdk::pg3::{Connection, Decode};
///
/// # fn main() -> anyhow::Result<()> {
/// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb")?;
///
/// let query_result = db.query("SELECT COUNT(*) FROM users", &[])?;
///
/// assert_eq!(1, query_result.columns.len());
/// assert_eq!("count", query_result.columns[0].name);
/// assert_eq!(1, query_result.rows.len());
///
/// let count = i64::decode(&query_result.rows[0][0])?;
/// # Ok(())
/// # }
/// ```
///
/// Delete rows from a PostgreSQL table. This uses [Connection::execute()]
/// instead of the `query` method.
///
/// ```no_run
/// use spin_sdk::pg3::Connection;
///
/// # fn main() -> anyhow::Result<()> {
/// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb")?;
///
/// let rows_affected = db.execute(
/// "DELETE FROM users WHERE name = $1",
/// &["Baldrick".to_owned().into()]
/// )?;
/// # Ok(())
/// # }
/// ```
#[doc(inline)]
pub use super::wit::pg3::Connection;

/// The result of a database query.
///
/// # Examples
///
/// Load a set of rows from a local PostgreSQL database, and iterate over them
/// selecting one field from each. The columns collection allows you to find
/// column indexes for column names; you can bypass this lookup if you name
/// specific columns in the query.
///
/// ```no_run
/// use spin_sdk::pg3::{Connection, Decode};
///
/// # fn main() -> anyhow::Result<()> {
/// # let min_age = 0;
/// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb")?;
///
/// let query_result = db.query(
/// "SELECT * FROM users WHERE age >= $1",
/// &[min_age.into()]
/// )?;
///
/// let name_index = query_result.columns.iter().position(|c| c.name == "name").unwrap();
///
/// for row in &query_result.rows {
/// let name = String::decode(&row[name_index])?;
/// println!("Found user {name}");
/// }
/// # Ok(())
/// # }
/// ```
pub use super::wit::pg3::RowSet;

#[doc(inline)]
pub use super::wit::pg3::{Error as PgError, *};

use chrono::{Datelike, Timelike};

/// A pg error
/// A Postgres error
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Failed to deserialize [`DbValue`]
#[error("error value decoding: {0}")]
Decode(String),
/// Pg query failed with an error
/// Postgres query failed with an error
#[error(transparent)]
PgError(#[from] PgError),
}
Expand Down
Loading
Loading