Skip to content
Open
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
35 changes: 29 additions & 6 deletions cli/golem-cli/src/app/build/gen_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use crate::app::build::up_to_date_check::new_task_up_to_date_check;
use crate::app::context::BuildContext;
use crate::bridge_gen::rust::RustBridgeGenerator;
use crate::bridge_gen::typescript::TypeScriptBridgeGenerator;
use crate::bridge_gen::{BridgeGenerator, bridge_client_directory_name};
use crate::bridge_gen::{
BridgeGenerator, BridgeGeneratorConfig, DeriveRule, bridge_client_directory_name,
};
use crate::command::GolemCliCommand;
use crate::error::NonSuccessfulExit;
use crate::fs;
Expand Down Expand Up @@ -103,6 +105,19 @@ async fn collect_manifest_targets(ctx: &BuildContext<'_>) -> anyhow::Result<Vec<
agent_type,
target_language,
output_dir,
derive_rules: sdks_targets
.additional_derives
.as_ref()
.map(|rules| {
rules
.iter()
.map(|r| DeriveRule {
pattern: r.pattern.clone(),
derives: r.derives.clone(),
})
.collect()
})
.unwrap_or_default(),
});
}
}
Expand Down Expand Up @@ -173,6 +188,7 @@ async fn collect_custom_targets(
agent_type,
target_language,
output_dir,
derive_rules: custom_target.derive_rules.clone(),
});
}
}
Expand Down Expand Up @@ -200,12 +216,15 @@ async fn gen_bridge_sdk_target(
let final_wasm = component.final_wasm();
let agent_type_name = target.agent_type.type_name.clone();
let output_dir = Utf8PathBuf::try_from(target.output_dir)?;
let config = BridgeGeneratorConfig::new(target.derive_rules.clone())?;
let generator_config = config.clone();

new_task_up_to_date_check(ctx)
.with_task_result_marker(GenerateBridgeSdkMarkerHash {
component_name: &target.component_name,
agent_type_name: &target.agent_type.type_name,
language: &target.target_language,
derive_rules: &config.derive_rules,
})?
.with_sources(|| vec![&final_wasm])
.with_targets(|| vec![&output_dir])
Expand All @@ -227,12 +246,16 @@ async fn gen_bridge_sdk_target(
target.agent_type,
&output_dir,
false,
generator_config,
)?),
GuestLanguage::TypeScript => Box::new(TypeScriptBridgeGenerator::new(
target.agent_type,
&output_dir,
false,
)?),
GuestLanguage::TypeScript => {
Box::new(<TypeScriptBridgeGenerator as BridgeGenerator>::new(
target.agent_type,
&output_dir,
false,
generator_config,
)?)
}
GuestLanguage::Scala => {
bail!("Bridge generation is not yet supported for Scala")
}
Expand Down
40 changes: 38 additions & 2 deletions cli/golem-cli/src/app/build/task_result_marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

use crate::app::build::task_result_marker::TaskResultMarkerHashSourceKind::{Hash, HashFromString};
use crate::bridge_gen::DeriveRule;
use crate::fs;
use crate::log::log_warn_action;
use crate::model::app_raw::{
Expand Down Expand Up @@ -319,6 +320,7 @@ pub struct GenerateBridgeSdkMarkerHash<'a> {
pub component_name: &'a ComponentName,
pub agent_type_name: &'a AgentTypeName,
pub language: &'a GuestLanguage,
pub derive_rules: &'a [DeriveRule],
}

impl TaskResultMarkerHashSource for GenerateBridgeSdkMarkerHash<'_> {
Expand All @@ -332,12 +334,46 @@ impl TaskResultMarkerHashSource for GenerateBridgeSdkMarkerHash<'_> {

fn source(&self) -> anyhow::Result<TaskResultMarkerHashSourceKind> {
Ok(HashFromString(format!(
"{}-{}-{}",
self.component_name, self.agent_type_name, self.language
"{}-{}-{}-{:?}",
self.component_name, self.agent_type_name, self.language, self.derive_rules
)))
}
}

#[cfg(test)]
mod tests {
use super::*;
use test_r::test;

#[test]
fn generate_bridge_sdk_marker_hash_includes_derive_rules() {
let component_name = ComponentName("component".to_string());
let agent_type_name = AgentTypeName("Agent".to_string());
let language = GuestLanguage::Rust;
let derive_rules = vec![DeriveRule {
pattern: "^Foo$".to_string(),
derives: vec!["PartialEq".to_string()],
}];

let source = GenerateBridgeSdkMarkerHash {
component_name: &component_name,
agent_type_name: &agent_type_name,
language: &language,
derive_rules: &derive_rules,
}
.source()
.unwrap();

match source {
HashFromString(input) => {
assert!(input.contains("^Foo$"));
assert!(input.contains("PartialEq"));
}
Hash(_) => panic!("expected bridge SDK marker source to hash input text"),
}
}
}

pub struct ExtractAgentTypeMarkerHash<'a> {
pub component_name: &'a ComponentName,
}
Expand Down
1 change: 1 addition & 0 deletions cli/golem-cli/src/app/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ impl ApplicationContext {
agent_type_names: Default::default(),
target_language: Some(language),
output_dir: Some(repl_root_bridge_sdk_dir.clone()),
derive_rules: Vec::new(),
}
}

Expand Down
53 changes: 52 additions & 1 deletion cli/golem-cli/src/bridge_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,60 @@ pub mod typescript;
use camino::Utf8Path;
use golem_common::model::agent::{AgentType, AgentTypeName};
use heck::ToKebabCase;
use regex::Regex;
use syn::Path;

/// A rule that adds derive macros to generated types whose names match a regex pattern.
///
/// Multiple rules can match the same type; their derives are merged and deduplicated.
///
/// # Examples
///
/// Add `PartialEq` to all types:
/// ```yaml
/// { pattern: ".*", derives: ["PartialEq"] }
/// ```
///
/// Add `Eq` and `Hash` only to `Uuid`:
/// ```yaml
/// { pattern: "^Uuid$", derives: ["Eq", "Hash"] }
/// ```
#[derive(Debug, Clone)]
pub struct DeriveRule {
/// Regex pattern matched against the generated type name.
pub pattern: String,
/// Derive macros to add when the pattern matches (e.g., "PartialEq", "Eq", "Hash").
pub derives: Vec<String>,
}

/// Configuration options for bridge SDK code generation.
#[derive(Debug, Clone, Default)]
pub struct BridgeGeneratorConfig {
/// Rules for adding derive macros to generated types. Each rule pairs a regex
/// pattern (matched against type names) with a list of derives to add.
pub derive_rules: Vec<DeriveRule>,
}

impl BridgeGeneratorConfig {
pub fn new(derive_rules: Vec<DeriveRule>) -> anyhow::Result<Self> {
for rule in &derive_rules {
Regex::new(&rule.pattern)?;
for derive in &rule.derives {
syn::parse_str::<Path>(derive)?;
}
}

Ok(Self { derive_rules })
}
}

pub trait BridgeGenerator {
fn new(agent_type: AgentType, target_path: &Utf8Path, testing: bool) -> anyhow::Result<Self>
fn new(
agent_type: AgentType,
target_path: &Utf8Path,
testing: bool,
config: BridgeGeneratorConfig,
) -> anyhow::Result<Self>
where
Self: Sized;
fn generate(&mut self) -> anyhow::Result<()>;
Expand Down
Loading
Loading