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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use crate::{Error, Result};
use serde::{de::DeserializeOwned, Serialize};
use stac::Href;
use std::path::Path;
use url::Url;

/// Reads a STAC value from an href.
///
/// The href can be a url or a filesystem path.
///
/// # Examples
///
/// ```
/// # tokio_test::block_on(async {
/// let item: stac::Item = stac_async::read("data/simple-item.json").await.unwrap();
/// # })
/// ```
pub async fn read<T>(href: impl ToString) -> Result<T>
where
    T: DeserializeOwned + Href,
{
    let href = href.to_string();
    let mut value: T = read_json(&href).await?;
    value.set_href(href);
    Ok(value)
}

/// Reads any deserializable value from an href.
///
/// The href can be a url or a filesystem path.
///
/// # Examples
///
/// ```
/// # tokio_test::block_on(async {
/// let item: stac::Item = stac_async::read_json("data/simple-item.json").await.unwrap();
/// # })
/// ```
pub async fn read_json<T>(href: &str) -> Result<T>
where
    T: DeserializeOwned,
{
    if let Some(url) = stac::href_to_url(href) {
        read_json_from_url(url).await
    } else {
        read_json_from_path(href).await
    }
}

/// Writes any serializable value to a path.
///
/// # Examples
///
/// ```no_run
/// let item = stac::Item::new("an-id");
/// # tokio_test::block_on(async {
/// let value = stac_async::write_json_to_path("item.json", item).await.unwrap();
/// # })
/// ```
pub async fn write_json_to_path(path: impl AsRef<Path>, value: impl Serialize) -> Result<()> {
    let string = serde_json::to_string_pretty(&value)?;
    tokio::fs::write(path, string).await.map_err(Error::from)
}

async fn read_json_from_url<T>(url: Url) -> Result<T>
where
    T: DeserializeOwned,
{
    let response = reqwest::get(url).await?;
    response.json().await.map_err(Error::from)
}

async fn read_json_from_path<T>(path: impl AsRef<Path>) -> Result<T>
where
    T: DeserializeOwned,
{
    let string = tokio::fs::read_to_string(path).await?;
    serde_json::from_str(&string).map_err(Error::from)
}

#[cfg(test)]
mod tests {
    use stac::{Href, Item};

    #[tokio::test]
    async fn read_filesystem() {
        let item: Item = super::read("data/simple-item.json").await.unwrap();
        assert!(item.href().unwrap().ends_with("data/simple-item.json"));
    }

    #[tokio::test]
    async fn read_network() {
        let href = "https://raw.githubusercontent.com/radiantearth/stac-spec/v1.0.0/examples/simple-item.json";
        let item: Item = super::read(href).await.unwrap();
        assert_eq!(item.href().unwrap(), href);
    }
}