nova_forms/services/
zip_service.rs

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
use leptos::*;

/// Provides a service to get the city for a given swiss zip code.
/// This uses the swiss postal service address checker API (`https://service.post.ch/zopa/app/api/addresschecker/v1/zips`).
/// Returns a tuple that contains a function which takes an input event, and a signal that contains the city name.
/// The function taking an input event can be added to a input field and triggers the service call when the input field changes.
/// The response of the service call is stored in the signal.
pub fn use_zip_service() -> (impl Fn(leptos::ev::Event), Signal<Option<String>>) {
    let (zip, set_zip) = create_signal(String::new());
    let zip_service = create_server_action::<ZipService>();
    create_effect(move |_| {
        zip_service.dispatch(ZipService { zip: zip.get() });
    });
    let zip_service_value = zip_service.value();
    let city = create_memo(move |_| {
        if let Some(Ok(Some(city))) = zip_service_value.get() {
            Some(city)
        } else {
            None
        }
    });

    (move |ev| set_zip.set(event_target_value(&ev)), city.into())
}

#[server]
async fn zip_service(zip: String) -> Result<Option<String>, ServerFnError> {
    use serde::Deserialize;

    #[derive(Deserialize)]
    struct Response {
        zips: Vec<ResponseZip>,
    }

    #[derive(Deserialize)]
    struct ResponseZip {
        city18: String,
    }

    let zips = reqwest::get(format!(
        "https://service.post.ch/zopa/app/api/addresschecker/v1/zips?limit=2&zip={zip}"
    ))
    .await?
    .json::<Response>()
    .await?
    .zips;

    if zips.len() != 1 {
        return Ok(None);
    }

    Ok(Some(zips.first().unwrap().city18.to_owned()))
}