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}