Skip to main content

bock_codegen/
profile.rs

1//! Target profile definitions — capability matrices and conventions for each target language.
2
3use std::fmt;
4
5// ─── Support level ───────────────────────────────────────────────────────────
6
7/// How well a target supports a particular language construct.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum Support {
10    /// The target has direct, first-class support.
11    Native,
12    /// The target supports the construct via switch statements (pattern matching).
13    SwitchBased,
14    /// The target supports the construct via interfaces (traits).
15    InterfaceBased,
16    /// The target can express the construct through a synthesis strategy.
17    Emulated,
18    /// The target has no support; the construct cannot be represented.
19    None,
20}
21
22impl fmt::Display for Support {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            Self::Native => write!(f, "native"),
26            Self::SwitchBased => write!(f, "switch-based"),
27            Self::InterfaceBased => write!(f, "interface-based"),
28            Self::Emulated => write!(f, "emulated"),
29            Self::None => write!(f, "none"),
30        }
31    }
32}
33
34// ─── Memory model ────────────────────────────────────────────────────────────
35
36/// The memory management model of a target language.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum MemoryModel {
39    /// Garbage collected (JS, Python, Go, Java).
40    GC,
41    /// Automatic reference counting (Swift).
42    ARC,
43    /// Manual / ownership-based (Rust, C, C++).
44    Manual,
45}
46
47impl fmt::Display for MemoryModel {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        match self {
50            Self::GC => write!(f, "GC"),
51            Self::ARC => write!(f, "ARC"),
52            Self::Manual => write!(f, "manual"),
53        }
54    }
55}
56
57// ─── Async model ─────────────────────────────────────────────────────────────
58
59/// How a target handles asynchronous operations.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61pub enum AsyncModel {
62    /// Single-threaded event loop (JS).
63    EventLoop,
64    /// Green threads / goroutines (Go).
65    GreenThread,
66    /// OS threads with async runtime (Rust, Python).
67    OSThread,
68}
69
70impl fmt::Display for AsyncModel {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self {
73            Self::EventLoop => write!(f, "event-loop"),
74            Self::GreenThread => write!(f, "green-thread"),
75            Self::OSThread => write!(f, "OS-thread"),
76        }
77    }
78}
79
80// ─── Generics model ──────────────────────────────────────────────────────────
81
82/// How a target implements generic/parametric polymorphism.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84pub enum GenericsModel {
85    /// Generics are preserved at runtime (Java, C#, TypeScript).
86    Reified,
87    /// Generics are erased at compile time (Java bytecode, Go <1.18).
88    Erased,
89    /// Each instantiation generates a separate copy (Rust, C++).
90    Monomorphized,
91}
92
93impl fmt::Display for GenericsModel {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        match self {
96            Self::Reified => write!(f, "reified"),
97            Self::Erased => write!(f, "erased"),
98            Self::Monomorphized => write!(f, "monomorphized"),
99        }
100    }
101}
102
103// ─── Naming convention ───────────────────────────────────────────────────────
104
105/// The naming convention used by a target language.
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
107pub enum NamingConvention {
108    /// `camelCase` — JS, TS, Go (unexported).
109    CamelCase,
110    /// `snake_case` — Rust, Python.
111    SnakeCase,
112    /// `PascalCase` — Go (exported), C#.
113    PascalCase,
114}
115
116impl fmt::Display for NamingConvention {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        match self {
119            Self::CamelCase => write!(f, "camelCase"),
120            Self::SnakeCase => write!(f, "snake_case"),
121            Self::PascalCase => write!(f, "PascalCase"),
122        }
123    }
124}
125
126// ─── Error handling convention ───────────────────────────────────────────────
127
128/// How a target language handles errors idiomatically.
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
130pub enum ErrorHandling {
131    /// Exceptions (JS, Python, Java).
132    Exceptions,
133    /// Result types (Rust).
134    ResultType,
135    /// Multiple return values (Go).
136    MultipleReturn,
137}
138
139impl fmt::Display for ErrorHandling {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self {
142            Self::Exceptions => write!(f, "exceptions"),
143            Self::ResultType => write!(f, "result-type"),
144            Self::MultipleReturn => write!(f, "multiple-return"),
145        }
146    }
147}
148
149// ─── Indent style ────────────────────────────────────────────────────────────
150
151/// Indentation style for generated code.
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
153pub enum IndentStyle {
154    /// Spaces with a given width.
155    Spaces(u8),
156    /// Tab characters.
157    Tabs,
158}
159
160// ─── Target capabilities ─────────────────────────────────────────────────────
161
162/// The capability matrix describing what language constructs a target supports.
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct TargetCapabilities {
165    /// Memory management model.
166    pub memory_model: MemoryModel,
167    /// Whether the target has null-safety built into its type system.
168    pub null_safety: bool,
169    /// Support level for algebraic data types (sum types / tagged unions).
170    pub algebraic_types: Support,
171    /// Async programming model.
172    pub async_model: AsyncModel,
173    /// How generics are implemented.
174    pub generics: GenericsModel,
175    /// Whether functions are first-class values.
176    pub first_class_functions: bool,
177    /// Support level for pattern matching.
178    pub pattern_matching: Support,
179    /// Support level for traits/interfaces.
180    pub traits: Support,
181    /// Support level for string interpolation.
182    pub string_interpolation: Support,
183}
184
185// ─── Target conventions ──────────────────────────────────────────────────────
186
187/// Stylistic and idiomatic conventions for a target language.
188#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct TargetConventions {
190    /// Naming convention for functions and variables.
191    pub naming: NamingConvention,
192    /// Naming convention for types.
193    pub type_naming: NamingConvention,
194    /// Idiomatic error handling approach.
195    pub error_handling: ErrorHandling,
196    /// Indentation style.
197    pub indent: IndentStyle,
198    /// File extension for generated files (without dot).
199    pub file_extension: String,
200}
201
202// ─── AI synthesis hints ──────────────────────────────────────────────────────
203
204/// AIR node categories a target profile may flag as needing AI synthesis.
205///
206/// Populated into [`TargetProfile::ai_hints`] per target; consulted by
207/// [`crate::ai_synthesis::needs_ai_synthesis`] via the `CodeGenerator` hook.
208/// Trivial constructs (literals, arithmetic, direct calls, etc.) never
209/// appear in `ai_hints` — Tier 2 rules are sufficient — per §17.2 (Q3).
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
211pub enum NodeKindHint {
212    /// `match` expression with non-trivial arms (ADT emulation, if/else chain synthesis).
213    Match,
214    /// `enum` declaration — ADT emulation for targets without native sum types.
215    EnumDecl,
216    /// Enum variant construction.
217    EnumVariant,
218    /// `handle ... with` block — effect handler translation.
219    HandlingBlock,
220    /// Individual effect operation invocation.
221    EffectOp,
222    /// Move/borrow/mutable-borrow — ownership annotations to erase on GC targets.
223    Ownership,
224    /// String interpolation — formatting macro/concatenation synthesis.
225    Interpolation,
226    /// `impl` block — trait-method dispatch emulation.
227    ImplBlock,
228    /// `trait` declaration — interface emulation on duck-typed targets.
229    TraitDecl,
230}
231
232// ─── Target profile ──────────────────────────────────────────────────────────
233
234/// A complete target profile combining an identifier, capability matrix, and conventions.
235///
236/// Each supported transpilation target (JS, TS, Python, Rust, Go, etc.) is
237/// represented by a `TargetProfile` that informs the code generator about what
238/// constructs are natively available and what synthesis strategies are needed.
239#[derive(Debug, Clone, PartialEq, Eq)]
240pub struct TargetProfile {
241    /// Short identifier: `"js"`, `"ts"`, `"python"`, `"rust"`, `"go"`.
242    pub id: String,
243    /// Human-readable display name.
244    pub display_name: String,
245    /// What the target supports.
246    pub capabilities: TargetCapabilities,
247    /// Idiomatic conventions for the target.
248    pub conventions: TargetConventions,
249    /// AIR node categories that warrant Tier 1 AI synthesis on this target
250    /// (§17.2, Q3 amended). Empty → every node goes through Tier 2 rules.
251    pub ai_hints: Vec<NodeKindHint>,
252}
253
254impl TargetProfile {
255    /// Returns the built-in JavaScript target profile.
256    #[must_use]
257    pub fn javascript() -> Self {
258        Self {
259            id: "js".into(),
260            display_name: "JavaScript".into(),
261            capabilities: TargetCapabilities {
262                memory_model: MemoryModel::GC,
263                null_safety: false,
264                algebraic_types: Support::Emulated,
265                async_model: AsyncModel::EventLoop,
266                generics: GenericsModel::Erased,
267                first_class_functions: true,
268                pattern_matching: Support::SwitchBased,
269                traits: Support::Emulated,
270                string_interpolation: Support::Native,
271            },
272            conventions: TargetConventions {
273                naming: NamingConvention::CamelCase,
274                type_naming: NamingConvention::PascalCase,
275                error_handling: ErrorHandling::Exceptions,
276                indent: IndentStyle::Spaces(2),
277                file_extension: "js".into(),
278            },
279            // JS has no native ADTs, no native match, no ownership, no effects.
280            ai_hints: vec![
281                NodeKindHint::EnumDecl,
282                NodeKindHint::EnumVariant,
283                NodeKindHint::Match,
284                NodeKindHint::HandlingBlock,
285                NodeKindHint::EffectOp,
286                NodeKindHint::Ownership,
287                NodeKindHint::TraitDecl,
288                NodeKindHint::ImplBlock,
289            ],
290        }
291    }
292
293    /// Returns the built-in TypeScript target profile.
294    #[must_use]
295    pub fn typescript() -> Self {
296        Self {
297            id: "ts".into(),
298            display_name: "TypeScript".into(),
299            capabilities: TargetCapabilities {
300                memory_model: MemoryModel::GC,
301                null_safety: true,
302                algebraic_types: Support::Emulated,
303                async_model: AsyncModel::EventLoop,
304                generics: GenericsModel::Erased,
305                first_class_functions: true,
306                pattern_matching: Support::None,
307                traits: Support::InterfaceBased,
308                string_interpolation: Support::Native,
309            },
310            conventions: TargetConventions {
311                naming: NamingConvention::CamelCase,
312                type_naming: NamingConvention::PascalCase,
313                error_handling: ErrorHandling::Exceptions,
314                indent: IndentStyle::Spaces(2),
315                file_extension: "ts".into(),
316            },
317            // TS has tagged union types → less ADT synthesis than JS. Match
318            // still needs switch-based synthesis. Effects/ownership as on JS.
319            ai_hints: vec![
320                NodeKindHint::Match,
321                NodeKindHint::HandlingBlock,
322                NodeKindHint::EffectOp,
323                NodeKindHint::Ownership,
324            ],
325        }
326    }
327
328    /// Returns the built-in Python target profile.
329    #[must_use]
330    pub fn python() -> Self {
331        Self {
332            id: "python".into(),
333            display_name: "Python".into(),
334            capabilities: TargetCapabilities {
335                memory_model: MemoryModel::GC,
336                null_safety: false,
337                algebraic_types: Support::Emulated,
338                async_model: AsyncModel::OSThread,
339                generics: GenericsModel::Erased,
340                first_class_functions: true,
341                pattern_matching: Support::Native,
342                traits: Support::Emulated,
343                string_interpolation: Support::Native,
344            },
345            conventions: TargetConventions {
346                naming: NamingConvention::SnakeCase,
347                type_naming: NamingConvention::PascalCase,
348                error_handling: ErrorHandling::Exceptions,
349                indent: IndentStyle::Spaces(4),
350                file_extension: "py".into(),
351            },
352            // Python 3.10+ has structural pattern matching, so Match is rules-only.
353            // Effects and ownership still need synthesis; flagged traits for
354            // protocol/duck-typing translation.
355            ai_hints: vec![
356                NodeKindHint::HandlingBlock,
357                NodeKindHint::EffectOp,
358                NodeKindHint::Ownership,
359                NodeKindHint::TraitDecl,
360                NodeKindHint::ImplBlock,
361            ],
362        }
363    }
364
365    /// Returns the built-in Rust target profile.
366    #[must_use]
367    pub fn rust() -> Self {
368        Self {
369            id: "rust".into(),
370            display_name: "Rust".into(),
371            capabilities: TargetCapabilities {
372                memory_model: MemoryModel::Manual,
373                null_safety: true,
374                algebraic_types: Support::Native,
375                async_model: AsyncModel::OSThread,
376                generics: GenericsModel::Monomorphized,
377                first_class_functions: true,
378                pattern_matching: Support::Native,
379                traits: Support::Native,
380                string_interpolation: Support::Emulated,
381            },
382            conventions: TargetConventions {
383                naming: NamingConvention::SnakeCase,
384                type_naming: NamingConvention::PascalCase,
385                error_handling: ErrorHandling::ResultType,
386                indent: IndentStyle::Spaces(4),
387                file_extension: "rs".into(),
388            },
389            // Rust is closest to AIR semantics — only effects (still emulated
390            // everywhere per §17.6) and string interpolation (no native f-string
391            // literal form) warrant synthesis.
392            ai_hints: vec![
393                NodeKindHint::HandlingBlock,
394                NodeKindHint::EffectOp,
395                NodeKindHint::Interpolation,
396            ],
397        }
398    }
399
400    /// Returns the built-in Go target profile.
401    #[must_use]
402    pub fn go() -> Self {
403        Self {
404            id: "go".into(),
405            display_name: "Go".into(),
406            capabilities: TargetCapabilities {
407                memory_model: MemoryModel::GC,
408                null_safety: false,
409                algebraic_types: Support::Emulated,
410                async_model: AsyncModel::GreenThread,
411                generics: GenericsModel::Reified,
412                first_class_functions: true,
413                pattern_matching: Support::None,
414                traits: Support::InterfaceBased,
415                string_interpolation: Support::Emulated,
416            },
417            conventions: TargetConventions {
418                naming: NamingConvention::CamelCase,
419                type_naming: NamingConvention::PascalCase,
420                error_handling: ErrorHandling::MultipleReturn,
421                indent: IndentStyle::Tabs,
422                file_extension: "go".into(),
423            },
424            // Go has no match, no sum types, no ownership, emulated interpolation.
425            ai_hints: vec![
426                NodeKindHint::EnumDecl,
427                NodeKindHint::EnumVariant,
428                NodeKindHint::Match,
429                NodeKindHint::HandlingBlock,
430                NodeKindHint::EffectOp,
431                NodeKindHint::Ownership,
432                NodeKindHint::Interpolation,
433                NodeKindHint::TraitDecl,
434                NodeKindHint::ImplBlock,
435            ],
436        }
437    }
438
439    /// Returns all built-in target profiles.
440    #[must_use]
441    pub fn all_builtins() -> Vec<Self> {
442        vec![
443            Self::javascript(),
444            Self::typescript(),
445            Self::python(),
446            Self::rust(),
447            Self::go(),
448        ]
449    }
450
451    /// Looks up a built-in profile by its short id (e.g., `"js"`, `"rust"`).
452    #[must_use]
453    pub fn from_id(id: &str) -> Option<Self> {
454        match id {
455            "js" | "javascript" => Some(Self::javascript()),
456            "ts" | "typescript" => Some(Self::typescript()),
457            "python" | "py" => Some(Self::python()),
458            "rust" | "rs" => Some(Self::rust()),
459            "go" | "golang" => Some(Self::go()),
460            _ => None,
461        }
462    }
463}
464
465impl fmt::Display for TargetProfile {
466    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467        write!(f, "{} ({})", self.display_name, self.id)
468    }
469}
470
471// ─── NodeKind → NodeKindHint mapping ─────────────────────────────────────────
472
473/// Classifies an AIR node into a [`NodeKindHint`] category, or `None` if the
474/// node is trivial (literal, arithmetic, direct call, etc.) and must never
475/// trigger AI synthesis per §17.2 (Q3).
476///
477/// Called by [`crate::ai_synthesis::needs_ai_synthesis`] together with
478/// [`TargetProfile::ai_hints`] to decide invocation per node.
479#[must_use]
480pub fn classify_node(node: &bock_air::AIRNode) -> Option<NodeKindHint> {
481    use bock_air::NodeKind;
482    match &node.kind {
483        NodeKind::Match { .. } => Some(NodeKindHint::Match),
484        NodeKind::EnumDecl { .. } => Some(NodeKindHint::EnumDecl),
485        NodeKind::EnumVariant { .. } => Some(NodeKindHint::EnumVariant),
486        NodeKind::HandlingBlock { .. } => Some(NodeKindHint::HandlingBlock),
487        NodeKind::EffectOp { .. } => Some(NodeKindHint::EffectOp),
488        NodeKind::Move { .. } | NodeKind::Borrow { .. } | NodeKind::MutableBorrow { .. } => {
489            Some(NodeKindHint::Ownership)
490        }
491        NodeKind::Interpolation { .. } => Some(NodeKindHint::Interpolation),
492        NodeKind::ImplBlock { .. } => Some(NodeKindHint::ImplBlock),
493        NodeKind::TraitDecl { .. } => Some(NodeKindHint::TraitDecl),
494        _ => None,
495    }
496}
497
498// ─── Tests ───────────────────────────────────────────────────────────────────
499
500#[cfg(test)]
501mod tests {
502    use super::*;
503
504    #[test]
505    fn all_builtins_has_five_targets() {
506        let profiles = TargetProfile::all_builtins();
507        assert_eq!(profiles.len(), 5);
508
509        let ids: Vec<&str> = profiles.iter().map(|p| p.id.as_str()).collect();
510        assert!(ids.contains(&"js"));
511        assert!(ids.contains(&"ts"));
512        assert!(ids.contains(&"python"));
513        assert!(ids.contains(&"rust"));
514        assert!(ids.contains(&"go"));
515    }
516
517    #[test]
518    fn from_id_resolves_aliases() {
519        assert_eq!(TargetProfile::from_id("js").unwrap().id, "js");
520        assert_eq!(TargetProfile::from_id("javascript").unwrap().id, "js");
521        assert_eq!(TargetProfile::from_id("ts").unwrap().id, "ts");
522        assert_eq!(TargetProfile::from_id("typescript").unwrap().id, "ts");
523        assert_eq!(TargetProfile::from_id("python").unwrap().id, "python");
524        assert_eq!(TargetProfile::from_id("py").unwrap().id, "python");
525        assert_eq!(TargetProfile::from_id("rust").unwrap().id, "rust");
526        assert_eq!(TargetProfile::from_id("rs").unwrap().id, "rust");
527        assert_eq!(TargetProfile::from_id("go").unwrap().id, "go");
528        assert_eq!(TargetProfile::from_id("golang").unwrap().id, "go");
529        assert!(TargetProfile::from_id("unknown").is_none());
530    }
531
532    #[test]
533    fn js_profile_capabilities() {
534        let js = TargetProfile::javascript();
535        assert_eq!(js.capabilities.memory_model, MemoryModel::GC);
536        assert!(!js.capabilities.null_safety);
537        assert_eq!(js.capabilities.algebraic_types, Support::Emulated);
538        assert_eq!(js.capabilities.async_model, AsyncModel::EventLoop);
539        assert_eq!(js.capabilities.generics, GenericsModel::Erased);
540        assert!(js.capabilities.first_class_functions);
541        assert_eq!(js.capabilities.pattern_matching, Support::SwitchBased);
542        assert_eq!(js.capabilities.string_interpolation, Support::Native);
543    }
544
545    #[test]
546    fn rust_profile_capabilities() {
547        let rust = TargetProfile::rust();
548        assert_eq!(rust.capabilities.memory_model, MemoryModel::Manual);
549        assert!(rust.capabilities.null_safety);
550        assert_eq!(rust.capabilities.algebraic_types, Support::Native);
551        assert_eq!(rust.capabilities.generics, GenericsModel::Monomorphized);
552        assert_eq!(rust.capabilities.pattern_matching, Support::Native);
553        assert_eq!(rust.capabilities.traits, Support::Native);
554    }
555
556    #[test]
557    fn go_profile_capabilities() {
558        let go = TargetProfile::go();
559        assert_eq!(go.capabilities.memory_model, MemoryModel::GC);
560        assert_eq!(go.capabilities.async_model, AsyncModel::GreenThread);
561        assert_eq!(go.capabilities.pattern_matching, Support::None);
562        assert_eq!(go.capabilities.traits, Support::InterfaceBased);
563        assert_eq!(go.conventions.error_handling, ErrorHandling::MultipleReturn);
564    }
565
566    #[test]
567    fn python_profile_conventions() {
568        let py = TargetProfile::python();
569        assert_eq!(py.conventions.naming, NamingConvention::SnakeCase);
570        assert_eq!(py.conventions.type_naming, NamingConvention::PascalCase);
571        assert_eq!(py.conventions.indent, IndentStyle::Spaces(4));
572        assert_eq!(py.conventions.file_extension, "py");
573    }
574
575    #[test]
576    fn ts_profile_has_null_safety_and_erased_generics() {
577        let ts = TargetProfile::typescript();
578        assert!(ts.capabilities.null_safety);
579        assert_eq!(ts.capabilities.generics, GenericsModel::Erased);
580        assert_eq!(ts.capabilities.traits, Support::InterfaceBased);
581    }
582
583    #[test]
584    fn display_format() {
585        let js = TargetProfile::javascript();
586        assert_eq!(format!("{js}"), "JavaScript (js)");
587    }
588
589    #[test]
590    fn support_display() {
591        assert_eq!(format!("{}", Support::Native), "native");
592        assert_eq!(format!("{}", Support::SwitchBased), "switch-based");
593        assert_eq!(format!("{}", Support::InterfaceBased), "interface-based");
594        assert_eq!(format!("{}", Support::Emulated), "emulated");
595        assert_eq!(format!("{}", Support::None), "none");
596    }
597
598    // ── Spec table assertion tests (s13-transpilation) ──────────────────
599
600    #[test]
601    fn spec_js_profile() {
602        let p = TargetProfile::javascript();
603        let c = &p.capabilities;
604        assert_eq!(c.memory_model, MemoryModel::GC);
605        assert!(!c.null_safety);
606        assert_eq!(c.algebraic_types, Support::Emulated);
607        assert_eq!(c.async_model, AsyncModel::EventLoop);
608        assert_eq!(c.generics, GenericsModel::Erased);
609        assert!(c.first_class_functions);
610        assert_eq!(c.pattern_matching, Support::SwitchBased);
611        assert_eq!(c.traits, Support::Emulated);
612        assert_eq!(c.string_interpolation, Support::Native);
613        // conventions
614        assert_eq!(p.conventions.naming, NamingConvention::CamelCase);
615        assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
616        assert_eq!(p.conventions.error_handling, ErrorHandling::Exceptions);
617        assert_eq!(p.conventions.indent, IndentStyle::Spaces(2));
618        assert_eq!(p.conventions.file_extension, "js");
619    }
620
621    #[test]
622    fn spec_ts_profile() {
623        let p = TargetProfile::typescript();
624        let c = &p.capabilities;
625        assert_eq!(c.memory_model, MemoryModel::GC);
626        assert!(c.null_safety);
627        assert_eq!(c.algebraic_types, Support::Emulated);
628        assert_eq!(c.async_model, AsyncModel::EventLoop);
629        assert_eq!(c.generics, GenericsModel::Erased);
630        assert!(c.first_class_functions);
631        assert_eq!(c.pattern_matching, Support::None);
632        assert_eq!(c.traits, Support::InterfaceBased);
633        assert_eq!(c.string_interpolation, Support::Native);
634        // conventions
635        assert_eq!(p.conventions.naming, NamingConvention::CamelCase);
636        assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
637        assert_eq!(p.conventions.error_handling, ErrorHandling::Exceptions);
638        assert_eq!(p.conventions.indent, IndentStyle::Spaces(2));
639        assert_eq!(p.conventions.file_extension, "ts");
640    }
641
642    #[test]
643    fn spec_python_profile() {
644        let p = TargetProfile::python();
645        let c = &p.capabilities;
646        assert_eq!(c.memory_model, MemoryModel::GC);
647        assert!(!c.null_safety);
648        assert_eq!(c.algebraic_types, Support::Emulated);
649        assert_eq!(c.async_model, AsyncModel::OSThread);
650        assert_eq!(c.generics, GenericsModel::Erased);
651        assert!(c.first_class_functions);
652        assert_eq!(c.pattern_matching, Support::Native);
653        assert_eq!(c.traits, Support::Emulated);
654        assert_eq!(c.string_interpolation, Support::Native);
655        // conventions
656        assert_eq!(p.conventions.naming, NamingConvention::SnakeCase);
657        assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
658        assert_eq!(p.conventions.error_handling, ErrorHandling::Exceptions);
659        assert_eq!(p.conventions.indent, IndentStyle::Spaces(4));
660        assert_eq!(p.conventions.file_extension, "py");
661    }
662
663    #[test]
664    fn spec_rust_profile() {
665        let p = TargetProfile::rust();
666        let c = &p.capabilities;
667        assert_eq!(c.memory_model, MemoryModel::Manual);
668        assert!(c.null_safety);
669        assert_eq!(c.algebraic_types, Support::Native);
670        assert_eq!(c.async_model, AsyncModel::OSThread);
671        assert_eq!(c.generics, GenericsModel::Monomorphized);
672        assert!(c.first_class_functions);
673        assert_eq!(c.pattern_matching, Support::Native);
674        assert_eq!(c.traits, Support::Native);
675        assert_eq!(c.string_interpolation, Support::Emulated);
676        // conventions
677        assert_eq!(p.conventions.naming, NamingConvention::SnakeCase);
678        assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
679        assert_eq!(p.conventions.error_handling, ErrorHandling::ResultType);
680        assert_eq!(p.conventions.indent, IndentStyle::Spaces(4));
681        assert_eq!(p.conventions.file_extension, "rs");
682    }
683
684    #[test]
685    fn spec_go_profile() {
686        let p = TargetProfile::go();
687        let c = &p.capabilities;
688        assert_eq!(c.memory_model, MemoryModel::GC);
689        assert!(!c.null_safety);
690        assert_eq!(c.algebraic_types, Support::Emulated);
691        assert_eq!(c.async_model, AsyncModel::GreenThread);
692        assert_eq!(c.generics, GenericsModel::Reified);
693        assert!(c.first_class_functions);
694        assert_eq!(c.pattern_matching, Support::None);
695        assert_eq!(c.traits, Support::InterfaceBased);
696        assert_eq!(c.string_interpolation, Support::Emulated);
697        // conventions
698        assert_eq!(p.conventions.naming, NamingConvention::CamelCase);
699        assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
700        assert_eq!(p.conventions.error_handling, ErrorHandling::MultipleReturn);
701        assert_eq!(p.conventions.indent, IndentStyle::Tabs);
702        assert_eq!(p.conventions.file_extension, "go");
703    }
704}