Skip to content

Commit 74b2980

Browse files
committed
provisioning: Add new constraints type and plumb into create-partition
Signed-off-by: Ikey Doherty <[email protected]>
1 parent e1fbb41 commit 74b2980

File tree

5 files changed

+123
-4
lines changed

5 files changed

+123
-4
lines changed

crates/provisioning/src/commands/create_partition.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
// SPDX-License-Identifier: MPL-2.0
55

6-
use crate::{get_kdl_property, get_property_str, Context, FromKdlProperty, PartitionRole};
6+
use crate::{get_kdl_property, get_property_str, Constraints, Context, FromKdlProperty, PartitionRole};
77

88
/// Command to create a partition
99
#[derive(Debug)]
@@ -16,6 +16,8 @@ pub struct Command {
1616

1717
/// The role, if any, of the partition
1818
pub role: Option<PartitionRole>,
19+
20+
pub constraints: Constraints,
1921
}
2022

2123
/// Generate a command to create a partition
@@ -28,6 +30,18 @@ pub(crate) fn parse(context: Context<'_>) -> Result<super::Command, crate::Error
2830
None
2931
};
3032

33+
let constraints =
34+
if let Some(constraints) = context.node.iter_children().find(|n| n.name().value() == "constraints") {
35+
Constraints::from_kdl_node(constraints)?
36+
} else {
37+
return Err(crate::Error::MissingNode("constraints"));
38+
};
39+
3140
// TODO: Load constraints etc
32-
Ok(super::Command::CreatePartition(Box::new(Command { disk, id, role })))
41+
Ok(super::Command::CreatePartition(Box::new(Command {
42+
disk,
43+
id,
44+
role,
45+
constraints,
46+
})))
3347
}

crates/provisioning/src/errors.rs

+21
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ pub enum Error {
3737
#[error(transparent)]
3838
UnsupportedNode(#[from] UnsupportedNode),
3939

40+
#[diagnostic(transparent)]
41+
#[error(transparent)]
42+
MissingEntry(#[from] MissingEntry),
43+
44+
#[error("missing node: {0}")]
45+
MissingNode(&'static str),
46+
4047
#[diagnostic(transparent)]
4148
#[error(transparent)]
4249
MissingProperty(#[from] MissingProperty),
@@ -84,6 +91,20 @@ pub struct MissingProperty {
8491
pub advice: Option<String>,
8592
}
8693

94+
/// Error for missing mandatory properties
95+
#[derive(Debug, Diagnostic, Error)]
96+
#[error("missing entry: {id}")]
97+
#[diagnostic(severity(error))]
98+
pub struct MissingEntry {
99+
#[label]
100+
pub at: SourceSpan,
101+
102+
pub id: String,
103+
104+
#[help]
105+
pub advice: Option<String>,
106+
}
107+
87108
/// Error for unsupported node types
88109
#[derive(Debug, Diagnostic, Error)]
89110
#[error("unsupported node: {name}")]

crates/provisioning/src/helpers.rs

+32-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
//
33
// SPDX-License-Identifier: MPL-2.0
44

5-
use kdl::{KdlEntry, KdlNode};
5+
use kdl::{KdlEntry, KdlNode, NodeKey};
66

7-
use crate::{Error, InvalidType, KdlType, MissingProperty};
7+
use crate::{Error, FromKdlType, InvalidType, KdlType, MissingEntry, MissingProperty, StorageUnit};
88

99
// Get a property from a node
1010
pub(crate) fn get_kdl_property<'a>(node: &'a KdlNode, name: &'static str) -> Result<&'a KdlEntry, Error> {
@@ -17,6 +17,19 @@ pub(crate) fn get_kdl_property<'a>(node: &'a KdlNode, name: &'static str) -> Res
1717
Ok(entry)
1818
}
1919

20+
pub(crate) fn get_kdl_entry<'a, T>(node: &'a KdlNode, id: &'a T) -> Result<&'a KdlEntry, Error>
21+
where
22+
T: Into<NodeKey> + ToString + Clone,
23+
{
24+
let entry = node.entry(id.clone()).ok_or_else(|| MissingEntry {
25+
at: node.span(),
26+
id: id.to_string(),
27+
advice: None,
28+
})?;
29+
30+
Ok(entry)
31+
}
32+
2033
// Get a string property from a value
2134
pub(crate) fn kdl_value_to_string(entry: &kdl::KdlEntry) -> Result<String, Error> {
2235
let value = entry.value().as_string().ok_or(InvalidType {
@@ -27,6 +40,23 @@ pub(crate) fn kdl_value_to_string(entry: &kdl::KdlEntry) -> Result<String, Error
2740
Ok(value.to_owned())
2841
}
2942

43+
// Get an integer property from a value
44+
pub(crate) fn kdl_value_to_integer(entry: &kdl::KdlEntry) -> Result<i128, Error> {
45+
let value = entry.value().as_integer().ok_or(InvalidType {
46+
at: entry.span(),
47+
expected_type: KdlType::Integer,
48+
})?;
49+
50+
Ok(value)
51+
}
52+
53+
// Convert a KDL value to a storage size
54+
pub(crate) fn kdl_value_to_storage_size(entry: &kdl::KdlEntry) -> Result<u64, Error> {
55+
let value = kdl_value_to_integer(entry)?;
56+
let units = StorageUnit::from_kdl_type(entry)?;
57+
Ok(value as u64 * units as u64)
58+
}
59+
3060
// Get a string property from a node
3161
pub(crate) fn get_property_str(node: &KdlNode, name: &'static str) -> Result<String, Error> {
3262
let value = get_kdl_property(node, name).and_then(kdl_value_to_string)?;

crates/provisioning/src/types.rs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ pub use partition_role::*;
1616

1717
mod units;
1818
pub use units::*;
19+
pub mod constraints;
20+
pub use constraints::*;
1921

2022
/// The type of a KDL value
2123
#[derive(Debug)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-FileCopyrightText: Copyright © 2025 AerynOS Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use crate::{get_kdl_entry, kdl_value_to_storage_size};
6+
7+
/// Constraints for partition size, 1:1 mapping to SizeRequirements in
8+
/// partitioning strategy internals.
9+
#[derive(Debug)]
10+
pub enum Constraints {
11+
/// Exact size in bytes
12+
Exact(u64),
13+
/// Minimum size in bytes, using more if available
14+
AtLeast(u64),
15+
/// Between min and max bytes
16+
Range { min: u64, max: u64 },
17+
/// Use all remaining space
18+
Remaining,
19+
}
20+
21+
impl Constraints {
22+
pub fn from_kdl_node(node: &kdl::KdlNode) -> Result<Self, crate::Error> {
23+
let range = node
24+
.iter_children()
25+
.find(|n| n.name().value() == "min")
26+
.zip(node.iter_children().find(|n| n.name().value() == "max"));
27+
28+
if let Some((min, max)) = range {
29+
let min = kdl_value_to_storage_size(get_kdl_entry(min, &0)?)? as u64;
30+
let max = kdl_value_to_storage_size(get_kdl_entry(max, &0)?)? as u64;
31+
32+
Ok(Self::Range {
33+
min: min as u64,
34+
max: max as u64,
35+
})
36+
} else if let Some(min) = node.iter_children().find(|n| n.name().value() == "min") {
37+
let min = kdl_value_to_storage_size(get_kdl_entry(min, &0)?)? as u64;
38+
Ok(Self::AtLeast(min as u64))
39+
} else if let Some(exact) = node.iter_children().find(|n| n.name().value() == "exactly") {
40+
let exact = kdl_value_to_storage_size(get_kdl_entry(exact, &0)?)? as u64;
41+
Ok(Self::Exact(exact as u64))
42+
} else if node.iter_children().any(|n| n.name().value() == "remaining") {
43+
Ok(Self::Remaining)
44+
} else {
45+
Err(crate::Error::MissingProperty(crate::MissingProperty {
46+
at: node.span(),
47+
id: "min, max, exactly or remaining",
48+
advice: Some("add one of these properties".into()),
49+
}))
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)