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
5 changes: 5 additions & 0 deletions .changeset/good-readers-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@codama/renderers-rust': patch
---

Add anchorTraits option to Rust renderer
1 change: 1 addition & 0 deletions packages/renderers-rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ The `renderVisitor` accepts the following options.
| `dependencyMap` | `Record<string, string>` | `{}` | A mapping between import aliases and their actual crate name or path in Rust. |
| `renderParentInstructions` | `boolean` | `false` | When using nested instructions, whether the parent instructions should also be rendered. When set to `false` (default), only the instruction leaves are being rendered. |
| `traitOptions` | [`TraitOptions`](#trait-options) | `DEFAULT_TRAIT_OPTIONS` | A set of options that can be used to configure how traits are rendered for every Rust types. See [documentation below](#trait-options) for more information. |
| `anchorTraits` | `boolean` | `true` | Whether to generate Anchor traits `impl` for account types. |

## Trait Options

Expand Down
50 changes: 26 additions & 24 deletions packages/renderers-rust/public/templates/accountsPage.njk
Original file line number Diff line number Diff line change
Expand Up @@ -128,33 +128,35 @@ impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for {{ account.
}
}

#[cfg(feature = "anchor")]
impl anchor_lang::AccountDeserialize for {{ account.name | pascalCase }} {
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
Ok(Self::deserialize(buf)?)
}
}
{% if anchorTraits %}
#[cfg(feature = "anchor")]
impl anchor_lang::AccountDeserialize for {{ account.name | pascalCase }} {
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
Ok(Self::deserialize(buf)?)
}
}

#[cfg(feature = "anchor")]
impl anchor_lang::AccountSerialize for {{ account.name | pascalCase }} {}
#[cfg(feature = "anchor")]
impl anchor_lang::AccountSerialize for {{ account.name | pascalCase }} {}

#[cfg(feature = "anchor")]
impl anchor_lang::Owner for {{ account.name | pascalCase }} {
fn owner() -> Pubkey {
crate::{{ program.name | snakeCase | upper }}_ID
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::Owner for {{ account.name | pascalCase }} {
fn owner() -> Pubkey {
crate::{{ program.name | snakeCase | upper }}_ID
}
}

#[cfg(feature = "anchor-idl-build")]
impl anchor_lang::IdlBuild for {{ account.name | pascalCase }} {}
#[cfg(feature = "anchor-idl-build")]
impl anchor_lang::IdlBuild for {{ account.name | pascalCase }} {}

{#
This is not used for the IDL generation since default `IdlBuild` impl doesn't include
the type in the IDL but it stil needs to be added in order to make compilation work.
#}
#[cfg(feature = "anchor-idl-build")]
impl anchor_lang::Discriminator for {{ account.name | pascalCase }} {
const DISCRIMINATOR: [u8; 8] = [0; 8];
}
{#
This is not used for the IDL generation since default `IdlBuild` impl doesn't include
the type in the IDL but it stil needs to be added in order to make compilation work.
#}
#[cfg(feature = "anchor-idl-build")]
impl anchor_lang::Discriminator for {{ account.name | pascalCase }} {
const DISCRIMINATOR: [u8; 8] = [0; 8];
}
{% endif %}

{% endblock %}
3 changes: 3 additions & 0 deletions packages/renderers-rust/src/getRenderMapVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { renderValueNode } from './renderValueNodeVisitor';
import { getImportFromFactory, getTraitsFromNodeFactory, LinkOverrides, render, TraitOptions } from './utils';

export type GetRenderMapOptions = {
anchorTraits?: boolean;
defaultTraitOverrides?: string[];
dependencyMap?: Record<string, string>;
linkOverrides?: LinkOverrides;
Expand All @@ -46,6 +47,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
const getImportFrom = getImportFromFactory(options.linkOverrides ?? {});
const getTraitsFromNode = getTraitsFromNodeFactory(options.traitOptions);
const typeManifestVisitor = getTypeManifestVisitor({ getImportFrom, getTraitsFromNode });
const anchorTraits = options.anchorTraits ?? true;

return pipe(
staticVisitor(
Expand Down Expand Up @@ -92,6 +94,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
`accounts/${snakeCase(node.name)}.rs`,
render('accountsPage.njk', {
account: node,
anchorTraits,
constantSeeds,
hasVariableSeeds,
imports: imports
Expand Down
32 changes: 31 additions & 1 deletion packages/renderers-rust/test/accountsPage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { visit } from '@codama/visitors-core';
import { test } from 'vitest';

import { getRenderMapVisitor } from '../src';
import { codeContains } from './_setup';
import { codeContains, codeDoesNotContains } from './_setup';

test('it renders a byte array seed used on an account', () => {
// Given the following program with 1 account and 1 pda with a byte array as seeds.
Expand Down Expand Up @@ -134,3 +134,33 @@ test('it renders anchor traits impl', () => {
'impl anchor_lang::Owner for TestAccount',
]);
});

test('it renders account without anchor traits', () => {
// Given the following account.
const node = programNode({
accounts: [
accountNode({
discriminators: [
{
kind: 'fieldDiscriminatorNode',
name: camelCase('discriminator'),
offset: 0,
},
],
name: 'testAccount',
pda: pdaLinkNode('testPda'),
}),
],
name: 'myProgram',
publicKey: '1111',
});

// When we render it with anchor traits disabled.
const renderMap = visit(node, getRenderMapVisitor({ anchorTraits: false }));

// Then we do not expect Anchor traits.
codeDoesNotContains(renderMap.get('accounts/test_account.rs'), [
'#[cfg(feature = "anchor")]',
'#[cfg(feature = "anchor-idl-build")]',
]);
});