Skip to main content

rpkl_jdx/internal/msgapi/
outgoing.rs

1use std::collections::HashMap;
2use crate::value::value::MapImpl;
3
4use crate::internal::msgapi::codes::*;
5use serde::Serialize;
6
7use crate::{
8    api::{
9        evaluator::CREATE_EVALUATOR_REQUEST_ID,
10        reader::{PklModuleReader, PklResourceReader},
11    },
12    internal::msgapi::impl_pkl_message,
13    EvaluatorOptions,
14};
15
16impl From<&dyn PklModuleReader> for ClientModuleReader {
17    fn from(reader: &dyn PklModuleReader) -> Self {
18        ClientModuleReader {
19            scheme: reader.scheme().to_string(),
20            has_hierarchical_uris: reader.has_hierarchical_uris(),
21            is_globbable: reader.is_globbable(),
22            is_local: reader.is_local(),
23        }
24    }
25}
26
27impl From<&dyn PklResourceReader> for ClientResourceReader {
28    fn from(reader: &dyn PklResourceReader) -> Self {
29        ClientResourceReader {
30            scheme: reader.scheme().to_string(),
31            has_hierarchical_uris: reader.has_hierarchical_uris(),
32            is_globbable: reader.is_globbable(),
33        }
34    }
35}
36
37#[derive(Serialize)]
38#[serde(rename_all = "camelCase")]
39pub(crate) struct CreateEvaluator<'a> {
40    pub request_id: u64,
41    pub allowed_modules: Vec<String>,
42    pub allowed_resources: Vec<String>,
43
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub client_module_readers: Option<Vec<ClientModuleReader>>,
46
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub client_resource_readers: Option<Vec<ClientResourceReader>>,
49
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub env: Option<HashMap<String, String>>,
52
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub properties: Option<&'a MapImpl<String, String>>,
55
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub external_resource_readers: Option<&'a HashMap<String, ExternalReader>>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub external_module_readers: Option<&'a HashMap<String, ExternalReader>>,
60}
61
62impl Default for CreateEvaluator<'_> {
63    fn default() -> Self {
64        let env_vars: HashMap<String, String> = std::env::vars().collect();
65        Self {
66            request_id: CREATE_EVALUATOR_REQUEST_ID,
67            allowed_modules: vec![
68                "pkl:".into(),
69                "repl:".into(),
70                "file:".into(),
71                "https:".into(),
72                "package:".into(),
73            ],
74            allowed_resources: vec![
75                "env:".into(),
76                "prop:".into(),
77                "package:".into(),
78                "https:".into(),
79                "projectpackage:".into(),
80            ],
81            env: Some(env_vars),
82
83            client_module_readers: None,
84            client_resource_readers: None,
85            properties: None,
86            external_resource_readers: None,
87            external_module_readers: None,
88        }
89    }
90}
91
92impl<'a> From<&'a EvaluatorOptions> for CreateEvaluator<'a> {
93    fn from(opts: &'a EvaluatorOptions) -> Self {
94        let mut evaluator_message = CreateEvaluator::default();
95
96        /* handle user defined options */
97        {
98            if let Some(props) = &opts.properties {
99                evaluator_message.properties = Some(props);
100            }
101
102            if let Some(readers) = opts.client_module_readers.as_ref() {
103                for reader in readers.iter() {
104                    evaluator_message
105                        .allowed_modules
106                        .push(reader.scheme().to_string());
107                }
108                let module_readers: Vec<ClientModuleReader> = readers
109                    .iter()
110                    .map(|r: &Box<dyn PklModuleReader>| r.as_ref().into())
111                    .collect();
112                evaluator_message.client_module_readers = Some(module_readers);
113            }
114
115            if let Some(readers) = opts.client_resource_readers.as_ref() {
116                for reader in readers.iter() {
117                    evaluator_message
118                        .allowed_resources
119                        .push(reader.scheme().to_string());
120                }
121                let resource_readers: Vec<ClientResourceReader> =
122                    readers.iter().map(|r| r.as_ref().into()).collect();
123                evaluator_message.client_resource_readers = Some(resource_readers);
124            }
125
126            if let Some(readers) = &opts.external_resource_readers {
127                for uri in readers.keys() {
128                    evaluator_message.allowed_resources.push(uri.clone());
129                }
130
131                evaluator_message.external_resource_readers = Some(readers);
132            }
133
134            if let Some(readers) = &opts.external_module_readers {
135                for uri in readers.keys() {
136                    evaluator_message.allowed_modules.push(uri.clone());
137                }
138                evaluator_message.external_module_readers = Some(readers);
139            }
140        }
141        /* */
142
143        evaluator_message
144    }
145}
146
147#[derive(Serialize)]
148#[serde(rename_all = "camelCase")]
149pub struct ClientModuleReader {
150    pub scheme: String,
151    pub has_hierarchical_uris: bool,
152    pub is_globbable: bool,
153    pub is_local: bool,
154}
155
156#[derive(Serialize)]
157#[serde(rename_all = "camelCase")]
158pub struct ClientResourceReader {
159    /// The URI scheme this reader is responsible for reading.
160    pub scheme: String,
161
162    /// Tells whether the path part of ths URI has a
163    /// [hier-part](https://datatracker.ietf.org/doc/html/rfc3986#section-3).
164    ///
165    /// An example of a hierarchical URI is `file:///path/to/my/file`, where
166    /// `/path/to/my/file` designates a nested path through the `/` character.
167    ///
168    /// An example of a non-hierarchical URI is `pkl.base`, where the `base` does not denote
169    /// any form of hierarchy.
170    pub has_hierarchical_uris: bool,
171
172    /// Tells whether this reader supports globbing.
173    pub is_globbable: bool,
174}
175
176#[derive(Serialize)]
177#[serde(rename_all = "camelCase")]
178pub struct CloseEvaluator {
179    pub evaluator_id: i64,
180}
181
182#[derive(Serialize)]
183#[serde(rename_all = "camelCase")]
184pub struct EvaluateRequest {
185    pub request_id: u64,
186    pub evaluator_id: i64,
187    pub module_uri: String,
188}
189
190/// Code: 0x27
191///
192/// Type: Client Response
193#[derive(Serialize)]
194#[serde(rename_all = "camelCase")]
195pub(crate) struct ReadResourceResponse {
196    /// A number identifying this request.
197    pub request_id: i64,
198
199    /// A number identifying this evaluator.
200    pub evaluator_id: i64,
201
202    /// The contents of the resource.
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub contents: Option<Vec<u8>>,
205
206    /// The description of the error that occured when reading this resource.
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub error: Option<String>,
209}
210
211/// Code: 0x29
212///
213/// Type: Client Response
214#[derive(Serialize)]
215#[serde(rename_all = "camelCase")]
216pub(crate) struct ReadModuleResponse {
217    /// A number identifying this request.
218    pub request_id: i64,
219
220    /// A number identifying this evaluator.
221    pub evaluator_id: i64,
222
223    /// The contents of the module.
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub contents: Option<String>,
226
227    /// The description of the error that occured when reading this resource.
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub error: Option<String>,
230}
231
232/// Code: 0x103
233/// Type: Client Response
234#[derive(Serialize)]
235#[serde(rename_all = "camelCase")]
236pub(crate) struct InitializeResourceReaderResponse {
237    /// A number identifying this request.
238    pub request_id: i64,
239
240    /// Client-side resource reader spec.
241    ///
242    /// Null when the external process does not implement the requested scheme.
243    /// [ClientResourceReader] is defined at https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html#create-evaluator-request
244    ///
245    ///
246    #[serde(skip_serializing_if = "Option::is_none")]
247    pub spec: Option<ClientResourceReader>,
248}
249
250/// Code: 0x2f
251/// Type: Client Response
252#[derive(Serialize)]
253#[serde(rename_all = "camelCase")]
254pub(crate) struct InitializeModuleReaderResponse {
255    /// A number identifying this request.
256    pub request_id: i64,
257
258    /// Client-side resource reader spec.
259    ///
260    /// Null when the external process does not implement the requested scheme.
261    /// [ClientResourceReader] is defined at https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html#create-evaluator-request
262    ///
263    ///
264    #[serde(skip_serializing_if = "Option::is_none")]
265    pub spec: Option<ClientModuleReader>,
266}
267
268/// Code: 0x2a
269/// Type: Server Request
270#[derive(Serialize)]
271#[serde(rename_all = "camelCase")]
272pub(crate) struct ListResourcesResponse {
273    /// A number identifying this request.
274    pub request_id: i64,
275
276    /// A number identifying this evaluator.
277    pub evaluator_id: i64,
278
279    /// The elements at the provided base path.
280    #[serde(skip_serializing_if = "Option::is_none")]
281    pub path_elements: Option<Vec<PathElements>>,
282
283    /// The description of the error that occured when listing elements.
284    #[serde(skip_serializing_if = "Option::is_none")]
285    pub error: Option<String>,
286}
287
288/// Code: 0x2c
289/// Type: Server Request
290#[derive(Serialize)]
291#[serde(rename_all = "camelCase")]
292pub(crate) struct ListModulesResponse {
293    /// A number identifying this request.
294    pub request_id: i64,
295
296    /// A number identifying this evaluator.
297    pub evaluator_id: i64,
298
299    /// The elements at the provided base path.
300    #[serde(skip_serializing_if = "Option::is_none")]
301    pub path_elements: Option<Vec<PathElements>>,
302
303    /// The description of the error that occured when listing elements.
304    #[serde(skip_serializing_if = "Option::is_none")]
305    pub error: Option<String>,
306}
307
308#[derive(Serialize)]
309#[serde(rename_all = "camelCase")]
310pub struct PathElements {
311    /// The name of the element at this path
312    pub name: String,
313
314    /// Tells whether the element is a directory.
315    pub is_directory: bool,
316}
317
318#[derive(Serialize)]
319pub struct ExternalReader {
320    /// May be specified as an absolute path to an executable
321    /// May also be specified as just an executable name, in which case it will be resolved according to the PATH environment variable
322    pub executable: String,
323
324    /// Command line arguments that will be passed to the reader process
325    pub arguments: Vec<String>,
326}
327
328impl_pkl_message!(CreateEvaluator<'a>, CREATE_EVALUATOR);
329impl_pkl_message!(EvaluateRequest, EVALUATE_REQUEST);
330impl_pkl_message!(CloseEvaluator, CLOSE);
331
332// region: Reader messages
333impl_pkl_message!(
334    InitializeResourceReaderResponse,
335    INITIALIZE_RESOURCE_READER_RESPONSE
336);
337impl_pkl_message!(
338    InitializeModuleReaderResponse,
339    INITIALIZE_MODULE_READER_RESPONSE
340);
341impl_pkl_message!(ReadResourceResponse, READ_RESOURCE_RESPONSE);
342impl_pkl_message!(ReadModuleResponse, READ_MODULE_RESPONSE);
343impl_pkl_message!(ListResourcesResponse, LIST_RESOURCES_RESPONSE);
344impl_pkl_message!(ListModulesResponse, LIST_MODULES_RESPONSE);
345// endregion