1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// This is free and unencumbered software released into the public domain.
//! Validated X (formerly Twitter) usernames.
//!
//! `XHandle` is available with `alloc` (enabled by the default `std` feature).
//! See the [shared handle contract](known_types::handle) 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
//!
//! ```
//! # #[cfg(feature = "alloc")]
//! # {
//! 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());
//! # }
//! # Ok::<(), known_types::handle::ParseHandleError>(())
//! ```
//!
//! # Serde
//!
//! Enable `serde` to encode handles as strings and validate on decode. For
//! `no_std` applications, also enable `alloc` explicitly:
//!
//! ```toml
//! [dependencies]
//! known-types-x = { version = "0.1", default-features = false, features = ["alloc", "serde"] }
//! serde_json = { version = "1", default-features = false, features = ["alloc"] }
//! ```
//!
//! ```
//! # #[cfg(all(feature = "alloc", feature = "serde"))]
//! # {
//! 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());
//! # }
//! # Ok::<(), serde_json::Error>(())
//! ```
//!
//! # GraphQL
//!
//! The `async-graphql` feature enables `std` and works without `serde` on this
//! crate. Scalars accept only strings and validate them with `FromStr`.
//!
//! ```toml
//! [dependencies]
//! known-types-x = { version = "0.1", default-features = false, features = ["async-graphql"] }
//! async-graphql = { version = "7.2", default-features = false }
//! ```
//!
//! ```
//! # #[cfg(feature = "async-graphql")]
//! # futures_executor::block_on(async {
//! 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.
//!
//! ```
//! # #[cfg(feature = "async-graphql")]
//! # {
//! 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);
//! # }
//! # Ok::<(), known_types::handle::ParseHandleError>(())
//! ```
//!
//! # 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`.
//!
//! ```toml
//! [dependencies]
//! known-types-x = { version = "0.1", default-features = false, features = ["sqlx-postgres"] }
//! sqlx = { version = "0.9", default-features = false, features = ["postgres", "runtime-tokio"] }
//! ```
//!
//! ```no_run
//! # #[cfg(feature = "sqlx-postgres")]
//! # {
//! 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:
//!
//! ```
//! # #[cfg(feature = "sqlx-sqlite")]
//! # {
//! # futures_executor::block_on(async {
//! 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 { .. })));
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! # })?;
//! # }
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! 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.
//#[cfg(doctest)]
//#[doc = include_str!("../README.md")]
//pub struct ReadmeDoctests;
extern crate alloc;
extern crate std;
pub use *;