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
use std::{
    fs::{File, OpenOptions},
    io::Read,
    path::Path,
};

use crate::{
    error::Error,
    file::{Compression, Limits},
    validate::Problems,
};

use super::reader::Omf1Reader;

/// Returns true if the file looks more like OMF1 than OMF2.
///
/// Does not guarantee that the file will load. Returns an error in file read fails.
pub fn detect(read: &mut impl Read) -> Result<bool, Error> {
    const PREFIX: [u8; 8] = [0x84, 0x83, 0x82, 0x81, b'O', b'M', b'F', b'-'];
    let mut prefix = [0; PREFIX.len()];
    read.read_exact(&mut prefix)?;
    Ok(prefix == PREFIX)
}

/// Returns true if the path looks more like OMF1 than OMF2.
///
/// Does not guarantee that the file will load. Returns an error in file open or read fails.
pub fn detect_open(path: &Path) -> Result<bool, Error> {
    detect(&mut File::open(path)?)
}

/// Converts a OMF1 files to OMF2.
///
/// This object allows you to set up the desired parameters then convert one or more files.
#[derive(Debug, Default)]
pub struct Converter {
    limits: Limits,
    compression: Compression,
}

impl Converter {
    /// Creates a new default converter.
    pub fn new() -> Self {
        Self::default()
    }

    /// Returns the current limits.
    pub fn limits(&self) -> Limits {
        self.limits
    }

    /// Set the limits to use during conversion.
    pub fn set_limits(&mut self, limits: Limits) {
        self.limits = limits;
    }

    /// Returns the current compression level.
    pub fn compression(&self) -> Compression {
        self.compression
    }

    /// Set the compression level to use when writing.
    pub fn set_compression(&mut self, compression: Compression) {
        self.compression = compression;
    }

    /// Runs a conversion from one open file to another file.
    ///
    /// `input` must support read and seek, while `output` must support write.
    /// On success the validation warnings are returned.
    ///
    /// May be called more than once to convert multiple files with the same parameters.
    pub fn convert(&self, input: File, output: File) -> Result<Problems, Error> {
        let reader = Omf1Reader::new(input, self.limits.json_bytes)?;
        let mut writer = crate::file::Writer::new(output)?;
        writer.set_compression(self.compression);
        let project = reader.project()?.convert(&reader, &mut writer)?;
        writer.finish(project).map(|(_, p)| p)
    }

    /// Runs a conversion from one filename to another.
    ///
    /// The output file will be created if it does not exist, and truncated if it does.
    /// On success the validation warnings are returned.
    ///
    /// May be called more than once to convert multiple files with the same parameters.
    pub fn convert_open(
        &self,
        input_path: impl AsRef<Path>,
        output_path: impl AsRef<Path>,
    ) -> Result<Problems, Error> {
        let input = File::open(input_path)?;
        let output = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(output_path)?;
        self.convert(input, output)
    }
}