1use anyhow::{Context, Result};
2use serde_json::Value;
3use std::fs;
4use std::path::Path;
5
6pub fn read_json(path: &Path) -> Result<Value> {
7 let raw = fs::read_to_string(path)
8 .with_context(|| format!("failed to read json: {}", path.display()))?;
9 let value: Value =
10 serde_json::from_str(&raw).with_context(|| format!("invalid json: {}", path.display()))?;
11 Ok(value)
12}
13
14pub fn string_at(value: &Value, path: &[&str]) -> Option<String> {
15 let mut cursor = value;
16 for key in path {
17 cursor = cursor.get(*key)?;
18 }
19 cursor.as_str().map(strip_newlines)
20}
21
22pub fn i64_at(value: &Value, path: &[&str]) -> Option<i64> {
23 let mut cursor = value;
24 for key in path {
25 cursor = cursor.get(*key)?;
26 }
27 match cursor {
28 Value::Number(value) => value.as_i64(),
29 Value::String(value) => value.trim().parse::<i64>().ok(),
30 _ => None,
31 }
32}
33
34pub fn strip_newlines(raw: &str) -> String {
35 raw.split(&['\n', '\r'][..])
36 .next()
37 .unwrap_or("")
38 .to_string()
39}