Skip to content
This repository was archived by the owner on Feb 6, 2026. It is now read-only.

Commit bae64c7

Browse files
committed
Do not allow panics in the dal by default
This commit disallows panics in the dal by default. It fixes or permits existing panic locations along the way. Signed-off-by: Nick Gerace <nick@systeminit.com>
1 parent 030a772 commit bae64c7

16 files changed

Lines changed: 69 additions & 35 deletions

File tree

lib/dal/src/code_view.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,14 @@ impl CodeView {
8888
};
8989

9090
let code_gen_entry: CodeGenerationEntry = serde_json::from_value(func_execution)?;
91-
if code_gen_entry.code.is_none() || code_gen_entry.format.is_none() {
92-
return Ok(None);
93-
}
94-
95-
// Safe unwraps because of the above check
96-
let format = code_gen_entry.format.as_ref().unwrap();
97-
let code = code_gen_entry.code.as_ref().unwrap();
91+
let format = match code_gen_entry.format {
92+
Some(found) => found,
93+
None => return Ok(None),
94+
};
95+
let code = match code_gen_entry.code {
96+
Some(found) => found,
97+
None => return Ok(None),
98+
};
9899

99100
let language = if format.is_empty() {
100101
CodeLanguage::Unknown

lib/dal/src/component.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,14 @@ pub enum ComponentError {
9797
LayerDb(#[from] si_layer_cache::LayerDbError),
9898
#[error("component {0} missing attribute value for code")]
9999
MissingCodeValue(ComponentId),
100+
#[error("missing controlling func data for parent attribute value id: {0}")]
101+
MissingControllingFuncDataForParentAttributeValue(AttributeValueId),
100102
#[error("component {0} missing attribute value for qualifications")]
101103
MissingQualificationsValue(ComponentId),
102104
#[error("component {0} missing attribute value for root")]
103105
MissingRootProp(ComponentId),
106+
#[error("more than one schema variant found for component: {0}")]
107+
MoreThanOneSchemaVariantFound(ComponentId),
104108
#[error("found multiple parents for component: {0}")]
105109
MultipleParentsForComponent(ComponentId),
106110
#[error("found multiple root attribute values ({0} and {1}, at minimum) for component: {2}")]
@@ -607,12 +611,12 @@ impl Component {
607611
let content_hash_discriminants: ContentAddressDiscriminants =
608612
content.content_address().into();
609613
if let ContentAddressDiscriminants::SchemaVariant = content_hash_discriminants {
610-
// TODO(nick): consider creating a new edge weight kind to make this easier.
611-
// We also should use a proper error here.
612614
schema_variant_id = match schema_variant_id {
613615
None => Some(content.id().into()),
614616
Some(_already_found_schema_variant_id) => {
615-
panic!("already found a schema variant")
617+
return Err(ComponentError::MoreThanOneSchemaVariantFound(
618+
component_id,
619+
));
616620
}
617621
};
618622
}
@@ -1120,8 +1124,9 @@ impl Component {
11201124
// if av has a parent and parent is controlled by dynamic func, that's the controller
11211125
// else av controls itself
11221126
let controlling_tuple = if let Some(parent_av_id) = maybe_parent_av_id {
1123-
// We can unwrap because if we're on the child, we've added the parent to result
1124-
let parent_controlling_data = *result.get(&parent_av_id).unwrap();
1127+
let parent_controlling_data = *result.get(&parent_av_id).ok_or(
1128+
ComponentError::MissingControllingFuncDataForParentAttributeValue(parent_av_id),
1129+
)?;
11251130

11261131
if parent_controlling_data.is_dynamic_func {
11271132
parent_controlling_data

lib/dal/src/context.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ impl ConnectionState {
183183
matches!(self, Self::Connections(_))
184184
}
185185

186+
#[allow(clippy::panic)]
186187
fn txns(&mut self) -> &mut Transactions {
187188
match self {
188189
Self::Transactions(txns) => txns,
@@ -625,20 +626,19 @@ impl DalContext {
625626
new
626627
}
627628

628-
pub async fn parent_is_head(&self) -> bool {
629+
pub async fn parent_is_head(&self) -> Result<bool, TransactionsError> {
629630
if let Some(workspace_pk) = self.tenancy.workspace_pk() {
630631
let workspace = Workspace::get_by_pk(self, &workspace_pk)
631632
.await
632-
.unwrap()
633-
.unwrap();
634-
workspace.default_change_set_id()
635-
== self
636-
.change_set_pointer()
637-
.unwrap()
638-
.base_change_set_id
639-
.unwrap()
633+
.map_err(|err| TransactionsError::Workspace(err.to_string()))?
634+
.ok_or(TransactionsError::WorkspaceNotFound(workspace_pk))?;
635+
let change_set = self.change_set_pointer()?;
636+
let base_change_set_id = change_set
637+
.base_change_set_id
638+
.ok_or(TransactionsError::NoBaseChangeSet(change_set.id))?;
639+
Ok(workspace.default_change_set_id() == base_change_set_id)
640640
} else {
641-
false
641+
Ok(false)
642642
}
643643
}
644644

@@ -1028,6 +1028,8 @@ pub enum TransactionsError {
10281028
JobQueueProcessor(#[from] JobQueueProcessorError),
10291029
#[error(transparent)]
10301030
Nats(#[from] NatsError),
1031+
#[error("no base change set for change set: {0}")]
1032+
NoBaseChangeSet(ChangeSetId),
10311033
#[error(transparent)]
10321034
Pg(#[from] PgError),
10331035
#[error(transparent)]

lib/dal/src/func.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub enum FuncError {
3232
Base64Decode(#[from] base64::DecodeError),
3333
#[error("change set error: {0}")]
3434
ChangeSet(#[from] ChangeSetPointerError),
35+
#[error("chrono parse error: {0}")]
36+
ChronoParse(#[from] chrono::ParseError),
3537
#[error("edge weight error: {0}")]
3638
EdgeWeight(#[from] EdgeWeightError),
3739
#[error("cannot find intrinsic func {0}")]

lib/dal/src/func/backend/js_action.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ impl FuncDispatch for FuncBackendJsAction {
3838
execution_id: "ayrtonsennajscommand".to_string(),
3939
handler: handler.into(),
4040
code_base64: code_base64.into(),
41-
args: serde_json::to_value(args).unwrap(),
41+
args: serde_json::to_value(args)
42+
.expect("should be impossible to fail serialization here"),
4243
before,
4344
};
4445

lib/dal/src/func/backend/js_reconciliation.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ impl FuncDispatch for FuncBackendJsReconciliation {
5151
execution_id: "freeronaldinhogauchojsreconciliation".to_string(),
5252
handler: handler.into(),
5353
code_base64: code_base64.into(),
54-
args: serde_json::to_value(args).unwrap(),
54+
args: serde_json::to_value(args)
55+
.expect("should be impossible to fail serialization here"),
5556
before,
5657
};
5758

lib/dal/src/func/intrinsics.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,9 @@ impl IntrinsicFunc {
2727
let mut builder = PkgSpec::builder();
2828
builder.name("si-intrinsic-funcs");
2929
builder.version("2023-05-24");
30-
builder.created_at(
31-
DateTime::parse_from_rfc2822("Wed, 24 May 2023 00:00:00 PST")
32-
.expect("able to parse default datetime"),
33-
);
30+
builder.created_at(DateTime::parse_from_rfc2822(
31+
"Wed, 24 May 2023 00:00:00 PST",
32+
)?);
3433
builder.created_by("System Initiative");
3534
for instrinsic in IntrinsicFunc::iter() {
3635
builder.func(instrinsic.to_spec()?);

lib/dal/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
//! The Data Access Layer (DAL) for System Initiative.
22
3+
#![warn(
4+
clippy::panic,
5+
clippy::panic_in_result_fn,
6+
clippy::unwrap_in_result,
7+
clippy::unwrap_used
8+
)]
9+
310
use std::time::Duration;
411

512
use rand::Rng;

lib/dal/src/pkg.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ pub enum PkgError {
8585
SchemaVariant(#[from] SchemaVariantError),
8686
#[error("json serialization error: {0}")]
8787
SerdeJson(#[from] serde_json::Error),
88+
#[error("taking output socket as input for a prop is unsupported for name ({0}) and socket name ({1})")]
89+
TakingOutputSocketAsInputForPropUnsupported(String, String),
8890
#[error("transactions error: {0}")]
8991
Transactions(#[from] TransactionsError),
9092
#[error(transparent)]

lib/dal/src/pkg/import.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3069,9 +3069,13 @@ async fn create_attr_proto_arg(
30693069
.await?;
30703070
apa_id
30713071
}
3072-
_ => {
3073-
// xxx: make this an error
3074-
panic!("unsupported taking output socket as input for prop");
3072+
SiPkgAttrFuncInputView::OutputSocket {
3073+
name, socket_name, ..
3074+
} => {
3075+
return Err(PkgError::TakingOutputSocketAsInputForPropUnsupported(
3076+
name.to_owned(),
3077+
socket_name.to_owned(),
3078+
));
30753079
}
30763080
})
30773081
}

0 commit comments

Comments
 (0)