1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use std::marker::PhantomData;

use merfolk::{
  interfaces::{Backend, Middleware},
  Call, Reply,
};

use anyhow::Result;
use thiserror::Error;

use regex::Regex;
#[derive(Debug, Error)]
pub enum Error {
  #[error("could not parse regex: {0}")]
  RegexParse(#[from] regex::Error),
}

#[derive(derive_builder::Builder)]
#[builder(pattern = "owned")]
pub struct Router<B> {
  #[builder(private, default = "PhantomData")]
  __phantom: PhantomData<B>,

  #[builder(setter(into), default = "vec![]")]
  routes: Vec<(String, String)>,
}

impl<B> RouterBuilder<B> {
  pub fn build_boxed(self) -> std::result::Result<Box<Router<B>>, RouterBuilderError> {
    self.build().map(Box::new)
  }
}

impl<B> Router<B> {
  pub fn builder() -> RouterBuilder<B> {
    RouterBuilder::default()
  }
}

impl<B: Backend + 'static> Middleware for Router<B> {
  type Backend = B;

  fn wrap_call(&self, call: Result<crate::Call<<Self::Backend as Backend>::Intermediate>>) -> Result<crate::Call<<Self::Backend as Backend>::Intermediate>> {
    call
  }

  fn wrap_reply(&self, reply: Result<crate::Reply<<Self::Backend as Backend>::Intermediate>>) -> Result<crate::Reply<<Self::Backend as Backend>::Intermediate>> {
    reply
  }

  fn unwrap_call(&self, mut call: Result<crate::Call<<Self::Backend as Backend>::Intermediate>>) -> Result<crate::Call<<Self::Backend as Backend>::Intermediate>> {
    if let Ok(call_ok) = &mut call {
      let route = self.routes.iter().find_map(|r| match Regex::new(&r.0) {
        Err(err) => Some(Err(err)),
        Ok(regex) => {
          if regex.is_match(&call_ok.procedure) {
            Some(Ok(regex.replace_all(&call_ok.procedure, r.1.as_str())))
          } else {
            None
          }
        }
      });

      if let Some(route) = route {
        call_ok.procedure = route.map_err(Error::RegexParse)?.to_string();
      }
    }
    call
  }

  fn unwrap_reply(&self, reply: Result<crate::Reply<<Self::Backend as Backend>::Intermediate>>) -> Result<crate::Reply<<Self::Backend as Backend>::Intermediate>> {
    reply
  }

  fn as_any(&mut self) -> &mut dyn core::any::Any {
    self
  }
}