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
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use std::fs::{read_dir, File};
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::error::Error;
use std::ffi::OsStr;
use serde::Deserialize;
use chrono::prelude::*;

/// A single timestamped entry, containing an [`Event`], [`NavRoute`], etc.
#[derive(Deserialize, Debug, PartialEq)]
pub struct Entry<E> {
    pub timestamp: DateTime<Utc>,
    #[serde(flatten)]
    pub event: E,
}

#[test]
fn entry() {
    use crate::Government;

    #[derive(Deserialize)]
    enum Dumb { Foo }
    assert!(serde_json::from_str::<Entry<Dumb>>(r#"
        {
            "timestamp": "1970-01-01T00:00:00Z",
            "Foo": null
        }
    "#).is_ok());
    #[derive(Deserialize)]
    #[serde(rename_all = "PascalCase")]
    #[allow(unused)]
    struct Dumber {
        key: (),
    }
    assert!(serde_json::from_str::<Entry<Dumber>>(r#"
        {
            "timestamp": "1970-01-01T00:00:00Z",
            "Key": null
        }
    "#).is_ok());
    #[derive(Deserialize)]
    #[serde(rename_all = "PascalCase")]
    #[allow(unused)]
    struct Dumbest {
        key: Government,
    }
    assert!(serde_json::from_str::<Entry<Dumbest>>(r#"
        {
            "timestamp": "1970-01-01T00:00:00Z",
            "Key": "Anarchy"
        }
    "#).is_ok());
}


/// Parse a single file's worth of journal entries
// TODO: Our own error types.
// TODO: add result inside vec too.
// TODO: This should be an interator, since files are updates as the game runs.
pub fn parse_journal_file<P: AsRef<Path>>(path: P) -> Result<Vec<Entry<Event>>, Box<dyn Error>> {
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    Ok(reader.lines().map(|line| {
        serde_json::from_str(&line.unwrap()).unwrap()
    }).collect())
}

/// Parse all journals file's entries in a directory
// TODO: Our own error types.
// TODO: add result inside vec too.
// TODO: is there a way to stream this too? search for current running log files?
pub fn parse_journal_dir<P: AsRef<Path>>(path: P) -> Result<Vec<Entry<Event>>, Box<dyn Error>> {
    let mut entries = Vec::new();
    for entry in read_dir(path)? {
        let entry = entry?;
        if entry.file_type().unwrap().is_file() &&
           entry.path().extension().and_then(OsStr::to_str) == Some("log")
        {
            entries.append(&mut parse_journal_file(entry.path())?);
        }
    }
    Ok(entries)
}


/// Parse a status file entry
// TODO: Our own error types.
pub fn parse_status_file<P: AsRef<Path>, E>(path: P) -> Result<Entry<E>, serde_json::Error>
    where for<'de> E: Deserialize<'de>
{
    let file = File::open(path).unwrap();
    let reader = BufReader::new(file);
    serde_json::from_reader(reader)
}


/// `Journal.<timestamp>.<part>.log`
///
/// These files store a list of events in JSON lines format. The means that each line is a complete
/// JSON object.
///
/// Each incremental journal file will begin with a [`Fileheader`][incremental::Event::Fileheader]
/// event which in addition to some other metadata, also contains the `part` of the log. This, in
/// addition to the ubiquitous `timestamp` makes parsing the filename unnecessary. For more
/// information on each [`Event`] read their individual documentation.
pub mod incremental;
pub use self::incremental::Event;

/// `Status.json` TODO
pub mod status {}

/// `NavRoute.json`
pub mod route;
pub use self::route::NavRoute;

/// `Cargo.json` TODO
pub mod cargo {}

/// `Market.json` TODO
pub mod market {}

/// `Shipyard.json` TODO
pub mod shipyard {}

/// `Outfitting.json` TODO
pub mod outfitting {}

/// `ModulesInfo.json` TODO
pub mod modules_info {}