avm_rs/cli/commands/
assemble.rs1use crate::assembler::Assembler;
4use crate::cli::{AssembleCommand, BytecodeFormat, GlobalOptions};
5use anyhow::{Context, Result};
6use base64::{Engine, engine::general_purpose::STANDARD as BASE64_STANDARD};
7use std::fs;
8
9pub fn handle(cmd: AssembleCommand, global: &GlobalOptions) -> Result<()> {
11 if !global.quiet && global.verbose {
12 println!("š§ Assembling TEAL source...");
13 println!("Input: {:?}", cmd.input);
14 println!("Output format: {:?}", cmd.output_format);
15 }
16
17 let source = fs::read_to_string(&cmd.input)
19 .with_context(|| format!("Failed to read TEAL file: {:?}", cmd.input))?;
20
21 let mut assembler = Assembler::new();
23 let bytecode = assembler
24 .assemble(&source)
25 .map_err(|e| anyhow::anyhow!("Assembly failed: {}", e))?;
26
27 let formatted = format_bytecode(&bytecode, &cmd.output_format)?;
29
30 if let Some(output_path) = &cmd.output {
32 fs::write(output_path, &formatted)
33 .with_context(|| format!("Failed to write output: {output_path:?}"))?;
34
35 if !global.quiet {
36 println!("ā
Assembled {} bytes to {:?}", bytecode.len(), output_path);
37 }
38 } else {
39 println!("{formatted}");
41 }
42
43 if cmd.stats && !global.quiet {
45 show_assembly_stats(&bytecode, &source, global)?;
46 }
47
48 Ok(())
49}
50
51fn format_bytecode(bytecode: &[u8], format: &BytecodeFormat) -> Result<String> {
53 match format {
54 BytecodeFormat::Hex => Ok(hex::encode(bytecode)),
55 BytecodeFormat::Base64 => Ok(BASE64_STANDARD.encode(bytecode)),
56 BytecodeFormat::Binary => {
57 Ok(hex::encode(bytecode))
60 }
61 BytecodeFormat::Auto => Ok(hex::encode(bytecode)), }
63}
64
65fn show_assembly_stats(bytecode: &[u8], source: &str, global: &GlobalOptions) -> Result<()> {
67 let source_lines = source
68 .lines()
69 .filter(|line| {
70 let line = line.trim();
71 !line.is_empty()
72 && !line.starts_with("//")
73 && !line.starts_with(";")
74 && !line.starts_with("#pragma")
75 })
76 .count();
77
78 match global.format {
79 crate::cli::OutputFormat::Text => {
80 println!("\nš Assembly Statistics:");
81 println!(" Source lines: {source_lines}");
82 println!(" Bytecode size: {} bytes", bytecode.len());
83 println!(
84 " Compression ratio: {:.2}x",
85 source.len() as f64 / bytecode.len() as f64
86 );
87
88 let estimated_cost = estimate_execution_cost(bytecode);
90 println!(" Estimated cost: ~{estimated_cost} units");
91 }
92 crate::cli::OutputFormat::Json => {
93 let stats = serde_json::json!({
94 "source_lines": source_lines,
95 "bytecode_size": bytecode.len(),
96 "compression_ratio": source.len() as f64 / bytecode.len() as f64,
97 "estimated_cost": estimate_execution_cost(bytecode)
98 });
99 println!("{}", serde_json::to_string_pretty(&stats)?);
100 }
101 }
102
103 Ok(())
104}
105
106fn estimate_execution_cost(bytecode: &[u8]) -> u64 {
108 bytecode.len() as u64
111}