bootspec/
lib.rs

1mod deser;
2pub mod error;
3pub mod generation;
4pub mod v1;
5
6use std::collections::HashMap;
7use std::fmt;
8use std::path::{Path, PathBuf};
9
10use serde::{Deserialize, Serialize};
11
12use crate::error::{BootspecError, SynthesizeError};
13use crate::generation::Generation;
14
15#[doc(hidden)]
16pub(crate) type Result<T, E = BootspecError> = core::result::Result<T, E>;
17
18/// A wrapper type describing the name of a NixOS specialisation.
19#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
20pub struct SpecialisationName(pub String);
21
22impl fmt::Display for SpecialisationName {
23    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24        write!(f, "{}", self.0)
25    }
26}
27
28/// A wrapper type describing the root directory of a NixOS system configuration.
29#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
30pub struct SystemConfigurationRoot(pub PathBuf);
31
32/// The bootspec schema filename.
33pub const JSON_FILENAME: &str = "boot.json";
34/// The type for a collection of generic extensions.
35pub type Extensions = HashMap<String, serde_json::Value>;
36
37// !!! IMPORTANT: KEEP `BootSpec`, `Specialisations`, `Specialisation`, and `SCHEMA_VERSION` IN SYNC !!!
38/// The current bootspec generation type.
39pub type BootSpec = v1::GenerationV1;
40/// The current specialisations type.
41pub type Specialisations = v1::SpecialisationsV1;
42/// The current specialisations type.
43pub type Specialisation = v1::SpecialisationV1;
44/// The current bootspec schema version.
45pub const SCHEMA_VERSION: u64 = v1::SCHEMA_VERSION;
46
47/// The current bootspec schema.
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
49pub struct BootJson {
50    #[serde(flatten)]
51    pub generation: Generation,
52    #[serde(
53        default = "HashMap::new",
54        skip_serializing_if = "HashMap::is_empty",
55        deserialize_with = "deser::skip_generation_fields",
56        flatten
57    )]
58    pub extensions: Extensions,
59}
60
61impl BootJson {
62    /// Synthesize a [`BootJson`] struct from the path to a generation using the latest
63    /// specification version defined in this crate ([`SCHEMA_VERSION`]).
64    ///
65    /// See also [`BootJson::synthesize_version`].
66    ///
67    /// ## Warnings
68    ///
69    /// Extensions will not be synthesized and will be an empty [`HashMap`].
70    pub fn synthesize_latest(generation_path: &Path) -> Result<BootJson> {
71        Self::synthesize_version(generation_path, SCHEMA_VERSION)
72    }
73
74    /// Synthesize a [`BootJson`] struct from the path to a generation and a specific version.
75    ///
76    /// This is useful when used on generations that do not have a bootspec attached to it.
77    /// This will not synthesize arbitrary extensions.
78    ///
79    /// ## Warnings
80    ///
81    /// Extensions will not be synthesized and will be an empty [`HashMap`].
82    pub fn synthesize_version(generation_path: &Path, version: u64) -> Result<BootJson> {
83        let generation = match version {
84            v1::SCHEMA_VERSION => {
85                let generation = v1::GenerationV1::synthesize(generation_path)?;
86                Generation::V1(generation)
87            }
88            v => {
89                return Err(BootspecError::Synthesize(
90                    SynthesizeError::UnsupportedVersion(v),
91                ))
92            }
93        };
94
95        Ok(BootJson {
96            generation,
97            extensions: HashMap::new(),
98        })
99    }
100}