Skip to main content

alux_jsonrpc/
program.rs

1use crate::{JsonRpcAlg, JsonRpcApiAlg, JsonRpcFallibleAlg, JsonRpcMethodAlg, JsonRpcProgramAlg};
2use alux_ext::{ApplyAlg, HandlerContextAlg, OperationAlg};
3use core::marker::PhantomData;
4
5/// Compiles a first-order JSON-RPC program with a concrete interpreter.
6pub trait CompileJsonRpcProgram<Compiler> {
7    /// The method collection produced by `Compiler`.
8    type Methods;
9
10    /// Folds the complete first-order program through `compiler`.
11    fn compile_jsonrpc_program(self, compiler: &Compiler) -> Self::Methods;
12}
13
14/// Represents the empty JSON-RPC program.
15#[derive(Debug, Default)]
16pub struct Empty;
17
18/// Represents the composition of two JSON-RPC programs.
19#[derive(Debug)]
20pub struct Merge<Left, Right> {
21    left: Left,
22    right: Right,
23}
24
25/// Includes a separately named JSON-RPC program.
26#[derive(Debug)]
27pub struct Named<Program>(Program);
28
29/// Selects positional JSON-RPC parameter decoding.
30#[derive(Debug, Default)]
31pub struct Positional;
32
33/// Selects named JSON-RPC parameter decoding.
34#[derive(Debug, Default)]
35pub struct NamedParams;
36
37/// Selects positional decoding for a method whose error answers as a protocol error.
38#[derive(Debug, Default)]
39pub struct FalliblePositional;
40
41/// Selects named decoding for a method whose error answers as a protocol error.
42#[derive(Debug, Default)]
43pub struct FallibleNamed;
44
45/// Represents one named JSON-RPC method without choosing an interpreter.
46#[derive(Debug)]
47pub struct Method<Handler, Params = Positional> {
48    name: &'static str,
49    handler: Handler,
50    marker: PhantomData<fn(Params)>,
51}
52
53/// Carries a typed operation declaration as first-order data.
54#[derive(Debug)]
55pub struct Operation<Handler, Params = Positional> {
56    handler: Handler,
57    marker: PhantomData<fn(Params)>,
58}
59
60/// Carries a typed JSON-RPC program during fluent composition.
61#[derive(Debug)]
62pub struct JsonRpcProgram<Program>(Program);
63
64/// Constructs neutral JSON-RPC programs.
65#[derive(Debug, Default)]
66pub struct JsonRpcProgramBuilder;
67
68impl JsonRpcProgramBuilder {
69    /// Starts an empty, uninterpreted JSON-RPC program.
70    pub fn methods(&self) -> JsonRpcProgram<Empty> {
71        JsonRpcProgram(Empty)
72    }
73
74    /// Wraps a first-order handler operation in a neutral declaration.
75    pub fn op<Handler>(&self, handler: Handler) -> Operation<Handler> {
76        Operation { handler, marker: PhantomData }
77    }
78
79    /// Includes a named JSON-RPC program in another program.
80    pub fn program<Program>(&self, program: Program) -> JsonRpcProgram<Named<Program>> {
81        JsonRpcProgram(Named(program))
82    }
83}
84
85impl<Program> JsonRpcProgram<Program> {
86    /// Records the composition of two typed JSON-RPC programs.
87    pub fn merge<Other>(self, other: JsonRpcProgram<Other>) -> JsonRpcProgram<Merge<Program, Other>> {
88        JsonRpcProgram(Merge { left: self.0, right: other.0 })
89    }
90
91    /// Registers a typed operation under a JSON-RPC method name.
92    pub fn method<Handler, Params>(
93        self,
94        name: &'static str,
95        operation: Operation<Handler, Params>,
96    ) -> JsonRpcProgram<Merge<Program, Method<Handler, Params>>> {
97        self.merge(JsonRpcProgram(Method { name, handler: operation.handler, marker: PhantomData }))
98    }
99
100    /// Removes the fluent wrapper and returns the first-order syntax tree.
101    pub fn into_program(self) -> Program {
102        self.0
103    }
104}
105
106impl<Handler, Params> Operation<Handler, Params> {
107    /// Declares this operation under one method name, with no program around it.
108    ///
109    /// A program that states many methods composes their registrations rather than their types, so
110    /// it needs each method on its own. `JsonRpcProgram::method` states the same thing inside a
111    /// composition, and is what an author writes.
112    pub fn declare(self, name: &'static str) -> Method<Handler, Params> {
113        Method { name, handler: self.handler, marker: PhantomData }
114    }
115
116    /// Reads this declaration under another parameter or failure mode.
117    fn retyped<Mode>(self) -> Operation<Handler, Mode> {
118        Operation { handler: self.handler, marker: PhantomData }
119    }
120}
121
122impl<Handler> Operation<Handler, Positional> {
123    /// Selects positional parameter decoding for this operation declaration.
124    ///
125    /// Positional decoding is already the default, so this modifier is useful
126    /// when a declaration benefits from making the wire shape explicit.
127    #[must_use]
128    pub fn positional(self) -> Self {
129        self
130    }
131
132    /// Selects named parameter decoding using the defunctionalized operation's
133    /// source argument names.
134    pub fn named(self) -> Operation<Handler, NamedParams> {
135        self.retyped()
136    }
137
138    /// Converts the operation's error into a JSON-RPC protocol error.
139    pub fn fallible(self) -> Operation<Handler, FalliblePositional> {
140        self.retyped()
141    }
142}
143
144impl<Handler> Operation<Handler, NamedParams> {
145    /// Converts the operation's error into a JSON-RPC protocol error.
146    pub fn fallible(self) -> Operation<Handler, FallibleNamed> {
147        self.retyped()
148    }
149}
150
151impl<Handler> Operation<Handler, FalliblePositional> {
152    /// Selects named parameter decoding, keeping the failure mode.
153    pub fn named(self) -> Operation<Handler, FallibleNamed> {
154        self.retyped()
155    }
156}
157
158impl<Compiler> CompileJsonRpcProgram<Compiler> for Empty
159where
160    Compiler: JsonRpcAlg,
161{
162    type Methods = Compiler::Methods;
163
164    fn compile_jsonrpc_program(self, compiler: &Compiler) -> Self::Methods {
165        compiler.jsonrpc_empty()
166    }
167}
168
169impl<Compiler, Left, Right, Methods> CompileJsonRpcProgram<Compiler> for Merge<Left, Right>
170where
171    Compiler: JsonRpcAlg<Methods = Methods>,
172    Left: CompileJsonRpcProgram<Compiler, Methods = Methods>,
173    Right: CompileJsonRpcProgram<Compiler, Methods = Methods>,
174{
175    type Methods = Methods;
176
177    fn compile_jsonrpc_program(self, compiler: &Compiler) -> Self::Methods {
178        compiler
179            .jsonrpc_merge(self.left.compile_jsonrpc_program(compiler), self.right.compile_jsonrpc_program(compiler))
180    }
181}
182
183impl<Compiler, Program> CompileJsonRpcProgram<Compiler> for Named<Program>
184where
185    Program: JsonRpcProgramAlg<Compiler>,
186{
187    type Methods = Program::Methods;
188
189    fn compile_jsonrpc_program(self, compiler: &Compiler) -> Self::Methods {
190        self.0.compile_jsonrpc(compiler)
191    }
192}
193
194// One implementation per mode, so each states the capability its registration needs and no mode is
195// reachable through another's bounds.
196impl<Compiler, Handler, Handle> CompileJsonRpcProgram<Compiler> for Method<Handler, Positional>
197where
198    Compiler: JsonRpcApiAlg
199        + HandlerContextAlg<Handler::Context, Handle = Handle>
200        + JsonRpcMethodAlg<Handle, Handler::Args, Handler::Output>,
201    Handler: OperationAlg + ApplyAlg<Handle, Handler::Args> + Send + Sync + 'static,
202{
203    type Methods = Compiler::Methods;
204
205    fn compile_jsonrpc_program(self, compiler: &Compiler) -> Self::Methods {
206        compiler.finish_jsonrpc_positional_method(self.name, Handler::ARG_NAMES, self.handler)
207    }
208}
209
210impl<Compiler, Handler, Handle> CompileJsonRpcProgram<Compiler> for Method<Handler, NamedParams>
211where
212    Compiler: JsonRpcApiAlg
213        + HandlerContextAlg<Handler::Context, Handle = Handle>
214        + JsonRpcMethodAlg<Handle, Handler::Args, Handler::Output>,
215    Handler: OperationAlg + ApplyAlg<Handle, Handler::Args> + Send + Sync + 'static,
216{
217    type Methods = Compiler::Methods;
218
219    fn compile_jsonrpc_program(self, compiler: &Compiler) -> Self::Methods {
220        compiler.finish_jsonrpc_named_method(self.name, Handler::ARG_NAMES, self.handler)
221    }
222}
223
224impl<Compiler, Handler, Handle> CompileJsonRpcProgram<Compiler> for Method<Handler, FalliblePositional>
225where
226    Compiler: JsonRpcApiAlg
227        + HandlerContextAlg<Handler::Context, Handle = Handle>
228        + JsonRpcFallibleAlg<Handle, Handler::Args, Handler::Output>,
229    Handler: OperationAlg + ApplyAlg<Handle, Handler::Args> + Send + Sync + 'static,
230{
231    type Methods = Compiler::Methods;
232
233    fn compile_jsonrpc_program(self, compiler: &Compiler) -> Self::Methods {
234        compiler.finish_jsonrpc_positional_fallible(self.name, Handler::ARG_NAMES, self.handler)
235    }
236}
237
238impl<Compiler, Handler, Handle> CompileJsonRpcProgram<Compiler> for Method<Handler, FallibleNamed>
239where
240    Compiler: JsonRpcApiAlg
241        + HandlerContextAlg<Handler::Context, Handle = Handle>
242        + JsonRpcFallibleAlg<Handle, Handler::Args, Handler::Output>,
243    Handler: OperationAlg + ApplyAlg<Handle, Handler::Args> + Send + Sync + 'static,
244{
245    type Methods = Compiler::Methods;
246
247    fn compile_jsonrpc_program(self, compiler: &Compiler) -> Self::Methods {
248        compiler.finish_jsonrpc_named_fallible(self.name, Handler::ARG_NAMES, self.handler)
249    }
250}