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§
- Parse
Handle Error - An invalid social media handle.