Skip to content

Commit

Permalink
Test data records and drop impl fragments
Browse files Browse the repository at this point in the history
  • Loading branch information
arnodb committed Nov 1, 2024
1 parent 3c5b157 commit cafb951
Show file tree
Hide file tree
Showing 8 changed files with 520 additions and 91 deletions.
12 changes: 8 additions & 4 deletions examples/machin/machin/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use std::{collections::BTreeMap, env, fs::File, io::Write, path::PathBuf};

use machin_data::MachinEnum;
use truc::{
generator::{config::GeneratorConfig, fragment::serde::SerdeImplGenerator, generate},
generator::{
config::GeneratorConfig,
fragment::{serde::SerdeImplGenerator, FragmentGenerator},
generate,
},
record::{
definition::{DatumDefinitionOverride, RecordDefinitionBuilder},
type_resolver::{DynamicTypeInfo, StaticTypeResolver},
Expand Down Expand Up @@ -216,9 +220,9 @@ fn serialize_deserialize() {
"{}",
generate(
&definition,
&GeneratorConfig {
custom_fragment_generators: vec![Box::new(SerdeImplGenerator)]
}
&GeneratorConfig::default_with_custom_generators([
Box::new(SerdeImplGenerator) as Box<dyn FragmentGenerator>
])
)
)
.unwrap();
Expand Down
47 changes: 44 additions & 3 deletions truc/src/generator/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,47 @@
use super::fragment::FragmentGenerator;
use super::fragment::{
data_records::DataRecordsGenerator, drop_impl::DropImplGenerator,
from_previous_record_data_records::FromPreviousRecordDataRecordsGenerator,
from_previous_record_impls::FromPreviousRecordImplsGenerator,
from_unpacked_record_impls::FromUnpackedRecordImplsGenerator, record::RecordGenerator,
record_impl::RecordImplGenerator, FragmentGenerator,
};

#[derive(Default)]
pub struct GeneratorConfig {
pub custom_fragment_generators: Vec<Box<dyn FragmentGenerator>>,
pub(crate) fragment_generators: Vec<Box<dyn FragmentGenerator>>,
}

impl GeneratorConfig {
fn common_fragment_generators() -> [Box<dyn FragmentGenerator>; 7] {
[
Box::new(DataRecordsGenerator),
Box::new(RecordGenerator),
Box::new(RecordImplGenerator),
Box::new(DropImplGenerator),
Box::new(FromUnpackedRecordImplsGenerator),
Box::new(FromPreviousRecordDataRecordsGenerator),
Box::new(FromPreviousRecordImplsGenerator),
]
}

pub fn new(fragment_generators: impl IntoIterator<Item = Box<dyn FragmentGenerator>>) -> Self {
Self {
fragment_generators: fragment_generators.into_iter().collect(),
}
}

pub fn default_with_custom_generators(
custom_generators: impl IntoIterator<Item = Box<dyn FragmentGenerator>>,
) -> Self {
Self::new(
Self::common_fragment_generators()
.into_iter()
.chain(custom_generators.into_iter()),
)
}
}

impl Default for GeneratorConfig {
fn default() -> Self {
Self::new(Self::common_fragment_generators())
}
}
218 changes: 218 additions & 0 deletions truc/src/generator/fragment/data_records.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,221 @@ using it."#,
}
}
}

#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use std::collections::BTreeSet;

use maplit::btreeset;
use pretty_assertions::assert_eq;
use syn::File;

use super::*;
use crate::{
generator::{config::GeneratorConfig, generate_variant, tests::assert_fragment_eq},
record::{definition::RecordDefinitionBuilder, type_resolver::HostTypeResolver},
};

#[test]
fn should_generate_empty_data_record() {
let mut builder = RecordDefinitionBuilder::new(HostTypeResolver);
builder.close_record_variant();
let definition = builder.build();

let config =
GeneratorConfig::new([Box::new(DataRecordsGenerator) as Box<dyn FragmentGenerator>]);

let mut scope = Scope::new();
let mut type_size_assertions = BTreeSet::new();

generate_variant(
&definition,
definition.max_type_align(),
definition.variants().next().expect("variant"),
None,
&config,
&mut scope,
&mut type_size_assertions,
);

let actual = syn::parse_str::<File>(&scope.to_string()).expect("actual");
let expected = syn::parse_str::<File>(
r#"
/// Data container for packing/unpacking records.
///
/// All the fields are named for the safe interoperability between the generated code and the code
/// using it.
pub struct UnpackedRecord0;
/// Data container for packing/unpacking records without the data to be left uninitialized.
///
/// All the fields are named for the safe interoperability between the generated code and the code
/// using it.
pub struct UnpackedUninitRecord0;
/// It only exists to check that the uninitialized data is actually [`Copy`] at run time.
struct UnpackedUninitSafeRecord0;
impl From<UnpackedUninitRecord0> for UnpackedUninitSafeRecord0 {
fn from(_from: UnpackedUninitRecord0) -> Self {
Self { }
}
}
"#,
)
.expect("expected");

if actual != expected {
assert_eq!(quote!(#expected).to_string(), quote!(#actual).to_string());
}
assert_eq!(btreeset![], type_size_assertions);
}

#[test]
fn should_generate_data_record_with_data() {
let mut builder = RecordDefinitionBuilder::new(HostTypeResolver);
builder.add_datum_allow_uninit::<u32, _>("integer");
builder.add_datum::<u32, _>("not_copy_integer");
builder.close_record_variant();
let definition = builder.build();

let config =
GeneratorConfig::new([Box::new(DataRecordsGenerator) as Box<dyn FragmentGenerator>]);

let mut scope = Scope::new();
let mut type_size_assertions = BTreeSet::new();

generate_variant(
&definition,
definition.max_type_align(),
definition.variants().next().expect("variant"),
None,
&config,
&mut scope,
&mut type_size_assertions,
);

assert_fragment_eq(
r#"
/// Data container for packing/unpacking records.
///
/// All the fields are named for the safe interoperability between the generated code and the code
/// using it.
pub struct UnpackedRecord0 {
pub integer: u32,
pub not_copy_integer: u32,
}
/// Data container for packing/unpacking records without the data to be left uninitialized.
///
/// All the fields are named for the safe interoperability between the generated code and the code
/// using it.
pub struct UnpackedUninitRecord0 {
pub not_copy_integer: u32,
}
/// It only exists to check that the uninitialized data is actually [`Copy`] at run time.
struct UnpackedUninitSafeRecord0<T0: Copy> {
pub integer: std::marker::PhantomData<T0>,
pub not_copy_integer: u32,
}
impl<T0: Copy> From<UnpackedUninitRecord0> for UnpackedUninitSafeRecord0<T0> {
fn from(from: UnpackedUninitRecord0) -> Self {
Self { integer: std::marker::PhantomData, not_copy_integer: from.not_copy_integer }
}
}
"#,
&scope.to_string(),
);

assert_eq!(
btreeset![("u32", std::mem::size_of::<u32>()),],
type_size_assertions
);
}

#[test]
fn should_generate_next_data_record_with_data() {
let mut builder = RecordDefinitionBuilder::new(HostTypeResolver);
let i0 = builder.add_datum_allow_uninit::<u32, _>("integer0");
let nci0 = builder.add_datum::<u32, _>("not_copy_integer0");
builder.add_datum_allow_uninit::<bool, _>("boolean1");
builder.close_record_variant();
builder.remove_datum(i0);
builder.remove_datum(nci0);
builder.add_datum_allow_uninit::<u32, _>("integer1");
builder.add_datum::<u32, _>("not_copy_integer1");
builder.close_record_variant();
let definition = builder.build();

let config =
GeneratorConfig::new([Box::new(DataRecordsGenerator) as Box<dyn FragmentGenerator>]);

let mut scope = Scope::new();
let mut type_size_assertions = BTreeSet::new();

let record0_spec = generate_variant(
&definition,
definition.max_type_align(),
definition.variants().next().expect("variant"),
None,
&config,
&mut scope,
&mut type_size_assertions,
);
let mut scope = Scope::new();
type_size_assertions.clear();
generate_variant(
&definition,
definition.max_type_align(),
definition.variants().nth(1).expect("variant"),
Some(&record0_spec),
&config,
&mut scope,
&mut type_size_assertions,
);

assert_fragment_eq(
r#"
/// Data container for packing/unpacking records.
///
/// All the fields are named for the safe interoperability between the generated code and the code
/// using it.
pub struct UnpackedRecord1 {
pub boolean1: bool,
pub integer1: u32,
pub not_copy_integer1: u32,
}
/// Data container for packing/unpacking records without the data to be left uninitialized.
///
/// All the fields are named for the safe interoperability between the generated code and the code
/// using it.
pub struct UnpackedUninitRecord1 {
pub not_copy_integer1: u32,
}
/// It only exists to check that the uninitialized data is actually [`Copy`] at run time.
struct UnpackedUninitSafeRecord1<T0: Copy, T1: Copy> {
pub boolean1: std::marker::PhantomData<T0>,
pub integer1: std::marker::PhantomData<T1>,
pub not_copy_integer1: u32,
}
impl<T0: Copy, T1: Copy> From<UnpackedUninitRecord1> for UnpackedUninitSafeRecord1<T0, T1> {
fn from(from: UnpackedUninitRecord1) -> Self {
Self { boolean1: std::marker::PhantomData, integer1: std::marker::PhantomData, not_copy_integer1: from.not_copy_integer1 }
}
}
"#,
&scope.to_string(),
);

assert_eq!(
btreeset![("u32", std::mem::size_of::<u32>()),],
type_size_assertions
);
}
}
Loading

0 comments on commit cafb951

Please sign in to comment.