abstio/
io.rs

1use std::collections::BTreeMap;
2
3use anyhow::Result;
4use serde::de::DeserializeOwned;
5
6use abstutil::{basename, parent_path, Timer};
7
8use crate::{list_dir, maybe_read_binary, slurp_file};
9
10pub fn maybe_read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) -> Result<T> {
11    if !path.ends_with(".json") && !path.ends_with(".geojson") {
12        bail!("read_json needs {} to end with .json or .geojson", path);
13    }
14
15    timer.start(format!("parse {}", path));
16    // TODO timer.read_file isn't working here. And we need to call stop() if there's no file.
17    let result: Result<T> =
18        slurp_file(&path).and_then(|raw| serde_json::from_slice(&raw).map_err(|err| err.into()));
19    timer.stop(format!("parse {}", path));
20    result
21}
22
23pub fn read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {
24    match maybe_read_json(path.clone(), timer) {
25        Ok(obj) => obj,
26        Err(err) => panic!("Couldn't read_json({}): {}", path, err),
27    }
28}
29
30pub fn read_binary<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {
31    match maybe_read_binary(path.clone(), timer) {
32        Ok(obj) => obj,
33        Err(err) => panic!("Couldn't read_binary({}): {}", path, err),
34    }
35}
36
37/// May be a JSON or binary file
38pub fn read_object<T: DeserializeOwned>(path: String, timer: &mut Timer) -> Result<T> {
39    if path.ends_with(".bin") {
40        maybe_read_binary(path, timer)
41    } else {
42        maybe_read_json(path, timer)
43    }
44}
45
46/// May be a JSON or binary file. Panics on failure.
47pub fn must_read_object<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {
48    match read_object(path.clone(), timer) {
49        Ok(obj) => obj,
50        Err(err) => panic!("Couldn't read_object({}): {}", path, err),
51    }
52}
53
54/// Keeps file extensions
55pub fn find_prev_file(orig: String) -> Option<String> {
56    let mut files = list_dir(parent_path(&orig));
57    files.reverse();
58    files.into_iter().find(|f| *f < orig)
59}
60
61pub fn find_next_file(orig: String) -> Option<String> {
62    let files = list_dir(parent_path(&orig));
63    files.into_iter().find(|f| *f > orig)
64}
65
66/// Load all serialized things from a directory, return sorted by name, with file extension removed.
67/// Detects JSON or binary. Filters out broken files.
68pub fn load_all_objects<T: DeserializeOwned>(dir: String) -> Vec<(String, T)> {
69    let mut timer = Timer::new(format!("load_all_objects from {}", dir));
70    let mut tree: BTreeMap<String, T> = BTreeMap::new();
71    for path in list_dir(dir) {
72        match read_object(path.clone(), &mut timer) {
73            Ok(obj) => {
74                tree.insert(basename(path), obj);
75            }
76            Err(err) => {
77                error!("Couldn't load {}: {}", path, err);
78            }
79        }
80    }
81    tree.into_iter().collect()
82}
83
84/// Just list all things from a directory, return sorted by name, with file extension removed.
85///
86/// Hidden files (starting with `.`) are filtered out.
87pub fn list_all_objects(dir: String) -> Vec<String> {
88    list_dir(dir)
89        .into_iter()
90        .map(basename)
91        .filter(|x| !x.starts_with("."))
92        .collect()
93}