Writing the plugin model
We will now start writing the plugin's model in the kv-model
crate. A plugin's model defines the structure it uses to keep track of everything it needs to successfully resolve operations. It is parsed from a user's .exo
file.
-
Add
bincode
andserde
as dependencies toCargo.toml
. Models need to be serializable in order to be written to a.exo_ir
file.[dependencies]
bincode = "1"
serde = "1"
core-plugin-interface.workspace = true -
Clear
lib.rs
. -
Create a
types.rs
module in the crate. We will define the plugin's inner types here.lib.rs
mod types;
Define the following structs:
types.rs
use core_plugin_interface::core_model::mapped_arena::SerializableSlabIndex;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct Type {
pub name: String,
pub kind: TypeKind,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum TypeKind {
Primitive,
Composite { fields: Vec<KvCompositeField> },
CompositeInput { fields: Vec<KvCompositeField> },
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct KvCompositeField {
pub name: String,
pub type_name: String,
pub type_id: SerializableSlabIndex<Type>,
}-
Type
represents a type that the plugin can work with. It consists of a name and a kind (TypeKind
). -
TypeKind
defines what kind of type a ``Type` is.Primitive
s are predefined scalar types that we've brought in from Exograph (e.g.String
,Int
, etc.)Composite
s are types that a user might have defined in their.exo
file in their module declaration using atype
block:
@kv
module KeyValueModule {
type ExampleType {
field: String
}
}infoAlthough a
type
block may hold fields that are of a non-scalar type, ourComposite
s will only be allowed to be composed of primitive fields for the sake of simplicity.
Define a
SystemSerializer
implementation forKvModelSystem
.impl SystemSerializer for KvModelSystem {
type Underlying = Self;
fn serialize(&self) -> Result<Vec<u8>, ModelSerializationError> {
bincode::serialize(&self).map_err(ModelSerializationError::Serialize)
}
fn deserialize_reader(
reader: impl std::io::Read,
) -> Result<Self::Underlying, ModelSerializationError> {
bincode::deserialize_from(reader).map_err(ModelSerializationError::Deserialize)
}
}SystemSerializer
is a helper trait that defines serialization and deserialization methods for models. generically. Although we usebincode
here, the model can be serialized into any format that can be represented by aVec<u8>
. -
-
Create a
operations.rs
module in the crate. We will define what the plugin's queries and mutations look like in here.lib.rs
mod operations;
Define the following structs:
operations.rs
use core_plugin_interface::core_model::mapped_arena::SerializableSlabIndex;
use serde::{Deserialize, Serialize};
use crate::types::Type;
#[derive(Debug, Serialize, Deserialize)]
pub struct Query {
pub name: String,
pub arguments: Vec<Argument>,
pub return_type: OperationReturnType,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Mutation {
pub name: String,
pub arguments: Vec<Argument>,
pub return_type: OperationReturnType,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct OperationReturnType {
pub type_name: String,
pub type_id: SerializableSlabIndex<Type>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Argument {
pub name: String,
pub type_name: String,
pub type_id: SerializableSlabIndex<Type>,
} -
In
lib.rs
, defineKvModelSystem
.#[derive(Debug, Serialize, Deserialize)]
pub struct KvModelSystem {
pub types: MappedArena<Type>,
pub queries: MappedArena<Query>,
pub mutations: MappedArena<Mutation>,
}exograph-kv-plugin
will need to keep track of allTypes
declared by both Exograph and the user, as well as the necessary queries and mutations it needs to support.