diff options
| author | Daniel Schadt <kingdread@gmx.de> | 2020-05-09 17:30:34 +0200 | 
|---|---|---|
| committer | Daniel Schadt <kingdread@gmx.de> | 2020-05-09 17:30:34 +0200 | 
| commit | dd0e9cbc3da25a59e3bdc08cc0da9c56f8ac15ad (patch) | |
| tree | f85da0956cd8c603ccdc30610fe7d3a69d98041d /src | |
| parent | 7a1dff2610dba8f11fc54a9e016d386bf8dc0a88 (diff) | |
| download | evtclib-dd0e9cbc3da25a59e3bdc08cc0da9c56f8ac15ad.tar.gz evtclib-dd0e9cbc3da25a59e3bdc08cc0da9c56f8ac15ad.tar.bz2 evtclib-dd0e9cbc3da25a59e3bdc08cc0da9c56f8ac15ad.zip | |
move process_* out of lib.rs
This is the start of an effort to clean up lib.rs a bit by moving out
functions into their own module and re-exporting them.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 157 | ||||
| -rw-r--r-- | src/processing.rs | 156 | 
2 files changed, 162 insertions, 151 deletions
| @@ -32,8 +32,9 @@  //!  //! `evtclib` provides two convenience functions to obtain a [`Log`][Log]:  //! -//! If you have a stream (that is, something that is [`Read`][Read] + [`Seek`][Seek]), you can use -//! [`process_stream`][process_stream] to obtain a [`Log`][Log] by reading from the stream. +//! If you have a stream (that is, something that is [`Read`][std::io::Read] + +//! [`Seek`][std::io::Seek]), you can use [`process_stream`][process_stream] to obtain a +//! [`Log`][Log] by reading from the stream.  //!  //! If your evtc is saved in a file, you can use [`process_file`][process_file] to obtain a [`Log`]  //! from it. This will also ensure that the buffering is set up correctly, to avoid unnecessary @@ -89,10 +90,7 @@  use std::collections::HashMap;  use std::convert::TryFrom; -use std::fs::File; -use std::io::{BufReader, Read, Seek};  use std::marker::PhantomData; -use std::path::Path;  use getset::{CopyGetters, Getters};  use num_traits::FromPrimitive; @@ -103,6 +101,9 @@ pub mod raw;  pub mod event;  pub use event::{Event, EventKind}; +mod processing; +pub use processing::{Compression, process, process_file, process_stream}; +  pub mod gamedata;  use gamedata::CmTrigger;  pub use gamedata::{Boss, EliteSpec, Profession}; @@ -909,152 +910,6 @@ impl Log {      }  } -/// Main function to turn a low-level [`Evtc`][raw::Evtc] to a high-level [`Log`][Log]. -/// -/// This function takes an [`Evtc`][raw::Evtc] and does the required type conversions and -/// pre-processing to get a high-level [`Log`][Log]. This pre-processing includes -/// -/// * Setting the correct aware times for the agents -/// * Setting the master agents for each agent -/// * Converting all events -/// -/// Note that the structures are quite different, so this function does not consume the given -/// [`Evtc`][raw::Evtc]. -pub fn process(data: &raw::Evtc) -> Result<Log, EvtcError> { -    // Prepare "augmented" agents -    let mut agents = setup_agents(data)?; -    // Do the first aware/last aware field -    set_agent_awares(data, &mut agents)?; - -    // Set the master addr field -    set_agent_masters(data, &mut agents)?; - -    let events = data -        .events -        .iter() -        .filter_map(|e| Event::try_from(e).ok()) -        .collect(); - -    Ok(Log { -        agents, -        events, -        boss_id: data.header.combat_id, -    }) -} - -/// Indicates the given compression method for the file. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum Compression { -    /// No compression was used. -    None, -    /// The file is wrapped in a zip archive. -    Zip, -} - -/// Convenience function to process a given stream directly. -/// -/// This is a shorthand for using [`raw::parse_file`][raw::parse_file] followed by -/// [`process`][process]. -/// -/// The [`Seek`][Seek] bound is needed for zip compressed archives. If you have a reader that does -/// not support seeking, you can use [`raw::parse_file`][raw::parse_file] directly instead. -/// -/// ```no_run -/// # fn main() -> Result<(), Box<dyn std::error::Error>> { -/// use std::io::Cursor; -/// use evtclib::Compression; -/// let data = Cursor::new(vec![]); -/// let log = evtclib::process_stream(data, Compression::None)?; -/// # Ok(()) } -/// ``` -pub fn process_stream<R: Read + Seek>( -    input: R, -    compression: Compression, -) -> Result<Log, EvtcError> { -    let evtc = match compression { -        Compression::None => raw::parse_file(input)?, -        Compression::Zip => raw::parse_zip(input)?, -    }; -    process(&evtc) -} - -/// Convenience function to process a given file directly. -/// -/// This is a shorthand for opening the file and then using [`process_stream`][process_stream] with -/// it. This function automatically wraps the raw file in a buffered reader, to ensure the bext -/// reading performance. -/// -/// If you need more fine-grained control, consider using [`process_stream`][process_stream] or -/// [`raw::parse_file`][raw::parse_file] followed by [`process`][process] instead. -/// -/// ```no_run -/// # use evtclib::Compression; -/// # fn main() -> Result<(), Box<dyn std::error::Error>> { -/// let log = evtclib::process_file("logfile.zevtc", Compression::Zip)?; -/// # Ok(()) } -/// ``` -pub fn process_file<P: AsRef<Path>>(path: P, compression: Compression) -> Result<Log, EvtcError> { -    let file = File::open(path).map_err(Into::<raw::ParseError>::into)?; -    let buffered = BufReader::new(file); -    process_stream(buffered, compression) -} - -fn setup_agents(data: &raw::Evtc) -> Result<Vec<Agent>, EvtcError> { -    let mut agents = Vec::with_capacity(data.agents.len()); - -    for raw_agent in &data.agents { -        agents.push(Agent::try_from(raw_agent)?); -    } -    Ok(agents) -} - -fn get_agent_by_addr(agents: &mut [Agent], addr: u64) -> Option<&mut Agent> { -    for agent in agents { -        if agent.addr == addr { -            return Some(agent); -        } -    } -    None -} - -fn set_agent_awares(data: &raw::Evtc, agents: &mut [Agent]) -> Result<(), EvtcError> { -    for event in &data.events { -        if event.is_statechange == raw::CbtStateChange::None { -            if let Some(current_agent) = get_agent_by_addr(agents, event.src_agent) { -                current_agent.instance_id = event.src_instid; -                if current_agent.first_aware == 0 { -                    current_agent.first_aware = event.time; -                } -                current_agent.last_aware = event.time; -            } -        } -    } -    Ok(()) -} - -fn set_agent_masters(data: &raw::Evtc, agents: &mut [Agent]) -> Result<(), EvtcError> { -    for event in &data.events { -        if event.src_master_instid != 0 { -            let mut master_addr = None; -            for agent in &*agents { -                if agent.instance_id == event.src_master_instid -                    && agent.first_aware < event.time -                    && event.time < agent.last_aware -                { -                    master_addr = Some(agent.addr); -                    break; -                } -            } -            if let Some(master_addr) = master_addr { -                if let Some(current_slave) = get_agent_by_addr(agents, event.src_agent) { -                    current_slave.master_agent = Some(master_addr); -                } -            } -        } -    } -    Ok(()) -} -  fn time_between_buffs(events: &[Event], wanted_buff_id: u32) -> u64 {      let mut time_maps: HashMap<u64, Vec<u64>> = HashMap::new();      for event in events { diff --git a/src/processing.rs b/src/processing.rs new file mode 100644 index 0000000..f35ab35 --- /dev/null +++ b/src/processing.rs @@ -0,0 +1,156 @@ +//! Private module to contain the processing functions. + +use std::{ +    convert::TryFrom, +    fs::File, +    io::{Read, Seek, BufReader}, +    path::Path, +}; + +use super::{Agent, Event, EvtcError, Log, raw}; + +/// Main function to turn a low-level [`Evtc`][raw::Evtc] to a high-level [`Log`][Log]. +/// +/// This function takes an [`Evtc`][raw::Evtc] and does the required type conversions and +/// pre-processing to get a high-level [`Log`][Log]. This pre-processing includes +/// +/// * Setting the correct aware times for the agents +/// * Setting the master agents for each agent +/// * Converting all events +/// +/// Note that the structures are quite different, so this function does not consume the given +/// [`Evtc`][raw::Evtc]. +pub fn process(data: &raw::Evtc) -> Result<Log, EvtcError> { +    // Prepare "augmented" agents +    let mut agents = setup_agents(data)?; +    // Do the first aware/last aware field +    set_agent_awares(data, &mut agents)?; + +    // Set the master addr field +    set_agent_masters(data, &mut agents)?; + +    let events = data +        .events +        .iter() +        .filter_map(|e| Event::try_from(e).ok()) +        .collect(); + +    Ok(Log { +        agents, +        events, +        boss_id: data.header.combat_id, +    }) +} + +/// Indicates the given compression method for the file. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Compression { +    /// No compression was used. +    None, +    /// The file is wrapped in a zip archive. +    Zip, +} + +/// Convenience function to process a given stream directly. +/// +/// This is a shorthand for using [`raw::parse_file`][raw::parse_file] followed by +/// [`process`][process]. +/// +/// The [`Seek`][Seek] bound is needed for zip compressed archives. If you have a reader that does +/// not support seeking, you can use [`raw::parse_file`][raw::parse_file] directly instead. +/// +/// ```no_run +/// # fn main() -> Result<(), Box<dyn std::error::Error>> { +/// use std::io::Cursor; +/// use evtclib::Compression; +/// let data = Cursor::new(vec![]); +/// let log = evtclib::process_stream(data, Compression::None)?; +/// # Ok(()) } +/// ``` +pub fn process_stream<R: Read + Seek>( +    input: R, +    compression: Compression, +) -> Result<Log, EvtcError> { +    let evtc = match compression { +        Compression::None => raw::parse_file(input)?, +        Compression::Zip => raw::parse_zip(input)?, +    }; +    process(&evtc) +} + +/// Convenience function to process a given file directly. +/// +/// This is a shorthand for opening the file and then using [`process_stream`][process_stream] with +/// it. This function automatically wraps the raw file in a buffered reader, to ensure the bext +/// reading performance. +/// +/// If you need more fine-grained control, consider using [`process_stream`][process_stream] or +/// [`raw::parse_file`][raw::parse_file] followed by [`process`][process] instead. +/// +/// ```no_run +/// # use evtclib::Compression; +/// # fn main() -> Result<(), Box<dyn std::error::Error>> { +/// let log = evtclib::process_file("logfile.zevtc", Compression::Zip)?; +/// # Ok(()) } +/// ``` +pub fn process_file<P: AsRef<Path>>(path: P, compression: Compression) -> Result<Log, EvtcError> { +    let file = File::open(path).map_err(Into::<raw::ParseError>::into)?; +    let buffered = BufReader::new(file); +    process_stream(buffered, compression) +} + +fn setup_agents(data: &raw::Evtc) -> Result<Vec<Agent>, EvtcError> { +    let mut agents = Vec::with_capacity(data.agents.len()); + +    for raw_agent in &data.agents { +        agents.push(Agent::try_from(raw_agent)?); +    } +    Ok(agents) +} + +fn get_agent_by_addr(agents: &mut [Agent], addr: u64) -> Option<&mut Agent> { +    for agent in agents { +        if agent.addr == addr { +            return Some(agent); +        } +    } +    None +} + +fn set_agent_awares(data: &raw::Evtc, agents: &mut [Agent]) -> Result<(), EvtcError> { +    for event in &data.events { +        if event.is_statechange == raw::CbtStateChange::None { +            if let Some(current_agent) = get_agent_by_addr(agents, event.src_agent) { +                current_agent.instance_id = event.src_instid; +                if current_agent.first_aware == 0 { +                    current_agent.first_aware = event.time; +                } +                current_agent.last_aware = event.time; +            } +        } +    } +    Ok(()) +} + +fn set_agent_masters(data: &raw::Evtc, agents: &mut [Agent]) -> Result<(), EvtcError> { +    for event in &data.events { +        if event.src_master_instid != 0 { +            let mut master_addr = None; +            for agent in &*agents { +                if agent.instance_id == event.src_master_instid +                    && agent.first_aware < event.time +                    && event.time < agent.last_aware +                { +                    master_addr = Some(agent.addr); +                    break; +                } +            } +            if let Some(master_addr) = master_addr { +                if let Some(current_slave) = get_agent_by_addr(agents, event.src_agent) { +                    current_slave.master_agent = Some(master_addr); +                } +            } +        } +    } +    Ok(()) +} | 
