Skip to content

Commit

Permalink
provisioning: Rework all error handling to prevent huge memory usage
Browse files Browse the repository at this point in the history
Signed-off-by: Ikey Doherty <[email protected]>
  • Loading branch information
ikeycode committed Feb 3, 2025
1 parent 018b40c commit 7d7e11f
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 107 deletions.
3 changes: 2 additions & 1 deletion crates/provisioning/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ version = "0.1.0"
edition = "2021"

[dev-dependencies]
miette = { workspace = true }

[dependencies]
kdl = { workspace = true, features = ["span"] }
miette.workspace = true
miette = { workspace = true }
itertools = { workspace = true }
phf = { workspace = true, features = ["macros"] }
thiserror.workspace = true
24 changes: 5 additions & 19 deletions crates/provisioning/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,17 @@
//
// SPDX-License-Identifier: MPL-2.0

use std::sync::Arc;

use kdl::KdlNode;
use miette::NamedSource;
use crate::Context;

mod create_partition;
mod create_partition_table;
mod find_disk;

/// Command evaluation context
pub(crate) struct Context<'a> {
/// The document being parsed
pub(crate) document: &'a NamedSource<Arc<String>>,

/// The node being parsed
pub(crate) node: &'a KdlNode,
}

/// A command
#[derive(Debug)]
pub enum Command {
CreatePartition,
CreatePartitionTable,
CreatePartitionTable(Box<create_partition_table::Command>),
FindDisk,
}

Expand All @@ -33,19 +21,17 @@ type CommandExec = for<'a> fn(Context<'a>) -> Result<Command, crate::Error>;

/// Map of command names to functions
static COMMANDS: phf::Map<&'static str, CommandExec> = phf::phf_map! {
"find-disk" => find_disk::parse,
"create-partition" => create_partition::parse,
//"find-disk" => find_disk::parse,
//"create-partition" => create_partition::parse,
"create-partition-table" => create_partition_table::parse,
};

/// Parse a command from a node if possible
pub(crate) fn parse_command(context: Context<'_>) -> Result<Command, crate::Error> {
let name = context.node.name().value();
let func = COMMANDS.get(name).ok_or_else(|| crate::UnsupportedNode {
src: context.document.clone(),
at: context.node.span(),
id: name.to_string(),
advice: None,
name: name.into(),
})?;

func(context)
Expand Down
3 changes: 2 additions & 1 deletion crates/provisioning/src/commands/create_partition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
//
// SPDX-License-Identifier: MPL-2.0

use super::{Command, Context};
use super::Command;
use crate::Context;

/// Generate a command to create a partition
pub(crate) fn parse(_context: Context<'_>) -> Result<Command, crate::Error> {

Check warning on line 9 in crates/provisioning/src/commands/create_partition.rs

View workflow job for this annotation

GitHub Actions / Build & Test Project

[clippy] reported by reviewdog 🐶 warning: function `parse` is never used --> crates/provisioning/src/commands/create_partition.rs:9:15 | 9 | pub(crate) fn parse(_context: Context<'_>) -> Result<Command, crate::Error> { | ^^^^^ | = note: `#[warn(dead_code)]` on by default Raw Output: crates/provisioning/src/commands/create_partition.rs:9:15:w:warning: function `parse` is never used --> crates/provisioning/src/commands/create_partition.rs:9:15 | 9 | pub(crate) fn parse(_context: Context<'_>) -> Result<Command, crate::Error> { | ^^^^^ | = note: `#[warn(dead_code)]` on by default __END__
Expand Down
17 changes: 14 additions & 3 deletions crates/provisioning/src/commands/create_partition_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@
//
// SPDX-License-Identifier: MPL-2.0

use super::{Command, Context};
use crate::Context;
use crate::{get_kdl_property, FromKdlProperty, PartitionTableType};

/// Command to create a partition table
#[derive(Debug)]
pub struct Command {
/// The type of partition table to create
pub table_type: PartitionTableType,
}

/// Generate a command to create a partition table
pub(crate) fn parse(_context: Context<'_>) -> Result<Command, crate::Error> {
unimplemented!("Command not implemented");
pub(crate) fn parse(context: Context<'_>) -> Result<super::Command, crate::Error> {
let kind = get_kdl_property(context.node, "type")?;
let table_type = PartitionTableType::from_kdl_property(kind)?;

Ok(super::Command::CreatePartitionTable(Box::new(Command { table_type })))
}
3 changes: 2 additions & 1 deletion crates/provisioning/src/commands/find_disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
//
// SPDX-License-Identifier: MPL-2.0

use super::{Command, Context};
use super::Command;
use crate::Context;

/// Generate a command to find a disk
pub(crate) fn parse(_context: Context<'_>) -> Result<Command, crate::Error> {

Check warning on line 9 in crates/provisioning/src/commands/find_disk.rs

View workflow job for this annotation

GitHub Actions / Build & Test Project

[clippy] reported by reviewdog 🐶 warning: function `parse` is never used --> crates/provisioning/src/commands/find_disk.rs:9:15 | 9 | pub(crate) fn parse(_context: Context<'_>) -> Result<Command, crate::Error> { | ^^^^^ Raw Output: crates/provisioning/src/commands/find_disk.rs:9:15:w:warning: function `parse` is never used --> crates/provisioning/src/commands/find_disk.rs:9:15 | 9 | pub(crate) fn parse(_context: Context<'_>) -> Result<Command, crate::Error> { | ^^^^^ __END__
Expand Down
56 changes: 25 additions & 31 deletions crates/provisioning/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use std::{io, sync::Arc};
use miette::{Diagnostic, NamedSource, SourceSpan};
use thiserror::Error;

use crate::KdlType;

/// Error type for the provisioning crate
#[derive(Diagnostic, Debug, Error)]
pub enum Error {
Expand All @@ -22,6 +20,9 @@ pub enum Error {
#[error("unknown type")]
UnknownType,

#[error("unknown variant")]
UnknownVariant,

#[diagnostic(transparent)]
#[error(transparent)]
InvalidType(#[from] InvalidType),
Expand All @@ -36,69 +37,62 @@ pub enum Error {

#[diagnostic(transparent)]
#[error(transparent)]
ParseError(#[from] ParseError),
UnsupportedValue(#[from] UnsupportedValue),
}

/// Merged error for parsing failures
/// Returns a list of diagnostics for the user
#[derive(Debug, Diagnostic, Error)]
#[error("failed to parse KDL")]
#[diagnostic(severity(error))]
pub struct ParseError {
#[source_code]
pub src: NamedSource<Arc<String>>,
#[related]
pub diagnostics: Vec<Error>,
}

/// Error for invalid types
#[derive(Debug, Diagnostic, Error)]
#[error("property {id} should be {expected_type}, not {found_type}")]
#[error("invalid type")]
#[diagnostic(severity(error))]
pub struct InvalidType {
#[source_code]
pub src: NamedSource<Arc<String>>,

#[label("here")]
#[label]
pub at: SourceSpan,

#[help]
pub advice: Option<String>,

pub id: &'static str,
pub expected_type: KdlType,
pub found_type: KdlType,
}

/// Error for missing mandatory properties
#[derive(Debug, Diagnostic, Error)]
#[error("{name} is missing mandatory property: {id}")]
#[error("missing property: {id}")]
#[diagnostic(severity(error))]
pub struct MissingProperty {
#[source_code]
pub src: NamedSource<Arc<String>>,

#[label("here")]
#[label]
pub at: SourceSpan,

// The name of the node
pub name: String,

// The name of the missing property
pub id: &'static str,

#[help]
pub advice: Option<String>,
}

/// Error for unsupported node types
#[derive(Debug, Diagnostic, Error)]
#[error("unsupported node: {id}")]
#[error("unsupported node: {name}")]
#[diagnostic(severity(warning))]
pub struct UnsupportedNode {
#[source_code]
pub src: NamedSource<Arc<String>>,

#[label("here")]
#[label]
pub at: SourceSpan,

// The name of the node
pub id: String,
pub name: String,
}

/// Error for unsupported values
#[derive(Debug, Diagnostic, Error)]
#[error("unsupported value")]
#[diagnostic(severity(error))]
pub struct UnsupportedValue {
#[label]
pub at: SourceSpan,

#[help]
pub advice: Option<String>,
Expand Down
41 changes: 18 additions & 23 deletions crates/provisioning/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,30 @@
//
// SPDX-License-Identifier: MPL-2.0

use std::sync::Arc;
use kdl::{KdlEntry, KdlNode};

use kdl::KdlNode;
use miette::NamedSource;
use crate::{Error, InvalidType, MissingProperty};

use crate::{Error, InvalidType, KdlType, MissingProperty};

// Get a string property from a node
pub(crate) fn get_property_str(
ns: &NamedSource<Arc<String>>,
node: &KdlNode,
name: &'static str,
) -> Result<String, Error> {
// Get a property from a node
pub(crate) fn get_kdl_property<'a>(node: &'a KdlNode, name: &'static str) -> Result<&'a KdlEntry, Error> {
let entry = node.entry(name).ok_or_else(|| MissingProperty {
name: node.name().to_string(),
src: ns.clone(),
at: node.span(),
id: name,
advice: Some(format!("add `{name}=...` to bind the property")),
})?;
let value = entry.value();
let kind = KdlType::for_value(value)?;
let value = entry.value().as_string().ok_or(InvalidType {
src: ns.clone(),
at: entry.span(),
id: name,
expected_type: KdlType::String,
found_type: kind,
advice: Some("try using a quoted string".to_string()),
})?;

Ok(entry)
}

// Get a string property from a value
pub(crate) fn kdl_value_to_string(entry: &kdl::KdlEntry) -> Result<String, Error> {
let value = entry.value().as_string().ok_or(InvalidType { at: entry.span() })?;

Ok(value.to_owned())
}

// Get a string property from a node
pub(crate) fn get_property_str(node: &KdlNode, name: &'static str) -> Result<String, Error> {
let value = get_kdl_property(node, name).and_then(kdl_value_to_string)?;
Ok(value.to_owned())
}
Loading

0 comments on commit 7d7e11f

Please sign in to comment.