Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions derive/src/account/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,14 @@ pub(super) fn emit_dyn_writer(
.collect();

quote! {
pub struct #writer_name<'a> {
pub struct #writer_name<'a, R: quasar_lang::ops::RentAccess> {
__view: &'a mut AccountView,
__payer: &'a AccountView,
__rent_lpb: u64,
__rent_threshold: u64,
__rent: &'a R,
#(#setter_fields,)*
}

impl<'a> core::ops::Deref for #writer_name<'a> {
impl<'a, R: quasar_lang::ops::RentAccess> core::ops::Deref for #writer_name<'a, R> {
type Target = #zc_path;

#[inline(always)]
Expand All @@ -195,14 +194,14 @@ pub(super) fn emit_dyn_writer(
}
}

impl<'a> core::ops::DerefMut for #writer_name<'a> {
impl<'a, R: quasar_lang::ops::RentAccess> core::ops::DerefMut for #writer_name<'a, R> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.__view.data_mut_ptr().add(#disc_len) as *mut #zc_path) }
}
}

impl<'a> #writer_name<'a> {
impl<'a, R: quasar_lang::ops::RentAccess> #writer_name<'a, R> {
#(#setter_methods)*

pub fn commit(&mut self) -> Result<(), ProgramError> {
Expand All @@ -213,12 +212,13 @@ pub(super) fn emit_dyn_writer(
#(#size_terms)*;
let __old_total = self.__view.data_len();
if __new_total != __old_total {
let __rent = self.__rent.get()?;
quasar_lang::accounts::account::realloc_account_raw(
self.__view,
__new_total,
self.__payer,
self.__rent_lpb,
self.__rent_threshold,
__rent.lamports_per_byte(),
__rent.exemption_threshold_raw(),
)?;
}

Expand All @@ -237,12 +237,11 @@ pub(super) fn emit_dyn_writer(

impl #name {
#[inline(always)]
pub fn compact_writer<'a>(
pub fn compact_writer<'a, R: quasar_lang::ops::RentAccess>(
&'a mut self,
payer: &'a AccountView,
rent_lpb: u64,
rent_threshold: u64,
) -> #writer_name<'a> {
rent: &'a R,
) -> #writer_name<'a, R> {
// SAFETY: `self.__view` is the transparent account backing store for this
// wrapper. Reborrowing it as `&mut AccountView` is sound here because the
// writer exclusively owns `&'a mut self` for its full lifetime and does not
Expand All @@ -251,8 +250,7 @@ pub(super) fn emit_dyn_writer(
#writer_name {
__view,
__payer: payer,
__rent_lpb: rent_lpb,
__rent_threshold: rent_threshold,
__rent: rent,
#(#setter_inits,)*
}
}
Expand Down
213 changes: 81 additions & 132 deletions derive/src/account/fixed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ pub(super) fn generate_account(
field_infos: &[PodFieldInfo<'_>],
input: &DeriveInput,
gen_set_inner: bool,
custom: bool,
) -> TokenStream {
let vis = &input.vis;
let attrs = &input.attrs;
Expand All @@ -30,105 +29,64 @@ pub(super) fn generate_account(
let zc_definition = super::layout::emit_zc_definition(name, has_dynamic, &zc);
let account_wrapper =
super::layout::emit_account_wrapper(attrs, vis, name, disc_len, &zc.zc_path);
// Custom accounts skip Owner, Discriminator, and generated AccountLoad checks —
// the user's check() method replaces all framework validation. Instead we
// generate a direct AccountLoad impl that delegates to Self::check().
let (discriminator_impl, owner_impl, space_impl, account_check_impl, custom_account_load) =
if custom {
let space = super::traits::emit_space_impl(
let (discriminator_impl, owner_impl, space_impl, account_check_impl) = if has_dynamic {
// Dynamic/compact accounts: inline validation into AccountLoad::check.
let disc = super::traits::emit_discriminator_impl(name, disc_bytes, &bump_offset_impl);
let owner = super::traits::emit_owner_impl(name);
let space =
super::traits::emit_space_impl(name, field_infos, has_dynamic, disc_len, &zc.zc_mod);
let account_load =
super::traits::emit_dynamic_account_load(super::traits::AccountLoadSpec {
name,
field_infos,
has_dynamic,
disc_len,
&zc.zc_mod,
);
let account_load = quote::quote! {
impl quasar_lang::account_load::AccountLoad for #name {

#[inline(always)]
fn check(view: &quasar_lang::__internal::AccountView) -> Result<(), quasar_lang::prelude::ProgramError> {
#name::check(view)
}
}

};
// Custom accounts do NOT get generated AccountInit/AccountExit —
// the user provides manual trait impls if needed.
(
quote::quote! {},
quote::quote! {},
space,
quote::quote! {},
account_load,
)
} else if has_dynamic {
// Dynamic/compact accounts: inline validation into AccountLoad::check.
let disc = super::traits::emit_discriminator_impl(name, disc_bytes, &bump_offset_impl);
let owner = super::traits::emit_owner_impl(name);
let space = super::traits::emit_space_impl(
name,
field_infos,
has_dynamic,
disc_len,
&zc.zc_mod,
);
let account_load =
super::traits::emit_dynamic_account_load(super::traits::AccountLoadSpec {
name,
has_dynamic,
disc_len,
disc_indices,
disc_bytes,
zc_path: &zc.zc_path,
zc_mod: &zc.zc_mod,
});
(disc, owner, space, quote::quote! {}, account_load)
} else {
// Fixed accounts: emit AccountLayout + composed checks.
// AccountLoad::check is the single source of truth, composing
// Discriminator + DataLen + ZeroPod.
let disc = super::traits::emit_discriminator_impl(name, disc_bytes, &bump_offset_impl);
let owner = super::traits::emit_owner_impl(name);
let space = super::traits::emit_space_impl(
name,
field_infos,
has_dynamic,
disc_len,
&zc.zc_mod,
);
let disc_len_lit = disc_len;
let zc_mod_ident = &zc.zc_mod;
let account_load = quote::quote! {
impl quasar_lang::account_layout::AccountLayout for #name {
type Schema = #zc_mod_ident::__Schema;
type Target = <#zc_mod_ident::__Schema as quasar_lang::__zeropod::ZeroPodFixed>::Zc;
const DATA_OFFSET: usize = #disc_len_lit;
}
disc_indices,
disc_bytes,
zc_path: &zc.zc_path,
zc_mod: &zc.zc_mod,
});
(disc, owner, space, account_load)
} else {
// Fixed accounts: emit AccountLayout + composed checks.
// AccountLoad::check is the single source of truth, composing
// Discriminator + DataLen + ZeroPod.
let disc = super::traits::emit_discriminator_impl(name, disc_bytes, &bump_offset_impl);
let owner = super::traits::emit_owner_impl(name);
let space =
super::traits::emit_space_impl(name, field_infos, has_dynamic, disc_len, &zc.zc_mod);
let disc_len_lit = disc_len;
let zc_mod_ident = &zc.zc_mod;
let account_load = quote::quote! {
impl quasar_lang::account_layout::AccountLayout for #name {
type Schema = #zc_mod_ident::__Schema;
type Target = <#zc_mod_ident::__Schema as quasar_lang::__zeropod::ZeroPodFixed>::Zc;
const DATA_OFFSET: usize = #disc_len_lit;
}

impl quasar_lang::checks::Discriminator for #name {}
impl quasar_lang::checks::DataLen for #name {}
impl quasar_lang::checks::ZeroPod for #name {}
impl quasar_lang::checks::Discriminator for #name {}
impl quasar_lang::checks::DataLen for #name {}
impl quasar_lang::checks::ZeroPod for #name {}

impl quasar_lang::account_load::AccountLoad for #name {
#[inline(always)]
fn check(view: &quasar_lang::__internal::AccountView) -> Result<(), quasar_lang::__solana_program_error::ProgramError> {
<#name as quasar_lang::checks::Discriminator>::check(view)?;
<#name as quasar_lang::checks::ZeroPod>::check(view)?;
Ok(())
}
impl quasar_lang::account_load::AccountLoad for #name {
#[inline(always)]
fn check(view: &quasar_lang::__internal::AccountView) -> Result<(), quasar_lang::__solana_program_error::ProgramError> {
<#name as quasar_lang::checks::Discriminator>::check(view)?;
<#name as quasar_lang::checks::ZeroPod>::check(view)?;
Ok(())
}

#[inline(always)]
fn check_checked(view: &quasar_lang::__internal::AccountView) -> Result<(), quasar_lang::__solana_program_error::ProgramError> {
<#name as quasar_lang::checks::Discriminator>::check_checked(view)?;
<#name as quasar_lang::checks::ZeroPod>::check_checked(view)?;
Ok(())
}
#[inline(always)]
fn check_checked(view: &quasar_lang::__internal::AccountView) -> Result<(), quasar_lang::__solana_program_error::ProgramError> {
<#name as quasar_lang::checks::Discriminator>::check_checked(view)?;
<#name as quasar_lang::checks::ZeroPod>::check_checked(view)?;
Ok(())
}
}

};
// Composed checks are the single generated validation path.
(disc, owner, space, quote::quote! {}, account_load)
};
// Composed checks are the single generated validation path.
(disc, owner, space, account_load)
};
let dynamic_impl_block =
super::dynamic::emit_dynamic_impl_block(name, has_dynamic, disc_len, &zc.zc_mod, &dynamic);
let compact_mut = super::dynamic::emit_compact_mut(
Expand Down Expand Up @@ -158,49 +116,42 @@ pub(super) fn generate_account(
gen_set_inner,
});

// Generate AccountInit + AccountExit for non-custom accounts.
// Custom accounts and one_of enums skip these — the user provides
// manual impls if needed.
let lifecycle_impls = if custom {
quote::quote! {}
} else {
quote::quote! {
impl quasar_lang::account_init::AccountInit for #name {
type InitParams<'a> = ();

#[inline(always)]
fn init<'a>(
ctx: quasar_lang::account_init::InitCtx<'a>,
_params: &(),
) -> Result<(), quasar_lang::prelude::ProgramError> {
quasar_lang::account_init::init_account(
ctx.payer,
ctx.target,
ctx.space,
ctx.program_id,
ctx.signers,
ctx.rent,
<Self as quasar_lang::traits::Discriminator>::DISCRIMINATOR,
)
}
let lifecycle_impls = quote::quote! {
impl quasar_lang::account_init::AccountInit for #name {
type InitParams<'a> = ();

#[inline(always)]
fn init<'a, R: quasar_lang::ops::RentAccess>(
ctx: quasar_lang::account_init::InitCtx<'a, R>,
_params: &(),
) -> Result<(), quasar_lang::prelude::ProgramError> {
quasar_lang::account_init::init_account(
ctx.payer,
ctx.target,
ctx.space,
ctx.program_id,
ctx.signers,
ctx.rent.get()?,
<Self as quasar_lang::traits::Discriminator>::DISCRIMINATOR,
)
}
}

impl quasar_lang::ops::close::AccountClose for #name {
#[inline(always)]
fn close(
view: &mut quasar_lang::__internal::AccountView,
dest: &quasar_lang::__internal::AccountView,
) -> Result<(), quasar_lang::prelude::ProgramError> {
quasar_lang::ops::close::close_account(
view,
dest,
<Self as quasar_lang::traits::Discriminator>::DISCRIMINATOR.len(),
)
}
impl quasar_lang::ops::close::AccountClose for #name {
#[inline(always)]
fn close(
view: &mut quasar_lang::__internal::AccountView,
dest: &quasar_lang::__internal::AccountView,
) -> Result<(), quasar_lang::prelude::ProgramError> {
quasar_lang::ops::close::close_account(
view,
dest,
<Self as quasar_lang::traits::Discriminator>::DISCRIMINATOR.len(),
)
}

impl quasar_lang::ops::SupportsRealloc for #name {}
}

impl quasar_lang::ops::SupportsRealloc for #name {}
};

// IDL fragment emission (feature-gated)
Expand Down Expand Up @@ -323,8 +274,6 @@ pub(super) fn generate_account(

#account_check_impl

#custom_account_load

#lifecycle_impls

#dynamic_impl_block
Expand Down
10 changes: 5 additions & 5 deletions derive/src/account/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,11 @@ pub(super) fn emit_set_inner_impl(spec: SetInnerSpec<'_>) -> proc_macro2::TokenS

impl #name {
#[inline(always)]
pub fn set_inner(
pub fn set_inner<R: quasar_lang::ops::RentAccess>(
&mut self,
inner: #inner_name<'_>,
payer: &AccountView,
rent_lpb: u64,
rent_threshold: u64,
rent: &R,
) -> Result<(), ProgramError> {
#(let #init_field_names = inner.#init_field_names;)*
#(#max_checks)*
Expand All @@ -119,12 +118,13 @@ pub(super) fn emit_set_inner_impl(spec: SetInnerSpec<'_>) -> proc_macro2::TokenS
let __view = unsafe { &mut *(self as *mut Self as *mut AccountView) };

if __space != __view.data_len() {
let __rent = rent.get()?;
quasar_lang::accounts::account::realloc_account_raw(
__view,
__space,
payer,
rent_lpb,
rent_threshold,
__rent.lamports_per_byte(),
__rent.exemption_threshold_raw(),
)?;
}

Expand Down
Loading
Loading