agent_tk/conversion/
tst.rs

1//! Support for converting Tst via a recursive mapping process. It is especially useful for expanding TSTs.
2use futures::FutureExt;
3use std::{marker::PhantomData, sync::RwLock};
4
5use crate::{definitions, Error, Result};
6
7//  __  __
8// |  \/  | __ _ _ __  _ __   ___ _ __ ___
9// | |\/| |/ _` | '_ \| '_ \ / _ \ '__/ __|
10// | |  | | (_| | |_) | |_) |  __/ |  \__ \
11// |_|  |_|\__,_| .__/| .__/ \___|_|  |___/
12//              |_|   |_|
13
14/// Trait representing the mappers for each type of tree node
15pub trait Mappers: Sync
16{
17  /// Result of the mapping
18  type Output;
19  /// Map a noop node
20  fn map_noop<'a>(
21    &'a self,
22    tst_node: definitions::tst::Noop,
23  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>;
24  /// Map a sequential node
25  fn map_seq<'a>(
26    &'a self,
27    tst_node: definitions::tst::Seq,
28  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>;
29  /// Map a concurrent node
30  fn map_conc<'a>(
31    &'a self,
32    tst_node: definitions::tst::Conc,
33  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>;
34  /// Map a move to node
35  fn map_move_to<'a>(
36    &'a self,
37    tst_node: definitions::tst::MoveTo,
38  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>;
39  /// Map a search area node
40  fn map_search_area<'a>(
41    &'a self,
42    tst_node: definitions::tst::SearchArea,
43  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>;
44}
45
46//  __  __
47// |  \/  | __ _ _ __  _ __   ___ _ __
48// | |\/| |/ _` | '_ \| '_ \ / _ \ '__|
49// | |  | | (_| | |_) | |_) |  __/ |
50// |_|  |_|\__,_| .__/| .__/ \___|_|
51//              |_|   |_|
52
53/// Mapper trait
54pub trait Mapper<TInput, TOutput>: Send + Sync
55{
56  /// Map the node t
57  fn map<'a, 'b, M>(
58    &'a self,
59    mappers: &'b M,
60    t: TInput,
61  ) -> futures::future::BoxFuture<'b, Result<TOutput>>
62  where
63    M: Mappers<Output = TOutput> + Send,
64    TInput: 'b;
65}
66
67// Dispatch
68
69fn dispatch_mapping<'a, M>(
70  mappers: &'a M,
71  tst_node: definitions::tst::Node,
72) -> futures::future::BoxFuture<'a, Result<M::Output>>
73where
74  M: Mappers,
75{
76  match tst_node
77  {
78    definitions::tst::Node::Noop(noop) => mappers.map_noop(noop),
79    definitions::tst::Node::Seq(seq) => mappers.map_seq(seq),
80    definitions::tst::Node::Conc(conc) => mappers.map_conc(conc),
81    definitions::tst::Node::MoveTo(move_to) => mappers.map_move_to(move_to),
82    definitions::tst::Node::SearchArea(search_area) => mappers.map_search_area(search_area),
83  }
84}
85
86//     _                                  _       _
87//    / \   ___ ___ _   _ _ __ ___  _   _| | __ _| |_ ___  _ __
88//   / _ \ / __/ __| | | | '_ ` _ \| | | | |/ _` | __/ _ \| '__|
89//  / ___ \ (_| (__| |_| | | | | | | |_| | | (_| | || (_) | |
90// /_/   \_\___\___|\__,_|_| |_| |_|\__,_|_|\__,_|\__\___/|_|
91
92/// Accumulator used by SeqMapper and ConcMapper
93pub trait Accumulator<T>
94{
95  /// Start a new accumulator
96  fn new() -> Self;
97  /// Accumulate the next value
98  fn next(&mut self, t: T) -> Result<()>;
99  /// Finalise the accumulation
100  fn finalise(self) -> Result<T>;
101}
102
103//   ____                                _ _       _   _           _      __  __
104//  / ___|___  _ __ ___  _ __   ___  ___(_) |_ ___| \ | | ___   __| | ___|  \/  | __ _ _ __  _ __   ___ _ __
105// | |   / _ \| '_ ` _ \| '_ \ / _ \/ __| | __/ _ \  \| |/ _ \ / _` |/ _ \ |\/| |/ _` | '_ \| '_ \ / _ \ '__|
106// | |__| (_) | | | | | | |_) | (_) \__ \ | ||  __/ |\  | (_) | (_| |  __/ |  | | (_| | |_) | |_) |  __/ |
107//  \____\___/|_| |_| |_| .__/ \___/|___/_|\__\___|_| \_|\___/ \__,_|\___|_|  |_|\__,_| .__/| .__/ \___|_|
108//                      |_|                                                           |_|   |_|
109
110/// Generic Mapper for composite node with children
111#[derive(Default)]
112pub struct CompositeNodeMapper<TOutput, TAccumulator: Accumulator<TOutput>>
113{
114  _out: PhantomData<fn() -> TOutput>,
115  _acc: PhantomData<fn() -> TAccumulator>,
116}
117
118impl<TNode, TOutput, TAccumulator> Mapper<TNode, TOutput>
119  for CompositeNodeMapper<TOutput, TAccumulator>
120where
121  TNode: definitions::tst::CompositeNode
122    + Send
123    + IntoIterator<Item = definitions::tst::Node, IntoIter: Send>,
124  TAccumulator: Accumulator<TOutput> + Send,
125{
126  fn map<'a, 'b, M>(
127    &'a self,
128    mappers: &'b M,
129    t: TNode,
130  ) -> futures::future::BoxFuture<'b, Result<M::Output>>
131  where
132    M: Mappers<Output = TOutput>,
133    TNode: 'b,
134  {
135    async move {
136      let mut acc = TAccumulator::new();
137      for sub_task in t.into_iter()
138      {
139        acc.next(dispatch_mapping(mappers, sub_task).await?)?;
140      }
141      acc.finalise()
142    }
143    .boxed()
144  }
145}
146
147//  ___    _            _   _ _         __  __
148// |_ _|__| | ___ _ __ | |_(_) |_ _   _|  \/  | __ _ _ __  _ __   ___ _ __
149//  | |/ _` |/ _ \ '_ \| __| | __| | | | |\/| |/ _` | '_ \| '_ \ / _ \ '__|
150//  | | (_| |  __/ | | | |_| | |_| |_| | |  | | (_| | |_) | |_) |  __/ |
151// |___\__,_|\___|_| |_|\__|_|\__|\__, |_|  |_|\__,_| .__/| .__/ \___|_|
152//                                |___/             |_|   |_|
153
154/// Mapper that output the node, unchanged.
155#[derive(Default)]
156pub struct IdentityMapper {}
157
158impl<TNode> Mapper<TNode, definitions::tst::Node> for IdentityMapper
159where
160  TNode: definitions::tst::NodeTrait + Send,
161{
162  fn map<'a, 'b, M>(
163    &'a self,
164    _mappers: &'b M,
165    t: TNode,
166  ) -> futures::future::BoxFuture<'b, Result<definitions::tst::Node>>
167  where
168    M: Mappers<Output = definitions::tst::Node>,
169    TNode: 'b,
170  {
171    std::future::ready(Ok(t.into())).boxed()
172  }
173}
174
175//  _____              __  __                            _____          _ _
176// |_   _| __ ___  ___|  \/  | __ _ _ __  _ __   ___ _ _|_   _| __ __ _(_) |_
177//   | || '__/ _ \/ _ \ |\/| |/ _` | '_ \| '_ \ / _ \ '__|| || '__/ _` | | __|
178//   | || | |  __/  __/ |  | | (_| | |_) | |_) |  __/ |   | || | | (_| | | |_
179//   |_||_|  \___|\___|_|  |_|\__,_| .__/| .__/ \___|_|   |_||_|  \__,_|_|\__|
180//                                 |_|   |_|
181
182/// Base trait for a TreeMapper
183pub trait TreeMapperTrait<TOutput>
184{
185  /// Mapper for sequential nodes
186  type SeqMapper: Mapper<definitions::tst::Seq, TOutput>;
187  /// Mapper for concurrent nodes
188  type ConcMapper: Mapper<definitions::tst::Conc, TOutput>;
189  /// Mapper for move_to nodes
190  type MoveToMapper: Mapper<definitions::tst::MoveTo, TOutput>;
191  /// Mapper for search area nodes
192  type SearchAreaMapper: Mapper<definitions::tst::SearchArea, TOutput>;
193
194  /// Map the tst
195  fn map<'a>(
196    &'a self,
197    t: definitions::tst::Node,
198  ) -> futures::future::BoxFuture<'a, Result<TOutput>>;
199
200  /// Map the tst and block
201  fn map_direct(&self, t: definitions::tst::Node) -> Result<TOutput>
202  {
203    futures::executor::block_on(self.map(t))
204  }
205}
206
207//  _____              __  __
208// |_   _| __ ___  ___|  \/  | __ _ _ __  _ __   ___ _ __
209//   | || '__/ _ \/ _ \ |\/| |/ _` | '_ \| '_ \ / _ \ '__|
210//   | || | |  __/  __/ |  | | (_| | |_) | |_) |  __/ |
211//   |_||_|  \___|\___|_|  |_|\__,_| .__/| .__/ \___|_|
212//                                 |_|   |_|
213
214/// Implementation of mapping for a tree
215pub struct TreeMapper<
216  TOutput,
217  TNoopMapper: Mapper<definitions::tst::Noop, TOutput>,
218  TSeqMapper: Mapper<definitions::tst::Seq, TOutput>,
219  TConcMapper: Mapper<definitions::tst::Conc, TOutput>,
220  TMoveToMapper: Mapper<definitions::tst::MoveTo, TOutput>,
221  TSearchAreaMapper: Mapper<definitions::tst::SearchArea, TOutput>,
222> {
223  noop_mapper: TNoopMapper,
224  seq_mapper: TSeqMapper,
225  conc_mapper: TConcMapper,
226  move_to_mapper: TMoveToMapper,
227  search_area_mapper: TSearchAreaMapper,
228  _out: PhantomData<fn() -> TOutput>,
229}
230
231impl<TOutput, TNoopMapper, TSeqMapper, TConcMapper, TMoveToMapper, TSearchAreaMapper> Mappers
232  for TreeMapper<TOutput, TNoopMapper, TSeqMapper, TConcMapper, TMoveToMapper, TSearchAreaMapper>
233where
234  TNoopMapper: Mapper<definitions::tst::Noop, TOutput>,
235  TSeqMapper: Mapper<definitions::tst::Seq, TOutput>,
236  TConcMapper: Mapper<definitions::tst::Conc, TOutput>,
237  TMoveToMapper: Mapper<definitions::tst::MoveTo, TOutput>,
238  TSearchAreaMapper: Mapper<definitions::tst::SearchArea, TOutput>,
239{
240  type Output = TOutput;
241  fn map_noop<'a>(
242    &'a self,
243    tst_node: definitions::tst::Noop,
244  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>
245  {
246    self.noop_mapper.map(self, tst_node)
247  }
248  fn map_seq<'a>(
249    &'a self,
250    tst_node: definitions::tst::Seq,
251  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>
252  {
253    self.seq_mapper.map(self, tst_node)
254  }
255  fn map_conc<'a>(
256    &'a self,
257    tst_node: definitions::tst::Conc,
258  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>
259  {
260    self.conc_mapper.map(self, tst_node)
261  }
262  fn map_move_to<'a>(
263    &'a self,
264    tst_node: definitions::tst::MoveTo,
265  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>
266  {
267    self.move_to_mapper.map(self, tst_node)
268  }
269  fn map_search_area<'a>(
270    &'a self,
271    tst_node: definitions::tst::SearchArea,
272  ) -> futures::future::BoxFuture<'a, Result<Self::Output>>
273  {
274    self.search_area_mapper.map(self, tst_node)
275  }
276}
277
278impl<TOutput, TNoopMapper, TSeqMapper, TConcMapper, TMoveToMapper, TSearchAreaMapper>
279  TreeMapperTrait<TOutput>
280  for TreeMapper<TOutput, TNoopMapper, TSeqMapper, TConcMapper, TMoveToMapper, TSearchAreaMapper>
281where
282  TNoopMapper: Mapper<definitions::tst::Noop, TOutput>,
283  TSeqMapper: Mapper<definitions::tst::Seq, TOutput>,
284  TConcMapper: Mapper<definitions::tst::Conc, TOutput>,
285  TMoveToMapper: Mapper<definitions::tst::MoveTo, TOutput>,
286  TSearchAreaMapper: Mapper<definitions::tst::SearchArea, TOutput>,
287{
288  type SeqMapper = TSeqMapper;
289  type ConcMapper = TConcMapper;
290  type MoveToMapper = TMoveToMapper;
291  type SearchAreaMapper = TSearchAreaMapper;
292  /// Map the tst
293  fn map<'a>(&'a self, t: definitions::tst::Node)
294    -> futures::future::BoxFuture<'a, Result<TOutput>>
295  {
296    dispatch_mapping(self, t)
297  }
298}
299
300impl<TOutput, TNoopMapper, TSeqMapper, TConcMapper, TMoveToMapper, TSearchAreaMapper> Default
301  for TreeMapper<TOutput, TNoopMapper, TSeqMapper, TConcMapper, TMoveToMapper, TSearchAreaMapper>
302where
303  TNoopMapper: Mapper<definitions::tst::Noop, TOutput> + Default + Send,
304  TSeqMapper: Mapper<definitions::tst::Seq, TOutput> + Default + Send,
305  TConcMapper: Mapper<definitions::tst::Conc, TOutput> + Default + Send,
306  TMoveToMapper: Mapper<definitions::tst::MoveTo, TOutput> + Default + Send,
307  TSearchAreaMapper: Mapper<definitions::tst::SearchArea, TOutput> + Default + Send,
308{
309  fn default() -> Self
310  {
311    Self {
312      noop_mapper: Default::default(),
313      seq_mapper: Default::default(),
314      conc_mapper: Default::default(),
315      move_to_mapper: Default::default(),
316      search_area_mapper: Default::default(),
317      _out: Default::default(),
318    }
319  }
320}
321
322//  _____                     __                                 _                                  _       _
323// |_   _| __ __ _ _ __  ___ / _| ___  _ __ _ __ ___   ___ _ __ / \   ___ ___ _   _ _ __ ___  _   _| | __ _| |_ ___  _ __
324//   | || '__/ _` | '_ \/ __| |_ / _ \| '__| '_ ` _ \ / _ \ '__/ _ \ / __/ __| | | | '_ ` _ \| | | | |/ _` | __/ _ \| '__|
325//   | || | | (_| | | | \__ \  _| (_) | |  | | | | | |  __/ | / ___ \ (_| (__| |_| | | | | | | |_| | | (_| | || (_) | |
326//   |_||_|  \__,_|_| |_|___/_|  \___/|_|  |_| |_| |_|\___|_|/_/   \_\___\___|\__,_|_| |_| |_|\__,_|_|\__,_|\__\___/|_|
327//
328
329/// Accumulator for transform operations.
330pub struct TransformerAccumulator<TNode: definitions::tst::CompositeNode>
331{
332  children: RwLock<Option<Vec<definitions::tst::Node>>>,
333  tnode: PhantomData<fn() -> TNode>,
334}
335
336impl<TNode> Default for TransformerAccumulator<TNode>
337where
338  TNode: definitions::tst::CompositeNode,
339{
340  fn default() -> Self
341  {
342    Self::new()
343  }
344}
345
346impl<TNode> Accumulator<definitions::tst::Node> for TransformerAccumulator<TNode>
347where
348  TNode: definitions::tst::CompositeNode,
349{
350  fn new() -> Self
351  {
352    Self {
353      children: RwLock::new(Some(Default::default())),
354      tnode: Default::default(),
355    }
356  }
357  fn next(&mut self, t: definitions::tst::Node) -> Result<()>
358  {
359    let _: () = self
360      .children
361      .write()?
362      .as_mut()
363      .ok_or(Error::InternalError("Null vector should not happen."))?
364      .push(t);
365    Ok(())
366  }
367  fn finalise(self) -> Result<definitions::tst::Node>
368  {
369    let mut builder = TNode::build(Default::default());
370    for c in self
371      .children
372      .write()?
373      .take()
374      .ok_or(Error::InternalError("Null vector should not happen."))?
375      .into_iter()
376    {
377      builder = builder.add_node(c);
378    }
379    Ok(builder.into())
380  }
381}
382
383//  _____             _____                     __
384// |_   _| __ ___  __|_   _| __ __ _ _ __  ___ / _| ___  _ __ _ __ ___   ___ _ __
385//   | || '__/ _ \/ _ \| || '__/ _` | '_ \/ __| |_ / _ \| '__| '_ ` _ \ / _ \ '__|
386//   | || | |  __/  __/| || | | (_| | | | \__ \  _| (_) | |  | | | | | |  __/ |
387//   |_||_|  \___|\___||_||_|  \__,_|_| |_|___/_|  \___/|_|  |_| |_| |_|\___|_|
388//
389
390/// A mapper that transform a tree. Can be used for instance to transform nodes that do not have an executor
391pub type TreeTransformer<TSeqMapper, TConcMapper, TMoveToMapper, TSearchAreaMapper> = TreeMapper<
392  definitions::tst::Node,
393  IdentityMapper,
394  TSeqMapper,
395  TConcMapper,
396  TMoveToMapper,
397  TSearchAreaMapper,
398>;
399
400/// Tree transformer with default transformers for Seq/Conc
401pub type DefaultTreeTransformer<TMoveToMapper, TSearchAreaMapper> = TreeTransformer<
402  CompositeNodeMapper<definitions::tst::Node, TransformerAccumulator<definitions::tst::Seq>>,
403  CompositeNodeMapper<definitions::tst::Node, TransformerAccumulator<definitions::tst::Conc>>,
404  TMoveToMapper,
405  TSearchAreaMapper,
406>;
407
408impl<TMoveToMapper, TSearchAreaMapper> DefaultTreeTransformer<TMoveToMapper, TSearchAreaMapper>
409where
410  TMoveToMapper: Mapper<definitions::tst::MoveTo, definitions::tst::Node> + Send,
411  TSearchAreaMapper: Mapper<definitions::tst::SearchArea, definitions::tst::Node> + Send,
412{
413  /// Create a new default tree transformer with the given node mappers
414  pub fn new(move_to_mapper: TMoveToMapper, search_area_mapper: TSearchAreaMapper) -> Self
415  {
416    Self {
417      noop_mapper: IdentityMapper {},
418      seq_mapper: Default::default(),
419      conc_mapper: Default::default(),
420      move_to_mapper,
421      search_area_mapper,
422      _out: Default::default(),
423    }
424  }
425}
426
427#[cfg(test)]
428mod tests
429{
430  use super::*;
431  macro_rules! get_tst_node {
432    ($value:expr, $variant:path) => {
433      match $value
434      {
435        $variant(x) => x,
436        _ => panic!("Unexpected TST Node."),
437      }
438    };
439  }
440  #[derive(Default)]
441  struct FakeSearchAreaTransformer {}
442
443  impl Mapper<definitions::tst::SearchArea, definitions::tst::Node> for FakeSearchAreaTransformer
444  {
445    fn map<'a, 'b, M>(
446      &'a self,
447      _mappers: &'b M,
448      t: definitions::tst::SearchArea,
449    ) -> futures::future::BoxFuture<'b, Result<definitions::tst::Node>>
450    where
451      M: Mappers<Output = definitions::tst::Node> + Send,
452      definitions::tst::SearchArea: 'b,
453    {
454      let avg = t.params.area.iter().fold((0.0, 0.0), |acc, x| {
455        (acc.0 + x.longitude, acc.1 + x.latitude)
456      });
457      std::future::ready(Ok(
458        definitions::tst::MoveTo {
459          params: definitions::tst::MoveToParameters {
460            waypoint: definitions::tst::GeoPoint {
461              longitude: avg.0 / t.params.area.len() as f64,
462              latitude: avg.1 / t.params.area.len() as f64,
463              altitude: 0.0,
464            },
465            ..Default::default()
466          },
467          ..Default::default()
468        }
469        .into(),
470      ))
471      .boxed()
472    }
473  }
474
475  #[test]
476  fn expansion()
477  {
478    let node: definitions::tst::Node = definitions::tst::Seq::build(None)
479      .add(
480        definitions::tst::SearchAreaParameters {
481          area: vec![
482            definitions::tst::GeoPoint {
483              longitude: 10.0,
484              latitude: 50.0,
485              altitude: 0.0,
486            },
487            definitions::tst::GeoPoint {
488              longitude: 12.0,
489              latitude: 50.0,
490              altitude: 0.0,
491            },
492            definitions::tst::GeoPoint {
493              longitude: 12.0,
494              latitude: 52.0,
495              altitude: 0.0,
496            },
497            definitions::tst::GeoPoint {
498              longitude: 10.0,
499              latitude: 52.0,
500              altitude: 0.0,
501            },
502          ],
503          ..Default::default()
504        },
505        None,
506      )
507      .into();
508    let transformer: DefaultTreeTransformer<IdentityMapper, FakeSearchAreaTransformer> =
509      Default::default();
510    let transformed_node = transformer.map_direct(node).unwrap();
511    let node_seq = get_tst_node!(transformed_node, definitions::tst::Node::Seq);
512    let move_to = get_tst_node!(&node_seq.children[0], definitions::tst::Node::MoveTo);
513    assert_eq!(move_to.params.waypoint.longitude, 11.0);
514    assert_eq!(move_to.params.waypoint.latitude, 51.0);
515  }
516}