use cargo_toml::{Inheritable, Manifest, Package, Value};
use codama_errors::CodamaResult;
use codama_koroks::{CrateKorok, UnsupportedItemKorok};
use codama_nodes::{Node, ProgramNode};
use codama_syn_helpers::extensions::*;
use crate::KorokVisitor;
#[derive(Default)]
pub struct SetProgramMetadataVisitor {
identified_public_key: Option<String>,
}
impl SetProgramMetadataVisitor {
pub fn new() -> Self {
Self::default()
}
}
impl KorokVisitor for SetProgramMetadataVisitor {
fn visit_crate(&mut self, korok: &mut CrateKorok) -> CodamaResult<()> {
self.visit_children(korok)?;
let program = match &mut korok.node {
Some(Node::Root(root)) => &mut root.program,
Some(Node::Program(program)) => program,
None => {
korok.node = Some(ProgramNode::default().into());
if let Some(Node::Program(program)) = &mut korok.node {
program
} else {
unreachable!()
}
}
_ => return Ok(()),
};
if program.name.is_empty() {
if let Some(p) = get_package(&korok.store.manifest) {
program.name = p.name.clone().into()
}
}
if program.version.is_empty() {
if let Some(Package {
version: Inheritable::Set(version),
..
}) = get_package(&korok.store.manifest)
{
program.version = version.clone()
}
}
if program.public_key.is_empty() {
if let Some(public_key) = get_metadata_solana_program_id(&korok.store.manifest) {
program.public_key = public_key.into()
}
}
if program.public_key.is_empty() {
if let Some(public_key) = &self.identified_public_key {
program.public_key = public_key.into()
}
}
Ok(())
}
fn visit_unsupported_item(&mut self, korok: &mut UnsupportedItemKorok) -> CodamaResult<()> {
let syn::Item::Macro(syn::ItemMacro { mac, .. }) = korok.ast else {
return Ok(());
};
if let ("" | "solana_program", "declare_id") =
(mac.path.prefix().as_str(), mac.path.last_str().as_str())
{
self.identified_public_key = Some(mac.tokens.to_string().replace("\"", ""));
};
Ok(())
}
}
fn get_package(manifest: &Option<Manifest>) -> Option<&Package> {
match &manifest {
Some(Manifest { package, .. }) => package.as_ref(),
_ => None,
}
}
fn get_metadata(manifest: &Option<Manifest>) -> Option<&Value> {
match get_package(manifest) {
Some(Package { metadata, .. }) => metadata.as_ref(),
_ => None,
}
}
fn get_metadata_solana_program_id(manifest: &Option<Manifest>) -> Option<&str> {
match get_metadata(manifest) {
Some(metadata) => metadata.get("solana")?.get("program-id")?.as_str(),
_ => None,
}
}