use crate::core::hash::{self, CommentStyle};
use crate::e2e::codegen::call_ir::{CallIr, resolve_declared_result_type};
use crate::e2e::escape::escape_csharp;
pub(super) fn resolve_csharp_call_root_type(
call_config: &crate::e2e::config::CallConfig,
type_defs: &[crate::core::ir::TypeDef],
functions: &[crate::core::ir::FunctionDef],
) -> Option<String> {
let call_ir = CallIr { functions, type_defs };
resolve_declared_result_type(call_config, "csharp", call_ir)
}
pub(super) fn render_sealed_display(
type_name: &str,
enum_def: &crate::core::ir::EnumDef,
type_defs: &[crate::core::ir::TypeDef],
namespace: &str,
) -> String {
let header = hash::header(CommentStyle::DoubleSlash);
let mut out = header;
out.push_str(&format!("namespace {namespace}.E2e;\n\n"));
out.push_str(&format!(
"/// <summary>\n/// Helper class for extracting display strings from {type_name} sealed interface.\n /// </summary>\n"
));
out.push_str(&format!("internal static class {type_name}Display\n"));
out.push_str("{\n");
out.push_str(&format!(
" internal static string ToDisplayString({type_name}? value)\n"
));
out.push_str(" {\n");
out.push_str(" if (value == null) return \"\";\n");
out.push_str(" return value switch\n");
out.push_str(" {\n");
for variant in &enum_def.variants {
let variant_name = &variant.name;
let has_format_field = variant.is_tuple && variant.fields.len() == 1 && {
let field_type_name = match &variant.fields[0].ty {
crate::core::ir::TypeRef::Named(n) => Some(n.as_str()),
_ => None,
};
field_type_name.is_some_and(|tn| {
type_defs
.iter()
.find(|td| td.name == tn)
.is_some_and(|td| td.fields.iter().any(|f| f.name == "format"))
})
};
let display = if has_format_field {
"i.Value.Format".to_string()
} else {
let wire_name = crate::codegen::naming::wire_variant_value(
variant_name,
variant.serde_rename.as_deref(),
enum_def.serde_rename_all.as_deref(),
);
format!("\"{wire_name}\"")
};
let binding = if has_format_field {
format!("{type_name}.{variant_name} i")
} else {
format!("{type_name}.{variant_name}")
};
out.push_str(&format!(" {binding} => {display},\n"));
}
out.push_str(" _ => \"unknown\",\n");
out.push_str(" };\n");
out.push_str(" }\n");
out.push_str("}\n");
out
}
pub(super) fn effective_csharp_enum_fields(
e2e_config: &crate::e2e::config::E2eConfig,
call_config: &crate::e2e::config::CallConfig,
enum_fields: &std::collections::HashMap<String, String>,
cs_overrides: Option<&crate::e2e::config::CallOverride>,
) -> std::collections::HashSet<String> {
let mut effective_enum_fields: std::collections::HashSet<String> =
e2e_config.effective_fields_enum(call_config).clone();
for k in enum_fields.keys() {
effective_enum_fields.insert(k.clone());
}
if let Some(o) = cs_overrides {
for k in o.enum_fields.keys() {
effective_enum_fields.insert(k.clone());
}
}
effective_enum_fields
}
pub(super) const CSHARP_COLLECTION_INLINE_LIMIT: usize = 8;
pub(super) fn render_collection_literal(prefix: &str, items: Vec<String>) -> String {
if items.len() <= CSHARP_COLLECTION_INLINE_LIMIT {
return format!("{prefix} {{ {} }}", items.join(", "));
}
crate::e2e::template_env::render(
"csharp/wrapped_collection_literal.jinja",
minijinja::context! { prefix => prefix, items => items },
)
.trim_end_matches('\n')
.to_string()
}
pub(super) fn json_to_csharp(value: &serde_json::Value) -> String {
match value {
serde_json::Value::String(s) => format!("\"{}\"", escape_csharp(s)),
serde_json::Value::Bool(true) => "true".to_string(),
serde_json::Value::Bool(false) => "false".to_string(),
serde_json::Value::Number(n) => {
if n.is_f64() {
format!("{}d", n)
} else {
n.to_string()
}
}
serde_json::Value::Null => "null".to_string(),
serde_json::Value::Array(arr) => {
let items: Vec<String> = arr.iter().map(json_to_csharp).collect();
render_collection_literal("new[]", items)
}
serde_json::Value::Object(_) => {
let json_str = serde_json::to_string(value).unwrap_or_default();
format!("\"{}\"", escape_csharp(&json_str))
}
}
}