Skip to main content

rpkl_jdx/api/
external_reader.rs

1use std::io::Write;
2
3use crate::internal::msgapi::{
4    codes::{
5        CLOSE_EXTERNAL_PROCESS, INITIALIZE_MODULE_READER_REQUEST,
6        INITIALIZE_RESOURCE_READER_REQUEST, LIST_MODULES_REQUEST, LIST_RESOURCES_REQUEST,
7        READ_MODULE_REQUEST, READ_RESOURCE_REQUEST,
8    },
9    incoming::PklServerMessage,
10    outgoing::{
11        ClientModuleReader, ClientResourceReader, InitializeModuleReaderResponse,
12        InitializeResourceReaderResponse,
13    },
14    PklMessage,
15};
16
17use crate::{
18    api::{
19        evaluator::recv_msg,
20        reader::{
21            handle_list_modules, handle_list_resources, handle_read_module, handle_read_resource,
22        },
23    },
24    utils::macros::{_info, _warn},
25};
26
27pub struct ExternalReaderRuntime {
28    resource_readers: Vec<Box<dyn PklResourceReader>>,
29    module_readers: Vec<Box<dyn PklModuleReader>>,
30}
31
32impl ExternalReaderRuntime {
33    pub fn new() -> Self {
34        Self {
35            resource_readers: Vec::new(),
36            module_readers: Vec::new(),
37        }
38    }
39
40    /// Add a single, or tuple of resource readers to the client.
41    ///
42    /// Panics if any of the readers have the same scheme.
43    pub fn add_resource_readers<T: IntoResourceReaders>(&mut self, readers: T) -> &mut Self {
44        let readers = readers.into_readers();
45        self.resource_readers.extend(readers);
46
47        for (i, reader) in self.resource_readers.iter().enumerate() {
48            for other in &self.resource_readers[i + 1..] {
49                if reader.scheme() == other.scheme() {
50                    panic!(
51                        "Multiple resource readers sharing the same scheme: {}",
52                        reader.scheme()
53                    );
54                }
55            }
56        }
57
58        self
59    }
60
61    pub fn add_module_readers<T: IntoModuleReaders>(&mut self, readers: T) -> &mut Self {
62        let readers = readers.into_readers();
63        self.module_readers.extend(readers);
64
65        for (i, reader) in self.module_readers.iter().enumerate() {
66            for other in &self.module_readers[i + 1..] {
67                if reader.scheme() == other.scheme() {
68                    panic!(
69                        "Multiple resource readers sharing the same scheme: {}",
70                        reader.scheme()
71                    );
72                }
73            }
74        }
75
76        self
77    }
78
79    fn handle_initalize_resource_reader<W: Write>(
80        &self,
81        pkl_msg: &PklServerMessage,
82        out: &mut W,
83    ) -> Result<(), Box<dyn std::error::Error>> {
84        debug_assert!(pkl_msg.header == INITIALIZE_RESOURCE_READER_REQUEST);
85
86        let map = pkl_msg.response.as_map().unwrap();
87        let request_id = map.get(0).unwrap().1.as_i64().unwrap();
88        let scheme = map.get(1).unwrap().1.as_str().unwrap();
89
90        // TODO: send error to pkl
91        let Some(reader) = self.resource_readers.iter().find(|r| r.scheme() == scheme) else {
92            _warn!("Incompatible scheme: {:?}", scheme);
93
94            let serialized = InitializeResourceReaderResponse {
95                request_id,
96                spec: None,
97            }
98            .encode_msg()?;
99
100            out.write_all(&serialized)?;
101            out.flush()?;
102
103            return Ok(());
104        };
105
106        let serialized = InitializeResourceReaderResponse {
107            request_id,
108            spec: Some(ClientResourceReader {
109                scheme: scheme.to_owned(),
110                has_hierarchical_uris: reader.has_hierarchical_uris(),
111                is_globbable: reader.is_globbable(),
112            }),
113        }
114        .encode_msg()?;
115
116        out.write_all(&serialized)?;
117        out.flush()?;
118
119        Ok(())
120    }
121
122    fn handle_initalize_module_reader<W: Write>(
123        &self,
124        pkl_msg: &PklServerMessage,
125        out: &mut W,
126    ) -> Result<(), Box<dyn std::error::Error>> {
127        debug_assert!(pkl_msg.header == INITIALIZE_MODULE_READER_REQUEST);
128
129        let map = pkl_msg.response.as_map().unwrap();
130        let request_id = map.get(0).unwrap().1.as_i64().unwrap();
131        let scheme = map.get(1).unwrap().1.as_str().unwrap();
132
133        // TODO: send error to pkl
134        let Some(reader) = self.module_readers.iter().find(|r| r.scheme() == scheme) else {
135            _warn!("Incompatible scheme: {:?}", scheme);
136
137            let serialized = InitializeModuleReaderResponse {
138                request_id,
139                spec: None,
140            }
141            .encode_msg()?;
142
143            out.write_all(&serialized)?;
144            out.flush()?;
145
146            return Ok(());
147        };
148
149        let serialized = InitializeModuleReaderResponse {
150            request_id,
151            spec: Some(ClientModuleReader {
152                scheme: scheme.to_owned(),
153                has_hierarchical_uris: reader.has_hierarchical_uris(),
154                is_globbable: reader.is_globbable(),
155                is_local: reader.is_local(),
156            }),
157        }
158        .encode_msg()?;
159
160        out.write_all(&serialized)?;
161        out.flush()?;
162
163        Ok(())
164    }
165
166    pub fn run(&self) -> Result<(), Box<dyn std::error::Error>> {
167        let mut stdin = std::io::stdin().lock();
168        let mut stdout = std::io::stdout().lock();
169
170        for _reader in self.resource_readers.iter() {
171            _info!("Registered resource reader: {:?}", _reader.scheme());
172        }
173
174        for _reader in self.module_readers.iter() {
175            _info!("Registered module reader: {:?}", _reader.scheme());
176        }
177
178        loop {
179            let Ok(pkl_msg) = recv_msg(&mut stdin) else {
180                _warn!("Failed to decode message");
181                break;
182            };
183
184            match pkl_msg.header {
185                INITIALIZE_RESOURCE_READER_REQUEST => {
186                    self.handle_initalize_resource_reader(&pkl_msg, &mut stdout)?;
187                }
188                INITIALIZE_MODULE_READER_REQUEST => {
189                    self.handle_initalize_module_reader(&pkl_msg, &mut stdout)?;
190                }
191                LIST_RESOURCES_REQUEST => {
192                    handle_list_resources(&self.resource_readers, &pkl_msg, &mut stdout)?;
193                }
194                LIST_MODULES_REQUEST => {
195                    handle_list_modules(&self.module_readers, &pkl_msg, &mut stdout)?;
196                }
197                READ_RESOURCE_REQUEST => {
198                    handle_read_resource(&self.resource_readers, &pkl_msg, &mut stdout)?;
199                }
200                READ_MODULE_REQUEST => {
201                    handle_read_module(&self.module_readers, &pkl_msg, &mut stdout)?;
202                }
203                CLOSE_EXTERNAL_PROCESS => {
204                    _info!("CLOSE_EXTERNAL_PROCESS received");
205                    break;
206                }
207                _ => {
208                    _warn!("unexpected message type: {:x}", pkl_msg.header);
209                }
210            }
211        }
212
213        Ok(())
214    }
215}
216
217pub use crate::api::reader::{PklModuleReader, PklResourceReader};
218pub use crate::internal::msgapi::outgoing::PathElements;
219
220use super::reader::{IntoModuleReaders, IntoResourceReaders};