Skip to main content

lineark_sdk/
field_selection.rs

1//! Type-driven GraphQL field selection.
2//!
3//! Each type that implements [`GraphQLFields`] knows how to describe its
4//! own GraphQL selection string. Generated types return all their scalar
5//! fields. Consumers can define custom lean types with only the fields
6//! they need — the struct shape *is* the query shape.
7//!
8//! The `FullType` associated type provides compile-time validation:
9//! - Generated types implement `GraphQLFields` with `FullType = Self`
10//! - Custom types use `#[graphql(full_type = X)]` to validate fields at compile time
11
12/// Trait implemented by types that know their GraphQL field selection.
13///
14/// The `FullType` associated type ties this implementation to a specific
15/// GraphQL schema type, enabling compile-time validation:
16/// - **Generated types** set `FullType = Self` — they validate against themselves.
17/// - **Custom lean types** set `FullType` to the corresponding generated type,
18///   enabling compile-time field existence and type checks via `#[graphql(full_type = X)]`.
19///
20/// # Example
21///
22/// ```ignore
23/// use lineark_sdk::generated::types::Team;
24///
25/// #[derive(Deserialize, GraphQLFields)]
26/// #[graphql(full_type = Team)]
27/// struct TeamRow {
28///     id: String,
29///     key: String,
30///     name: String,
31/// }
32///
33/// // Compile-time validated: TeamRow fields exist on Team with compatible types
34/// let teams = client.teams::<TeamRow>().first(10).send().await?;
35/// ```
36pub trait GraphQLFields {
37    /// The full generated type this implementation validates against.
38    type FullType;
39
40    /// Return the GraphQL field selection string for this type.
41    ///
42    /// For flat types, this is just space-separated field names.
43    /// For types with nested objects, include sub-selections:
44    /// `"id title team { id name }"`.
45    fn selection() -> String;
46}
47
48/// Marker trait for compile-time field type compatibility.
49///
50/// Validates that a full type's field type `Self` is compatible with a custom
51/// type's field type `Custom`. Covers common wrapping patterns used in
52/// generated types (`Option`, `Box`, `Vec`).
53pub trait FieldCompatible<Custom> {}
54
55// Exact match
56impl<T> FieldCompatible<T> for T {}
57
58// Unwrap Option: full type has Option<T>, custom type has T
59impl<T> FieldCompatible<T> for Option<T> {}
60
61// Unwrap Option<Box<T>>: full type has Option<Box<T>>, custom type has T
62impl<T> FieldCompatible<T> for Option<Box<T>> {}
63
64// Unbox, keep Option: full type has Option<Box<T>>, custom type has Option<T>
65impl<T> FieldCompatible<Option<T>> for Option<Box<T>> {}
66
67// Cross-type: DateTime serializes as ISO 8601 string in JSON
68impl FieldCompatible<String> for chrono::DateTime<chrono::Utc> {}
69impl FieldCompatible<Option<String>> for Option<chrono::DateTime<chrono::Utc>> {}
70impl FieldCompatible<String> for Option<chrono::DateTime<chrono::Utc>> {}