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
use safecast::{Match, TryCastFrom, TryCastInto};

use tc_value::{TCString, Value};
use tcgeneric::{Map, PathSegment};

use super::{GetHandler, Handler, PostHandler, Route, StateInstance};

struct RenderHandler<'a> {
    template: &'a TCString,
}

impl<'a, State: StateInstance> Handler<'a, State> for RenderHandler<'a>
where
    TCString: TryCastFrom<State>,
{
    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|_txn, value| {
            Box::pin(async move {
                let result = if value.matches::<Map<Value>>() {
                    let data: Map<Value> = value.opt_cast_into().unwrap();
                    self.template.render(data)
                } else {
                    self.template.render(value)
                };

                result.map(Value::String).map(State::from)
            })
        }))
    }

    fn post<'b>(self: Box<Self>) -> Option<PostHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|_txn, params| {
            Box::pin(async move {
                let params = params
                    .into_iter()
                    .map(|(id, state)| {
                        let as_string = if state.matches::<TCString>() {
                            state.opt_cast_into().expect("string")
                        } else {
                            TCString::from(format!("{state:?}"))
                        };

                        (id, Value::String(as_string))
                    })
                    .collect::<Map<Value>>();

                self.template
                    .render(params)
                    .map(Value::String)
                    .map(State::from)
            })
        }))
    }
}

impl<State: StateInstance> Route<State> for TCString
where
    TCString: TryCastFrom<State>,
{
    fn route<'a>(&'a self, path: &'a [PathSegment]) -> Option<Box<dyn Handler<'a, State> + 'a>> {
        if path.len() != 1 {
            return None;
        }

        match path[0].as_str() {
            "render" => Some(Box::new(RenderHandler { template: self })),
            _ => None,
        }
    }
}