ot_tools_io/projects/
metadata.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Type and parsing of a project's OS metadata.
7//! Used in the [`crate::projects::ProjectFile`] type.
8
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12use crate::projects::{
13    parse_hashmap_string_value, string_to_hashmap, ProjectParseError, SectionHeader,
14};
15
16/*
17Example data:
18[META]\r\nTYPE=OCTATRACK DPS-1 PROJECT\r\nVERSION=19\r\nOS_VERSION=R0177     1.40B\r\n[/META]
19------
20[META]
21TYPE=OCTATRACK DPS-1 PROJECT
22VERSION=19
23OS_VERSION=R0177     1.40B
24[/META]
25*/
26
27/// Operating system metadata read from a `project.*` file.
28/// The octatrack checks these fields on project load/open to ensure:
29/// 1. it is possible to load the project (the project is not from an unrecognised OS version where a patch has not yet been installed)
30/// 2. the project is upgraded to run on a newer OS version (need to patch the project data files as tey were created with an older OS)
31#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
32pub struct OsMetadata {
33    /// Type of file (always a 'project').
34    ///
35    /// Example ASCII data:
36    /// ```text
37    /// TYPE=OCTATRACK DPS-1 PROJECT
38    /// ```
39    pub filetype: String,
40
41    /// Version of `project.*` data type in this file, a la the `datatype_version` field on other main file types (probably?)
42    ///
43    /// Example ASCII data:
44    /// ```text
45    /// VERSION=19
46    /// ```
47    pub project_version: u32,
48
49    /// Version of the Octatrack OS (that the project was created with?).
50    ///
51    /// Example ASCII data:
52    /// ```text
53    /// OS_VERSION=R0177     1.40B
54    /// ```
55    pub os_version: String,
56}
57
58impl Default for OsMetadata {
59    fn default() -> Self {
60        Self {
61            filetype: "OCTATRACK DPS-1 PROJECT".to_string(),
62            project_version: 19,
63            os_version: "R0177     1.40B".to_string(),
64        }
65    }
66}
67
68impl std::str::FromStr for OsMetadata {
69    type Err = ProjectParseError;
70
71    /// Extract `OctatrackProjectMetadata` fields from the project file's ASCII data
72    fn from_str(s: &str) -> Result<Self, Self::Err> {
73        let hmap: HashMap<String, String> = string_to_hashmap(s, &SectionHeader::Meta)?;
74
75        Ok(Self {
76            filetype: parse_hashmap_string_value::<String>(&hmap, "type", None)
77                .map_err(|_| ProjectParseError::String)?,
78            project_version: parse_hashmap_string_value::<u32>(&hmap, "version", None)?,
79            os_version: parse_hashmap_string_value::<String>(&hmap, "os_version", None)
80                .map_err(|_| ProjectParseError::String)?,
81        })
82    }
83}
84
85impl std::fmt::Display for OsMetadata {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
87        let mut s = "".to_string();
88        s.push_str("[META]\r\n");
89        s.push_str(format!("TYPE={}", self.filetype).as_str());
90        s.push_str("\r\n");
91        s.push_str(format!("VERSION={}", self.project_version).as_str());
92        s.push_str("\r\n");
93        s.push_str(format!("OS_VERSION={}", self.os_version).as_str());
94        s.push_str("\r\n[/META]");
95        write!(f, "{s:#}")
96    }
97}