Skip to content

Commit d6dc841

Browse files
authored
feat: read the full specialisation schema from bootspec (#207)
* feat: read the full specialisation schema from bootspec This fixes #147 by changing the type of specialisations from `Generation` to `BootJson`. This means that we capture the full recursive JSON schema for specialisations, including extensions, while the previous type threw this information away. This allows for instance to set specific sort keys for specialisations. Such sort keys are allowed by the bootspec specification, but are currently not retained when parsing a bootspec document. * fixup! make sure that we adhere to the spec
1 parent b20e28c commit d6dc841

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

bootspec/rfc0125_spec.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
},
2121
"org.nixos.specialisation.v1": {
2222
"<name>": {
23+
"org.nix-community.test": {
24+
"foo": "bar"
25+
},
2326
"org.nixos.bootspec.v1": {
2427
"system": "x86_64-linux",
2528
"init": "/nix/store/xxx-nixos-system-xxx/init",

bootspec/src/generation.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ mod tests {
8888
.map(ToOwned::to_owned)
8989
.collect::<Vec<_>>();
9090
assert!(keys.contains(&SpecialisationName(String::from("<name>"))));
91+
92+
assert_eq!(
93+
from_json
94+
.specialisations
95+
.get(&SpecialisationName("<name>".into()))
96+
.unwrap()
97+
.extensions
98+
.get("org.nix-community.test")
99+
.unwrap()
100+
.as_object()
101+
.unwrap()
102+
.get("foo")
103+
.unwrap()
104+
.as_str(),
105+
Some("bar")
106+
)
91107
}
92108

93109
#[test]

bootspec/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ pub const JSON_FILENAME: &str = "boot.json";
3434
/// The type for a collection of generic extensions.
3535
pub type Extensions = HashMap<String, serde_json::Value>;
3636

37-
// !!! IMPORTANT: KEEP `BootSpec`, `Specialisations`, and `SCHEMA_VERSION` IN SYNC !!!
37+
// !!! IMPORTANT: KEEP `BootSpec`, `Specialisations`, `Specialisation`, and `SCHEMA_VERSION` IN SYNC !!!
3838
/// The current bootspec generation type.
3939
pub type BootSpec = v1::GenerationV1;
4040
/// The current specialisations type.
4141
pub type Specialisations = v1::SpecialisationsV1;
42+
/// The current specialisations type.
43+
pub type Specialisation = v1::SpecialisationV1;
4244
/// The current bootspec schema version.
4345
pub const SCHEMA_VERSION: u64 = v1::SCHEMA_VERSION;
4446

bootspec/src/v1.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use std::path::{Path, PathBuf};
55

66
use serde::{Deserialize, Serialize};
77

8+
use crate::deser;
89
use crate::error::{BootspecError, SynthesizeError};
9-
use crate::{Result, SpecialisationName, SystemConfigurationRoot};
10+
use crate::{Extensions, Result, SpecialisationName, SystemConfigurationRoot};
1011

1112
/// The V1 bootspec schema version.
1213
pub const SCHEMA_VERSION: u64 = 1;
@@ -48,7 +49,7 @@ impl GenerationV1 {
4849

4950
specialisations.insert(
5051
SpecialisationName(name.to_string()),
51-
Self::synthesize(&toplevel)?,
52+
SpecialisationV1::synthesize(&toplevel)?,
5253
);
5354
}
5455
}
@@ -62,8 +63,37 @@ impl GenerationV1 {
6263

6364
/// A mapping of V1 bootspec specialisations.
6465
///
65-
/// This structure represents the contents of the `org.nixos.specialisations.v1` key.
66-
pub type SpecialisationsV1 = HashMap<SpecialisationName, GenerationV1>;
66+
/// This structure represents the contents of the `org.nixos.specialisation.v1` key.
67+
pub type SpecialisationsV1 = HashMap<SpecialisationName, SpecialisationV1>;
68+
69+
/// A V1 bootspec specialisation.
70+
///
71+
/// This structure represents a single specialisation contained in the `org.nixos.specialisation.v1` key.
72+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
73+
pub struct SpecialisationV1 {
74+
#[serde(flatten)]
75+
pub generation: GenerationV1,
76+
#[serde(
77+
default = "HashMap::new",
78+
skip_serializing_if = "HashMap::is_empty",
79+
deserialize_with = "deser::skip_generation_fields",
80+
flatten
81+
)]
82+
pub extensions: Extensions,
83+
}
84+
85+
impl SpecialisationV1 {
86+
/// Synthesize a [`SpecialisationV1`] struct from the path to a NixOS generation.
87+
///
88+
/// This is useful when used on generations that do not have a bootspec attached to it.
89+
pub fn synthesize(generation_path: &Path) -> Result<Self> {
90+
let generation = GenerationV1::synthesize(generation_path)?;
91+
Ok(Self {
92+
generation,
93+
extensions: HashMap::new(),
94+
})
95+
}
96+
}
6797

6898
/// A V1 bootspec toplevel.
6999
///

0 commit comments

Comments
 (0)