dioxus_grpc/
lib.rs

1use {
2    convert_case::{Case, Casing},
3    std::{fmt::Write, path::Path},
4    tonic_build::Config,
5};
6
7/// - to_path: Is the directory in which files should be written to. When [`None`], defaults to `OUT_DIR`
8pub fn generate_hooks<P: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path>>(
9    protos: &[P],
10    includes: &[P2],
11    to_path: &Option<P3>,
12    uri: &str,
13) -> Result<(), std::io::Error> {
14    let mut config = Config::new();
15    let file_descriptor_set = config.load_fds(protos, includes)?;
16
17    for fd in file_descriptor_set.file {
18        let mut str = String::new();
19        let pkg_name = fd
20            .package
21            .as_ref()
22            .map_or_else(|| "_", |string| string.as_str());
23        let filename = format!("{pkg_name}.dx.rs");
24
25        for service in &fd.service {
26            write!(
27                str,
28                r#"
29                #[path = "{out_dir}/{package_name}.rs"]
30                mod proto;
31                pub use proto::*;
32                use ::dioxus::prelude::*;
33
34                pub struct {service_name}ServiceHook({tonic_client}<::tonic_web_wasm_client::Client>);
35
36                pub fn use_{service_name_lowercase}_service() -> {service_name}ServiceHook {{
37                    {service_name}ServiceHook({tonic_client}::new(::tonic_web_wasm_client::Client::new(
38                        {uri:?}.to_string()
39                    )))
40                }}
41
42                impl {service_name}ServiceHook {{
43                "#,
44                out_dir = std::env::var("OUT_DIR").expect("build.rs"),
45                tonic_client = format!(
46                    "proto::{}_client::{}Client",
47                    service.name().to_case(Case::Snake),
48                    service.name().to_case(Case::Pascal)
49                ),
50                package_name = pkg_name,
51                service_name = service.name().to_case(Case::Pascal),
52                service_name_lowercase = service.name().to_case(Case::Snake)
53            ).expect("write error");
54
55            for rpc in &service.method {
56                write!(
57                    str,
58                    r"
59                    pub fn {rpc_name}(&self, req: Signal<proto::{rpc_input}>) -> Resource<Result<proto::{rpc_ouptut}, tonic::Status>> {{
60                        let client = self.0.to_owned();
61                        use_resource(move || {{
62                            let mut client = client.clone();
63                            async move {{ client.{rpc_name}(req()).await.map(|resp| resp.into_inner()) }}
64                        }})
65                    }}
66                    ",
67                    rpc_name = rpc.name().to_case(Case::Snake),
68                    rpc_input = rpc
69                        .input_type()
70                        .split('.')
71                        .next_back()
72                        .expect("Should always have type"),
73                    rpc_ouptut = rpc
74                        .output_type()
75                        .split('.')
76                        .next_back()
77                        .expect("Should always have type"),
78                ).expect("write error");
79            }
80
81            str.push('}');
82        }
83
84        match to_path {
85            Some(p) => {
86                std::fs::write(
87                    {
88                        let mut path_to_file = p.as_ref().to_owned();
89                        path_to_file.push(filename);
90                        path_to_file
91                    },
92                    str,
93                )
94            },
95            None => {
96                std::fs::write(
97                    format!("{}/{filename}", std::env::var("OUT_DIR").expect("build.rs")),
98                    str,
99                )
100            },
101        }?;
102    }
103
104
105    Ok(())
106}