-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: validate for unique IDs in structs and enums
Add the first "compiler" validation logic, which ensures that each ID: - in named or unnamed fields of a struct or enum variant is unique - in variants of an enum is unique
- Loading branch information
Showing
12 changed files
with
180 additions
and
18 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ license.workspace = true | |
|
||
[dependencies] | ||
stef-parser = { path = "../stef-parser" } | ||
thiserror.workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
use std::collections::HashMap; | ||
|
||
use stef_parser::{Enum, Fields, Id, Struct}; | ||
use thiserror::Error; | ||
|
||
#[derive(Debug, Error)] | ||
pub enum DuplicateId { | ||
#[error("duplicate ID in an enum variant")] | ||
EnumVariant(#[from] DuplicateVariantId), | ||
#[error("duplicate ID in a field")] | ||
Field(#[from] DuplicateFieldId), | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
#[error("duplicate ID {} in enum variant `{name}`, already used in `{other_name}`", id.0)] | ||
pub struct DuplicateVariantId { | ||
pub id: Id, | ||
pub name: String, | ||
pub other_name: String, | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
pub enum DuplicateFieldId { | ||
#[error("duplicate ID {} in field `{name}`, already used in `{other_name}`", id.0)] | ||
Named { | ||
id: Id, | ||
name: String, | ||
other_name: String, | ||
}, | ||
#[error("duplicate ID {} in field {position}, already used in {other_position}", id.0)] | ||
Unnamed { | ||
id: Id, | ||
position: usize, | ||
other_position: usize, | ||
}, | ||
} | ||
|
||
/// Ensure all IDs inside a struct are unique (which are the field IDs). | ||
pub(crate) fn validate_struct_ids(value: &Struct<'_>) -> Result<(), DuplicateFieldId> { | ||
validate_field_ids(&value.fields) | ||
} | ||
|
||
/// Ensure all IDs inside an enum are unique, which means all variants have a unique ID, plus all | ||
/// potential fields in a variant are unique (within that variant). | ||
pub(crate) fn validate_enum_ids(value: &Enum<'_>) -> Result<(), DuplicateId> { | ||
let mut visited = HashMap::with_capacity(value.variants.len()); | ||
value | ||
.variants | ||
.iter() | ||
.find_map(|variant| { | ||
visited | ||
.insert(variant.id, variant.name) | ||
.map(|other_name| { | ||
DuplicateVariantId { | ||
id: variant.id, | ||
name: variant.name.to_owned(), | ||
other_name: other_name.to_owned(), | ||
} | ||
.into() | ||
}) | ||
.or_else(|| { | ||
validate_field_ids(&variant.fields) | ||
.err() | ||
.map(DuplicateId::from) | ||
}) | ||
}) | ||
.map_or(Ok(()), Err) | ||
} | ||
|
||
/// Ensure all field IDs of a struct or enum are unique. | ||
fn validate_field_ids(value: &Fields) -> Result<(), DuplicateFieldId> { | ||
match value { | ||
Fields::Named(named) => { | ||
let mut visited = HashMap::with_capacity(named.len()); | ||
named | ||
.iter() | ||
.find_map(|field| { | ||
visited.insert(field.id, field.name).map(|other_field| { | ||
DuplicateFieldId::Named { | ||
id: field.id, | ||
name: field.name.to_owned(), | ||
other_name: other_field.to_owned(), | ||
} | ||
}) | ||
}) | ||
.map_or(Ok(()), Err)?; | ||
} | ||
Fields::Unnamed(unnamed) => { | ||
let mut visited = HashMap::with_capacity(unnamed.len()); | ||
unnamed | ||
.iter() | ||
.enumerate() | ||
.find_map(|(pos, field)| { | ||
visited | ||
.insert(field.id, pos) | ||
.map(|other_position| DuplicateFieldId::Unnamed { | ||
id: field.id, | ||
position: pos, | ||
other_position, | ||
}) | ||
}) | ||
.map_or(Ok(()), Err)?; | ||
} | ||
Fields::Unit => {} | ||
} | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,30 @@ | ||
pub use ids::{DuplicateFieldId, DuplicateId, DuplicateVariantId}; | ||
use stef_parser::{Definition, Schema}; | ||
use thiserror::Error; | ||
|
||
mod ids; | ||
|
||
#[derive(Debug, Error)] | ||
pub enum Error { | ||
#[error("duplicate ID found")] | ||
DuplicateId(#[from] DuplicateId), | ||
} | ||
|
||
impl From<DuplicateFieldId> for Error { | ||
fn from(v: DuplicateFieldId) -> Self { | ||
Self::DuplicateId(v.into()) | ||
} | ||
} | ||
|
||
pub fn validate_schema(value: &Schema<'_>) -> Result<(), Error> { | ||
value.definitions.iter().try_for_each(validate_definition) | ||
} | ||
|
||
fn validate_definition(value: &Definition<'_>) -> Result<(), Error> { | ||
match value { | ||
Definition::Module(m) => m.definitions.iter().try_for_each(validate_definition), | ||
Definition::Struct(s) => ids::validate_struct_ids(s).map_err(Into::into), | ||
Definition::Enum(e) => ids::validate_enum_ids(e).map_err(Into::into), | ||
Definition::TypeAlias(_) | Definition::Const(_) | Definition::Import(_) => Ok(()), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
fn main() { | ||
stef_build::compile(&["src/sample.stef"], &["src/"]).unwrap(); | ||
stef_build::compile(&["schemas/*.stef"], &["schemas/"]).unwrap(); | ||
fn main() -> anyhow::Result<()> { | ||
stef_build::compile(&["src/sample.stef"], &["src/"])?; | ||
stef_build::compile(&["schemas/*.stef"], &["schemas/"])?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters