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
bincodeandserdeas dependencies toCargo.toml. Models need to be serializable in order to be written to a.exo_irfile.[dependencies]
bincode = "1"
serde = "1"
core-plugin-interface.workspace = true -
Clear
lib.rs. -
Create a
types.rsmodule 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>,
}-
Typerepresents a type that the plugin can work with. It consists of a name and a kind (TypeKind). -
TypeKinddefines what kind of type a ``Type` is.Primitives are predefined scalar types that we've brought in from Exograph (e.g.String,Int, etc.)Composites are types that a user might have defined in their.exofile in their module declaration using atypeblock:
@kv
module KeyValueModule {
type ExampleType {
field: String
}
}infoAlthough a
typeblock may hold fields that are of a non-scalar type, ourComposites will only be allowed to be composed of primitive fields for the sake of simplicity.
Define a
SystemSerializerimplementation 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)
}
}SystemSerializeris a helper trait that defines serialization and deserialization methods for models. generically. Although we usebincodehere, the model can be serialized into any format that can be represented by aVec<u8>. -
-
Create a
operations.rsmodule 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-pluginwill need to keep track of allTypesdeclared by both Exograph and the user, as well as the necessary queries and mutations it needs to support.