stac_io/
ndjson.rs

1use crate::{Error, FromJsonPath, Result};
2use stac::{Catalog, Collection, FromNdjson, Item, ItemCollection, SelfHref, ToNdjson, Value};
3use std::{
4    fs::File,
5    io::{BufRead, BufReader, BufWriter},
6    path::Path,
7};
8
9/// Create a STAC object from newline-delimited JSON.
10pub trait FromNdjsonPath: FromNdjson + FromJsonPath + SelfHref {
11    /// Reads newline-delimited JSON data from a file.
12    ///
13    /// # Examples
14    ///
15    /// ```
16    /// use stac::ItemCollection;
17    /// use stac_io::FromNdjsonPath;
18    ///
19    /// let item_collection = ItemCollection::from_ndjson_path("data/items.ndjson").unwrap();
20    /// ```
21    fn from_ndjson_path(path: impl AsRef<Path>) -> Result<Self> {
22        Self::from_json_path(path)
23    }
24}
25
26/// Write a STAC object to newline-delimited JSON.
27pub trait ToNdjsonPath: ToNdjson {
28    /// Writes a value to a path as newline-delimited JSON.
29    ///
30    /// # Examples
31    ///
32    /// ```no_run
33    /// use stac::{ItemCollection, Item};
34    /// use stac_io::ToNdjsonPath;
35    ///
36    /// let item_collection: ItemCollection = vec![Item::new("a"), Item::new("b")].into();
37    /// item_collection.to_ndjson_path("items.ndjson").unwrap();
38    /// ```
39    fn to_ndjson_path(&self, path: impl AsRef<Path>) -> Result<()> {
40        let file = File::create(path)?;
41        self.to_ndjson_writer(file)?;
42        Ok(())
43    }
44}
45
46impl FromNdjsonPath for Item {}
47impl FromNdjsonPath for Catalog {}
48impl FromNdjsonPath for Collection {}
49impl FromNdjsonPath for ItemCollection {
50    fn from_ndjson_path(path: impl AsRef<Path>) -> Result<Self> {
51        let path = path.as_ref();
52        let reader = BufReader::new(File::open(path)?);
53        let mut items = Vec::new();
54        for line in reader.lines() {
55            items.push(serde_json::from_str(&line?)?);
56        }
57        let mut item_collection = ItemCollection::from(items);
58        item_collection.set_self_href(path.to_string_lossy());
59        Ok(item_collection)
60    }
61}
62impl FromNdjsonPath for Value {
63    fn from_ndjson_path(path: impl AsRef<Path>) -> Result<Self> {
64        let path = path.as_ref();
65        let reader = BufReader::new(File::open(path)?);
66        let mut values: Vec<Value> = Vec::new();
67        for line in reader.lines() {
68            values.push(serde_json::from_str(&line?)?);
69        }
70        vec_into_value(values)
71    }
72}
73
74fn vec_into_value(mut values: Vec<Value>) -> Result<Value> {
75    if values.len() == 1 {
76        Ok(values.pop().unwrap())
77    } else {
78        Ok(ItemCollection::from(
79            values
80                .into_iter()
81                .map(|v| Item::try_from(v).map_err(Error::from))
82                .collect::<Result<Vec<_>>>()?,
83        )
84        .into())
85    }
86}
87
88impl ToNdjsonPath for Item {}
89impl ToNdjsonPath for Catalog {}
90impl ToNdjsonPath for Collection {}
91
92impl ToNdjsonPath for ItemCollection {
93    fn to_ndjson_path(&self, path: impl AsRef<Path>) -> Result<()> {
94        let file = BufWriter::new(File::create(path)?);
95        self.to_ndjson_writer(file)?;
96        Ok(())
97    }
98}
99
100impl ToNdjsonPath for Value {
101    fn to_ndjson_path(&self, path: impl AsRef<Path>) -> Result<()> {
102        match self {
103            Value::Item(item) => item.to_ndjson_path(path),
104            Value::Catalog(catalog) => catalog.to_ndjson_path(path),
105            Value::Collection(collection) => collection.to_ndjson_path(path),
106            Value::ItemCollection(item_collection) => item_collection.to_ndjson_path(path),
107        }
108    }
109}
110
111impl ToNdjsonPath for serde_json::Value {
112    fn to_ndjson_path(&self, path: impl AsRef<Path>) -> Result<()> {
113        let file = File::create(path)?;
114        self.to_ndjson_writer(file)?;
115        Ok(())
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::FromNdjsonPath;
122    use stac::{ItemCollection, SelfHref, Value};
123
124    #[test]
125    fn item_collection_read() {
126        let item_collection = ItemCollection::from_ndjson_path("data/items.ndjson").unwrap();
127        assert_eq!(item_collection.items.len(), 2);
128        assert!(
129            item_collection
130                .self_href()
131                .unwrap()
132                .ends_with("data/items.ndjson")
133        );
134    }
135
136    #[test]
137    fn value_read() {
138        let _ = Value::from_ndjson_path("data/items.ndjson").unwrap();
139    }
140}