Skip to main content

ot_tools_io/projects/
metadata.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2026 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 ot_tools_io_derive::{AsMutDerive, AsRefDerive};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13use crate::projects::{
14    parse_hashmap_string_value, string_to_hashmap, ProjectParseError, SectionHeader,
15};
16
17/*
18Example data:
19[META]\r\nTYPE=OCTATRACK DPS-1 PROJECT\r\nVERSION=19\r\nOS_VERSION=R0177     1.40B\r\n[/META]
20------
21[META]
22TYPE=OCTATRACK DPS-1 PROJECT
23VERSION=19
24OS_VERSION=R0177     1.40B
25[/META]
26*/
27
28/// Operating system metadata read from a `project.*` file.
29/// The octatrack checks these fields on project load/open to ensure:
30/// 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)
31/// 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)
32///
33/// No `Copy` trait as we (currently) use the `String` type for several fields
34#[derive(
35    Clone,
36    Debug,
37    Eq,
38    Hash,
39    Ord,
40    PartialEq,
41    PartialOrd,
42    Serialize,
43    Deserialize,
44    AsMutDerive,
45    AsRefDerive,
46)]
47pub struct OsMetadata {
48    /// Type of file (always a 'project').
49    ///
50    /// Example ASCII data:
51    /// ```text
52    /// TYPE=OCTATRACK DPS-1 PROJECT
53    /// ```
54    pub filetype: String,
55
56    /// Version of `project.*` data type in this file, a la the `datatype_version` field on other main file types (probably?)
57    ///
58    /// Example ASCII data:
59    /// ```text
60    /// VERSION=19
61    /// ```
62    pub project_version: u32,
63
64    /// Version of the Octatrack OS (that the project was created with?).
65    ///
66    /// Example ASCII data:
67    /// ```text
68    /// OS_VERSION=R0177     1.40B
69    /// ```
70    pub os_version: String,
71}
72
73impl Default for OsMetadata {
74    fn default() -> Self {
75        Self {
76            filetype: "OCTATRACK DPS-1 PROJECT".to_string(),
77            project_version: 19,
78            os_version: "R0177     1.40B".to_string(),
79        }
80    }
81}
82
83impl std::str::FromStr for OsMetadata {
84    type Err = ProjectParseError;
85
86    /// Extract `OctatrackProjectMetadata` fields from the project file's ASCII data
87    fn from_str(s: &str) -> Result<Self, Self::Err> {
88        let hmap: HashMap<String, String> = string_to_hashmap(s, &SectionHeader::Meta)?;
89
90        Ok(Self {
91            filetype: parse_hashmap_string_value::<String>(&hmap, "type", None)
92                .map_err(|_| ProjectParseError::String)?,
93            project_version: parse_hashmap_string_value::<u32>(&hmap, "version", None)?,
94            os_version: parse_hashmap_string_value::<String>(&hmap, "os_version", None)
95                .map_err(|_| ProjectParseError::String)?,
96        })
97    }
98}
99
100impl std::fmt::Display for OsMetadata {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
102        let mut s = "".to_string();
103        s.push_str("[META]\r\n");
104        s.push_str(format!("TYPE={}", self.filetype).as_str());
105        s.push_str("\r\n");
106        s.push_str(format!("VERSION={}", self.project_version).as_str());
107        s.push_str("\r\n");
108        s.push_str(format!("OS_VERSION={}", self.os_version).as_str());
109        s.push_str("\r\n[/META]");
110        write!(f, "{s:#}")
111    }
112}