1use super::Condition;
7use crate::lir::{Operand, ParaId, PlaceId, SymId};
8use crate::module::ModuleError;
9use crate::module::codec::{Decode, Encode, Reader, Writer};
10use crate::{codec_enum, codec_struct};
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum Datum<P = PlaceId, O = Operand, S = SymId> {
16 Place(P),
17 Value(O),
18 Text(S),
19 Bare,
20}
21
22pub type Opt<P = PlaceId, O = Operand, S = SymId> = Option<Datum<P, O, S>>;
24
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct CicsCommand<P = PlaceId, O = Operand, S = SymId> {
27 pub name: S,
29 pub command: Cics<P, O, S>,
30 pub resp: Resp<P, O, S>,
31 pub sinks: Vec<(P, Sink)>,
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub enum Sink {
39 DynamicTransfer,
40 RecordKey,
41 RecordUpdate,
42 Log,
43 Screen,
44 WebResponse,
45 HttpHeader,
46 OutboundHost,
47 OutboundHttp,
48 QueueName,
49 Sysid,
50}
51
52impl Sink {
53 pub fn kind(self) -> &'static str {
55 match self {
56 Self::DynamicTransfer => "cics-dynamic-transfer",
57 Self::RecordKey => "record-key",
58 Self::RecordUpdate => "record-update",
59 Self::Log => "log",
60 Self::Screen => "screen",
61 Self::WebResponse => "web-response",
62 Self::HttpHeader => "http-header",
63 Self::OutboundHost => "outbound-host",
64 Self::OutboundHttp => "outbound-http",
65 Self::QueueName => "queue-name",
66 Self::Sysid => "cics-sysid",
67 }
68 }
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
73pub struct Resp<P = PlaceId, O = Operand, S = SymId> {
74 pub resp: Opt<P, O, S>,
75 pub resp2: Opt<P, O, S>,
76 pub nohandle: bool,
77}
78
79#[derive(Clone, Debug, PartialEq, Eq)]
81pub enum Cics<P = PlaceId, O = Operand, S = SymId> {
82 File { verb: FileControl, file: Opt<P, O, S>, options: FileOptions<P, O, S> },
83 Return { transid: Opt<P, O, S>, commarea: Opt<P, O, S>, length: Opt<P, O, S>, channel: Opt<P, O, S>, immediate: bool },
85 Link(Transfer<P, O, S>),
86 Xctl(Transfer<P, O, S>),
87 Abend { abcode: Opt<P, O, S>, cancel: bool },
88 HandleCondition(Vec<(Condition, Option<ParaId>)>),
90 IgnoreCondition(Vec<Condition>),
91 PushHandle,
92 PopHandle,
93 HandleAbend { program: Opt<P, O, S>, label: Option<ParaId>, reset: bool },
95 HandleAid,
96 SendMap { map: Opt<P, O, S>, mapset: Opt<P, O, S>, from: Opt<P, O, S>, maponly: bool, dataonly: bool, cursor: Opt<P, O, S>, control: Control },
97 ReceiveMap { map: Opt<P, O, S>, mapset: Opt<P, O, S>, into: Opt<P, O, S>, set: Opt<P, O, S> },
98 SendControl { cursor: Opt<P, O, S>, control: Control },
99 Receive(Record<P, O, S>),
100 Asktime { abstime: Opt<P, O, S> },
101 Formattime { abstime: Opt<P, O, S>, datesep: Opt<P, O, S>, timesep: Opt<P, O, S>, outputs: Vec<(S, Datum<P, O, S>)> },
103 Assign(Assign<P, O, S>),
104 Getmain { flength: Opt<P, O, S>, length: Opt<P, O, S>, initimg: Opt<P, O, S>, set: Opt<P, O, S> },
105 Freemain,
106 Enq,
107 Deq,
108 Delay,
109 Syncpoint { rollback: bool },
111 Address { eib: Opt<P, O, S>, commarea: Opt<P, O, S>, cwa: Opt<P, O, S>, twa: Opt<P, O, S> },
112 SendText { from: Opt<P, O, S>, length: Opt<P, O, S> },
113 WriteOperator { text: Opt<P, O, S>, textlength: Opt<P, O, S> },
114 WriteqTs { queue: Opt<P, O, S>, from: Opt<P, O, S>, length: Opt<P, O, S>, rewrite: bool, item: Opt<P, O, S>, numitems: Opt<P, O, S> },
115 ReadqTs { queue: Opt<P, O, S>, next: bool, item: Opt<P, O, S>, numitems: Opt<P, O, S>, record: Record<P, O, S> },
116 DeleteqTs { queue: Opt<P, O, S> },
117 WriteqTd { queue: Opt<P, O, S>, from: Opt<P, O, S>, length: Opt<P, O, S> },
118 ReadqTd { queue: Opt<P, O, S>, record: Record<P, O, S> },
119 DeleteqTd { queue: Opt<P, O, S> },
120 Unsupported,
122 Refused(S),
126}
127
128#[derive(Clone, Copy, Debug, PartialEq, Eq)]
129pub enum FileControl {
130 Read,
131 Write,
132 Rewrite,
133 Delete,
134 Unlock,
135 Startbr,
136 Resetbr,
137 Readnext,
138 Readprev,
139 Endbr,
140}
141
142#[derive(Clone, Debug, PartialEq, Eq)]
143pub struct FileOptions<P = PlaceId, O = Operand, S = SymId> {
144 pub ridfld: Opt<P, O, S>,
145 pub keylength: Opt<P, O, S>,
146 pub reqid: Opt<P, O, S>,
147 pub from: Opt<P, O, S>,
148 pub numrec: Opt<P, O, S>,
149 pub record: Record<P, O, S>,
150 pub generic: bool,
151 pub rrn: bool,
152 pub gteq: bool,
153 pub equal: bool,
154 pub update: bool,
155}
156
157#[derive(Clone, Debug, PartialEq, Eq)]
160pub struct Record<P = PlaceId, O = Operand, S = SymId> {
161 pub into: Opt<P, O, S>,
162 pub set: Opt<P, O, S>,
163 pub length: Opt<P, O, S>,
164}
165
166#[derive(Clone, Debug, PartialEq, Eq)]
168pub struct Transfer<P = PlaceId, O = Operand, S = SymId> {
169 pub program: Opt<P, O, S>,
170 pub commarea: Opt<P, O, S>,
171 pub length: Opt<P, O, S>,
172}
173
174#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
176pub struct Control {
177 pub erase: bool,
178 pub freekb: bool,
179 pub alarm: bool,
180 pub frset: bool,
181}
182
183#[derive(Clone, Debug, PartialEq, Eq)]
185pub struct Assign<P = PlaceId, O = Operand, S = SymId> {
186 pub applid: Opt<P, O, S>,
187 pub sysid: Opt<P, O, S>,
188 pub userid: Opt<P, O, S>,
189 pub netname: Opt<P, O, S>,
190 pub facility: Opt<P, O, S>,
191 pub startcode: Opt<P, O, S>,
192 pub abcode: Opt<P, O, S>,
193 pub program: Opt<P, O, S>,
194 pub cwaleng: Opt<P, O, S>,
195 pub twaleng: Opt<P, O, S>,
196}
197
198impl<P, O, S> Cics<P, O, S> {
199 pub fn name(&self) -> &'static str {
201 match self {
202 Self::File { verb, .. } => verb.name(),
203 Self::Return { .. } => "RETURN",
204 Self::Link(_) => "LINK",
205 Self::Xctl(_) => "XCTL",
206 Self::Abend { .. } => "ABEND",
207 Self::HandleCondition(_) => "HANDLE CONDITION",
208 Self::IgnoreCondition(_) => "IGNORE CONDITION",
209 Self::PushHandle => "PUSH HANDLE",
210 Self::PopHandle => "POP HANDLE",
211 Self::HandleAbend { .. } => "HANDLE ABEND",
212 Self::HandleAid => "HANDLE AID",
213 Self::SendMap { .. } => "SEND MAP",
214 Self::ReceiveMap { .. } => "RECEIVE MAP",
215 Self::SendControl { .. } => "SEND CONTROL",
216 Self::Receive(_) => "RECEIVE",
217 Self::Asktime { .. } => "ASKTIME",
218 Self::Formattime { .. } => "FORMATTIME",
219 Self::Assign(_) => "ASSIGN",
220 Self::Getmain { .. } => "GETMAIN",
221 Self::Freemain => "FREEMAIN",
222 Self::Enq => "ENQ",
223 Self::Deq => "DEQ",
224 Self::Delay => "DELAY",
225 Self::Syncpoint { .. } => "SYNCPOINT",
226 Self::Address { .. } => "ADDRESS",
227 Self::SendText { .. } => "SEND TEXT",
228 Self::WriteOperator { .. } => "WRITE OPERATOR",
229 Self::WriteqTs { .. } => "WRITEQ TS",
230 Self::ReadqTs { .. } => "READQ TS",
231 Self::DeleteqTs { .. } => "DELETEQ TS",
232 Self::WriteqTd { .. } => "WRITEQ TD",
233 Self::ReadqTd { .. } => "READQ TD",
234 Self::DeleteqTd { .. } => "DELETEQ TD",
235 Self::Unsupported => "unsupported",
236 Self::Refused(_) => "refused",
237 }
238 }
239}
240
241pub trait Handles<P, O, S> {
244 type Place;
245 type Value;
246 type Text;
247 type Error;
248 fn place(&mut self, place: P) -> Result<Self::Place, Self::Error>;
249 fn value(&mut self, value: O) -> Result<Self::Value, Self::Error>;
250 fn text(&mut self, text: S) -> Result<Self::Text, Self::Error>;
251}
252
253type Mapped<H, P, O, S, T> = Result<T, <H as Handles<P, O, S>>::Error>;
254type DatumOf<H, P, O, S> = Datum<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
255type OptOf<H, P, O, S> = Option<DatumOf<H, P, O, S>>;
256type CommandOf<H, P, O, S> = CicsCommand<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
257type CicsOf<H, P, O, S> = Cics<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
258type RecordOf<H, P, O, S> = Record<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
259type TransferOf<H, P, O, S> = Transfer<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
260
261impl<P, O, S> Datum<P, O, S> {
262 pub fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, DatumOf<H, P, O, S>> {
263 Ok(match self {
264 Self::Place(p) => Datum::Place(h.place(p)?),
265 Self::Value(o) => Datum::Value(h.value(o)?),
266 Self::Text(s) => Datum::Text(h.text(s)?),
267 Self::Bare => Datum::Bare,
268 })
269 }
270}
271
272fn opt<P, O, S, H: Handles<P, O, S>>(o: Opt<P, O, S>, h: &mut H) -> Mapped<H, P, O, S, OptOf<H, P, O, S>> {
273 o.map(|d| d.map(h)).transpose()
274}
275
276impl<P, O, S> CicsCommand<P, O, S> {
277 pub fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, CommandOf<H, P, O, S>> {
279 let name = h.text(self.name)?;
280 let command = self.command.map(h)?;
281 let resp = Resp { resp: opt(self.resp.resp, h)?, resp2: opt(self.resp.resp2, h)?, nohandle: self.resp.nohandle };
282 let sinks = self.sinks.into_iter().map(|(p, sink)| Ok((h.place(p)?, sink))).collect::<Result<_, _>>()?;
283 Ok(CicsCommand { name, command, resp, sinks })
284 }
285}
286
287impl<P, O, S> Record<P, O, S> {
288 fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, RecordOf<H, P, O, S>> {
289 Ok(Record { into: opt(self.into, h)?, set: opt(self.set, h)?, length: opt(self.length, h)? })
290 }
291}
292
293impl<P, O, S> Transfer<P, O, S> {
294 fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, TransferOf<H, P, O, S>> {
295 Ok(Transfer { program: opt(self.program, h)?, commarea: opt(self.commarea, h)?, length: opt(self.length, h)? })
296 }
297}
298
299impl<P, O, S> Cics<P, O, S> {
300 fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, CicsOf<H, P, O, S>> {
301 Ok(match self {
302 Self::File { verb, file, options: o } => Cics::File {
303 verb,
304 file: opt(file, h)?,
305 options: FileOptions {
306 ridfld: opt(o.ridfld, h)?,
307 keylength: opt(o.keylength, h)?,
308 reqid: opt(o.reqid, h)?,
309 from: opt(o.from, h)?,
310 numrec: opt(o.numrec, h)?,
311 record: o.record.map(h)?,
312 generic: o.generic,
313 rrn: o.rrn,
314 gteq: o.gteq,
315 equal: o.equal,
316 update: o.update,
317 },
318 },
319 Self::Return { transid, commarea, length, channel, immediate } => {
320 Cics::Return { transid: opt(transid, h)?, commarea: opt(commarea, h)?, length: opt(length, h)?, channel: opt(channel, h)?, immediate }
321 }
322 Self::Link(t) => Cics::Link(t.map(h)?),
323 Self::Xctl(t) => Cics::Xctl(t.map(h)?),
324 Self::Abend { abcode, cancel } => Cics::Abend { abcode: opt(abcode, h)?, cancel },
325 Self::HandleCondition(labels) => Cics::HandleCondition(labels),
326 Self::IgnoreCondition(conditions) => Cics::IgnoreCondition(conditions),
327 Self::PushHandle => Cics::PushHandle,
328 Self::PopHandle => Cics::PopHandle,
329 Self::HandleAbend { program, label, reset } => Cics::HandleAbend { program: opt(program, h)?, label, reset },
330 Self::HandleAid => Cics::HandleAid,
331 Self::SendMap { map, mapset, from, maponly, dataonly, cursor, control } => {
332 Cics::SendMap { map: opt(map, h)?, mapset: opt(mapset, h)?, from: opt(from, h)?, maponly, dataonly, cursor: opt(cursor, h)?, control }
333 }
334 Self::ReceiveMap { map, mapset, into, set } => Cics::ReceiveMap { map: opt(map, h)?, mapset: opt(mapset, h)?, into: opt(into, h)?, set: opt(set, h)? },
335 Self::SendControl { cursor, control } => Cics::SendControl { cursor: opt(cursor, h)?, control },
336 Self::Receive(record) => Cics::Receive(record.map(h)?),
337 Self::Asktime { abstime } => Cics::Asktime { abstime: opt(abstime, h)? },
338 Self::Formattime { abstime, datesep, timesep, outputs } => {
339 let (abstime, datesep, timesep) = (opt(abstime, h)?, opt(datesep, h)?, opt(timesep, h)?);
340 let outputs = outputs.into_iter().map(|(name, d)| Ok((h.text(name)?, d.map(h)?))).collect::<Result<_, _>>()?;
341 Cics::Formattime { abstime, datesep, timesep, outputs }
342 }
343 Self::Assign(a) => Cics::Assign(Assign {
344 applid: opt(a.applid, h)?,
345 sysid: opt(a.sysid, h)?,
346 userid: opt(a.userid, h)?,
347 netname: opt(a.netname, h)?,
348 facility: opt(a.facility, h)?,
349 startcode: opt(a.startcode, h)?,
350 abcode: opt(a.abcode, h)?,
351 program: opt(a.program, h)?,
352 cwaleng: opt(a.cwaleng, h)?,
353 twaleng: opt(a.twaleng, h)?,
354 }),
355 Self::Getmain { flength, length, initimg, set } => Cics::Getmain { flength: opt(flength, h)?, length: opt(length, h)?, initimg: opt(initimg, h)?, set: opt(set, h)? },
356 Self::Freemain => Cics::Freemain,
357 Self::Enq => Cics::Enq,
358 Self::Deq => Cics::Deq,
359 Self::Delay => Cics::Delay,
360 Self::Syncpoint { rollback } => Cics::Syncpoint { rollback },
361 Self::Address { eib, commarea, cwa, twa } => Cics::Address { eib: opt(eib, h)?, commarea: opt(commarea, h)?, cwa: opt(cwa, h)?, twa: opt(twa, h)? },
362 Self::SendText { from, length } => Cics::SendText { from: opt(from, h)?, length: opt(length, h)? },
363 Self::WriteOperator { text, textlength } => Cics::WriteOperator { text: opt(text, h)?, textlength: opt(textlength, h)? },
364 Self::WriteqTs { queue, from, length, rewrite, item, numitems } => {
365 Cics::WriteqTs { queue: opt(queue, h)?, from: opt(from, h)?, length: opt(length, h)?, rewrite, item: opt(item, h)?, numitems: opt(numitems, h)? }
366 }
367 Self::ReadqTs { queue, next, item, numitems, record } => {
368 Cics::ReadqTs { queue: opt(queue, h)?, next, item: opt(item, h)?, numitems: opt(numitems, h)?, record: record.map(h)? }
369 }
370 Self::DeleteqTs { queue } => Cics::DeleteqTs { queue: opt(queue, h)? },
371 Self::WriteqTd { queue, from, length } => Cics::WriteqTd { queue: opt(queue, h)?, from: opt(from, h)?, length: opt(length, h)? },
372 Self::ReadqTd { queue, record } => Cics::ReadqTd { queue: opt(queue, h)?, record: record.map(h)? },
373 Self::DeleteqTd { queue } => Cics::DeleteqTd { queue: opt(queue, h)? },
374 Self::Unsupported => Cics::Unsupported,
375 Self::Refused(why) => Cics::Refused(h.text(why)?),
376 })
377 }
378
379 pub fn labels(&self) -> Vec<ParaId> {
381 match self {
382 Self::HandleCondition(labels) => labels.iter().filter_map(|&(_, p)| p).collect(),
383 Self::HandleAbend { label, .. } => label.iter().copied().collect(),
384 _ => Vec::new(),
385 }
386 }
387}
388
389impl FileControl {
390 pub fn name(self) -> &'static str {
391 match self {
392 Self::Read => "READ",
393 Self::Write => "WRITE",
394 Self::Rewrite => "REWRITE",
395 Self::Delete => "DELETE",
396 Self::Unlock => "UNLOCK",
397 Self::Startbr => "STARTBR",
398 Self::Resetbr => "RESETBR",
399 Self::Readnext => "READNEXT",
400 Self::Readprev => "READPREV",
401 Self::Endbr => "ENDBR",
402 }
403 }
404}
405
406impl Encode for Condition {
408 fn encode(&self, w: &mut Writer) {
409 w.leb(*self as u64);
410 }
411}
412
413impl Decode for Condition {
414 fn decode(r: &mut Reader<'_>) -> Result<Self, ModuleError> {
415 let at = r.position();
416 let tag = r.leb()?;
417 usize::try_from(tag).ok().and_then(|i| Condition::ALL.get(i).copied()).ok_or_else(|| r.malformed(at, format!("Condition has no tag {tag}")))
418 }
419}
420
421codec_enum!(Datum { Place(place) = 0, Value(value) = 1, Text(text) = 2, Bare = 3 });
422codec_struct!(CicsCommand { name, command, resp, sinks });
423codec_enum!(Sink {
424 DynamicTransfer = 0,
425 RecordKey = 1,
426 RecordUpdate = 2,
427 Log = 3,
428 Screen = 4,
429 WebResponse = 5,
430 HttpHeader = 6,
431 OutboundHost = 7,
432 OutboundHttp = 8,
433 QueueName = 9,
434 Sysid = 10,
435});
436codec_struct!(Resp { resp, resp2, nohandle });
437codec_enum!(Cics {
440 File { verb, file, options } = 0,
441 Link(transfer) = 2,
442 Xctl(transfer) = 3,
443 Abend { abcode, cancel } = 4,
444 HandleCondition(handlers) = 5,
445 IgnoreCondition(conditions) = 6,
446 PushHandle = 7,
447 PopHandle = 8,
448 HandleAid = 10,
449 SendMap { map, mapset, from, maponly, dataonly, cursor, control } = 11,
450 ReceiveMap { map, mapset, into, set } = 12,
451 SendControl { cursor, control } = 13,
452 Receive(record) = 14,
453 Asktime { abstime } = 15,
454 Formattime { abstime, datesep, timesep, outputs } = 16,
455 Assign(assign) = 17,
456 Getmain { flength, length, initimg, set } = 18,
457 Freemain = 19,
458 Enq = 20,
459 Deq = 21,
460 Delay = 22,
461 Syncpoint { rollback } = 23,
462 Address { eib, commarea, cwa, twa } = 24,
463 SendText { from, length } = 25,
464 WriteOperator { text, textlength } = 26,
465 WriteqTs { queue, from, length, rewrite, item, numitems } = 27,
466 ReadqTs { queue, next, item, numitems, record } = 28,
467 DeleteqTs { queue } = 29,
468 WriteqTd { queue, from, length } = 30,
469 ReadqTd { queue, record } = 31,
470 DeleteqTd { queue } = 32,
471 Unsupported = 33,
472 HandleAbend { program, label, reset } = 34,
473 Return { transid, commarea, length, channel, immediate } = 35,
474 Refused(why) = 36,
475});
476codec_enum!(FileControl {
477 Read = 0,
478 Write = 1,
479 Rewrite = 2,
480 Delete = 3,
481 Unlock = 4,
482 Startbr = 5,
483 Resetbr = 6,
484 Readnext = 7,
485 Readprev = 8,
486 Endbr = 9,
487});
488codec_struct!(FileOptions { ridfld, keylength, reqid, from, numrec, record, generic, rrn, gteq, equal, update });
489codec_struct!(Record { into, set, length });
490codec_struct!(Transfer { program, commarea, length });
491codec_struct!(Control { erase, freekb, alarm, frset });
492codec_struct!(Assign { applid, sysid, userid, netname, facility, startcode, abcode, program, cwaleng, twaleng });