use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Result};
use crate::stream_set::generate_stream_set_type;
use crate::utils::extract_stream_fields;
pub fn expand_derive_command(input: DeriveInput) -> Result<TokenStream> {
let name = &input.ident;
let generics = &input.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let stream_fields = extract_stream_fields(&input)?;
let stream_set_name = quote::format_ident!("{}StreamSet", name);
let stream_set_type = generate_stream_set_type(&stream_set_name, &stream_fields);
let read_streams_impl = generate_read_streams(&stream_fields);
let expanded = quote! {
#stream_set_type
impl #impl_generics eventcore::CommandStreams for #name #ty_generics #where_clause {
type StreamSet = #stream_set_name;
fn read_streams(&self) -> Vec<eventcore::StreamId> {
#read_streams_impl
}
}
};
Ok(expanded)
}
fn generate_read_streams(stream_fields: &[(String, syn::Type)]) -> TokenStream {
if stream_fields.is_empty() {
return quote! { vec![] };
}
let field_accesses: Vec<_> = stream_fields
.iter()
.map(|(field_name, _)| {
let field = quote::format_ident!("{}", field_name);
quote! { self.#field.clone() }
})
.collect();
quote! {
vec![#(#field_accesses),*]
}
}
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
#[test]
fn test_expand_derive_command_with_stream_fields() {
let input: DeriveInput = parse_quote! {
#[derive(Command)]
struct TransferMoney {
#[stream]
from_account: StreamId,
#[stream]
to_account: StreamId,
amount: Money,
}
};
let result = expand_derive_command(input);
assert!(result.is_ok());
let output = result.unwrap();
let output_str = output.to_string();
assert!(output_str.contains("TransferMoneyStreamSet"));
assert!(output_str.contains("fn read_streams"));
assert!(output_str.contains("self.from_account") || output_str.contains("vec"));
}
#[test]
fn test_expand_derive_command_no_stream_fields() {
let input: DeriveInput = parse_quote! {
#[derive(Command)]
struct SimpleCommand {
data: String,
}
};
let result = expand_derive_command(input);
assert!(result.is_ok());
let output = result.unwrap();
let output_str = output.to_string();
assert!(output_str.contains("fn read_streams"));
assert!(output_str.contains("eventcore"));
}
}