Skip to main content

Crate known_types_x

Crate known_types_x 

Source
Expand description

Validated X (formerly Twitter) usernames.

XHandle is available with alloc (enabled by the default std feature). See the shared handle contract for conversion, identity, and validation rules. The Serde, GraphQL, and SQLx recipes also apply to other platform handles, using their own parsing rules and GraphQL scalar names.

§Parsing handles

use known_types_x::XHandle;

let handle: XHandle = "@PlayItAgainSam".parse()?;
assert_eq!(handle.as_str(), "PlayItAgainSam");
assert_eq!(handle, "playitagainsam".parse::<XHandle>()?);
assert!("not a handle".parse::<XHandle>().is_err());

§Serde

Enable serde to encode handles as strings and validate on decode. For no_std applications, also enable alloc explicitly:

[dependencies]
known-types-x = { version = "0.1", default-features = false, features = ["alloc", "serde"] }
serde_json = { version = "1", default-features = false, features = ["alloc"] }
use known_types_x::XHandle;

let handle: XHandle = serde_json::from_str(r#""@Some_User""#)?;
assert_eq!(handle.as_str(), "Some_User");
assert_eq!(serde_json::to_string(&handle)?, r#""Some_User""#);
assert!(serde_json::from_str::<XHandle>(r#""bad handle""#).is_err());

§GraphQL

The async-graphql feature enables std and works without serde on this crate. Scalars accept only strings and validate them with FromStr.

[dependencies]
known-types-x = { version = "0.1", default-features = false, features = ["async-graphql"] }
async-graphql = { version = "7.2", default-features = false }
use async_graphql::{EmptyMutation, EmptySubscription, Object, Schema, value};
use known_types_x::XHandle;

struct Query;

#[Object]
impl Query {
    async fn handle(&self, input: XHandle) -> XHandle {
        input
    }
}

let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let response = schema.execute(r#"{ handle(input: "@Some_User") }"#).await;
assert!(response.errors.is_empty());
assert_eq!(response.data, value!({"handle": "Some_User"}));

The field is handle(input: XHandle!): XHandle!. Variables must use the scalar’s name, for example query($handle: XHandle!) { handle(input: $handle) } with {"handle": "Some_User"}. Handles also work in input/output objects, optional fields, and lists.

§Connection cursors

Cursors encode the stored spelling verbatim and validate on decode. Use a handle as a cursor when it is the connection’s unique ordering key; ordering and rename handling remain the application’s responsibility.

use async_graphql::connection::{Connection, CursorType, Edge};
use known_types_x::XHandle;

let handle: XHandle = "Some_User".parse()?;
let mut connection: Connection<XHandle, XHandle> = Connection::new(false, false);
connection.edges.push(Edge::new(handle.clone(), handle.clone()));
assert_eq!(handle.encode_cursor(), "Some_User");
assert_eq!(XHandle::decode_cursor(&handle.encode_cursor())?, handle);

§SQLx

Select sqlx-postgres, sqlx-mysql, or sqlx-sqlite; each enables std. If the application already enables a SQLx driver, sqlx alone supplies the scalar traits. Handles bind by value or reference, support Option<XHandle> for nullable columns, and validate database text on decode. PostgreSQL text arrays additionally support Vec<XHandle> with sqlx-postgres.

[dependencies]
known-types-x = { version = "0.1", default-features = false, features = ["sqlx-postgres"] }
sqlx = { version = "0.9", default-features = false, features = ["postgres", "runtime-tokio"] }
use known_types_x::XHandle;

async fn round_trip(pool: &sqlx::PgPool, handle: &XHandle) -> Result<XHandle, sqlx::Error> {
    sqlx::query_scalar("SELECT $1::text")
        .bind(handle)
        .fetch_one(pool)
        .await
}

For SQLite, enable sqlx-sqlite instead. This in-memory example needs no external database:

use known_types_x::XHandle;
use sqlx::{Connection, SqliteConnection};

let mut connection = SqliteConnection::connect("sqlite::memory:").await?;
let handle: XHandle = "@Some_User".parse()?;
let decoded: XHandle = sqlx::query_scalar("SELECT ?")
    .bind(&handle)
    .fetch_one(&mut connection)
    .await?;
assert_eq!(decoded.as_str(), handle.as_str());

let invalid = sqlx::query_scalar::<_, XHandle>("SELECT 'bad handle'")
    .fetch_one(&mut connection)
    .await;
assert!(matches!(invalid, Err(sqlx::Error::ColumnDecode { .. })));

For SQLx’s compile-time query macros (its macros feature), provide an explicit column type override: SELECT handle AS "handle: XHandle" with query!, or SELECT handle AS "handle: _" with query_as! and a struct field of type XHandle. Runtime selection and compile-time database configuration belong to the application.

§libsql

With libsql, an owned or borrowed XHandle converts into libsql::Value::Text, preserving its stored spelling. Select alloc,libsql when default features are disabled. This integration currently supplies value encoding only; it does not decode database values into handles.

Structs§

XHandle
An X handle (aka username).

Enums§

ParseHandleError
An invalid social media handle.