Skip to main content

wirespec_backend_c/
lib.rs

1// crates/wirespec-backend-c/src/lib.rs
2//
3// C backend for wirespec: lowers Codec IR into .h header and .c source files.
4
5pub mod checksum_binding;
6pub mod expr;
7pub mod header;
8pub mod names;
9pub mod parse_emit;
10pub mod serialize_emit;
11pub mod source;
12pub mod type_map;
13
14use wirespec_backend_api::*;
15use wirespec_codec::CodecModule;
16
17pub const TARGET_C: wirespec_backend_api::TargetId = wirespec_backend_api::TargetId("c");
18
19pub struct CBackend;
20
21impl Backend for CBackend {
22    type LoweredModule = CLoweredModule;
23
24    fn id(&self) -> TargetId {
25        TARGET_C
26    }
27
28    fn lower(
29        &self,
30        module: &CodecModule,
31        ctx: &BackendContext,
32    ) -> Result<Self::LoweredModule, BackendError> {
33        let opts = ctx
34            .target_options
35            .downcast_ref::<CBackendOptions>()
36            .ok_or_else(|| BackendError::UnsupportedOption {
37                target: TARGET_C,
38                option: "target_options".into(),
39                reason: "expected CBackendOptions".into(),
40            })?;
41        let emit_fuzz = opts.emit_fuzz_harness;
42
43        let header_content = header::emit_header(module, &ctx.module_prefix);
44        let source_content = source::emit_source(module, &ctx.module_prefix);
45        let fuzz_content = if emit_fuzz {
46            source::emit_fuzz_source(module, &ctx.module_prefix)
47        } else {
48            None
49        };
50
51        Ok(CLoweredModule {
52            header_content,
53            source_content,
54            fuzz_content,
55            prefix: ctx.module_prefix.clone(),
56            emit_fuzz,
57        })
58    }
59
60    fn emit(
61        &self,
62        lowered: &Self::LoweredModule,
63        sink: &mut dyn ArtifactSink,
64    ) -> Result<BackendOutput, BackendError> {
65        let mut artifacts = Vec::new();
66
67        // Header
68        sink.write(Artifact {
69            target: TARGET_C,
70            kind: ArtifactKind::C_HEADER,
71            module_name: lowered.prefix.clone(),
72            module_prefix: lowered.prefix.clone(),
73            relative_path: format!("{}.h", lowered.prefix).into(),
74            contents: lowered.header_content.as_bytes().to_vec(),
75        })?;
76        artifacts.push(ArtifactMeta {
77            kind: ArtifactKind::C_HEADER,
78            relative_path: format!("{}.h", lowered.prefix).into(),
79            byte_len: lowered.header_content.len(),
80        });
81
82        // Source
83        sink.write(Artifact {
84            target: TARGET_C,
85            kind: ArtifactKind::C_SOURCE,
86            module_name: lowered.prefix.clone(),
87            module_prefix: lowered.prefix.clone(),
88            relative_path: format!("{}.c", lowered.prefix).into(),
89            contents: lowered.source_content.as_bytes().to_vec(),
90        })?;
91        artifacts.push(ArtifactMeta {
92            kind: ArtifactKind::C_SOURCE,
93            relative_path: format!("{}.c", lowered.prefix).into(),
94            byte_len: lowered.source_content.len(),
95        });
96
97        // Fuzz harness
98        if let Some(ref fuzz) = lowered.fuzz_content {
99            sink.write(Artifact {
100                target: TARGET_C,
101                kind: ArtifactKind::C_FUZZ_SOURCE,
102                module_name: lowered.prefix.clone(),
103                module_prefix: lowered.prefix.clone(),
104                relative_path: format!("{}_fuzz.c", lowered.prefix).into(),
105                contents: fuzz.as_bytes().to_vec(),
106            })?;
107            artifacts.push(ArtifactMeta {
108                kind: ArtifactKind::C_FUZZ_SOURCE,
109                relative_path: format!("{}_fuzz.c", lowered.prefix).into(),
110                byte_len: fuzz.len(),
111            });
112        }
113
114        Ok(BackendOutput {
115            target: TARGET_C,
116            artifacts,
117        })
118    }
119}
120
121pub struct CLoweredModule {
122    pub header_content: String,
123    pub source_content: String,
124    pub fuzz_content: Option<String>,
125    pub prefix: String,
126    pub emit_fuzz: bool,
127}
128
129impl BackendDyn for CBackend {
130    fn id(&self) -> TargetId {
131        TARGET_C
132    }
133
134    fn lower_and_emit(
135        &self,
136        module: &CodecModule,
137        ctx: &BackendContext,
138        sink: &mut dyn ArtifactSink,
139    ) -> Result<BackendOutput, BackendError> {
140        let lowered = Backend::lower(self, module, ctx)?;
141        Backend::emit(self, &lowered, sink)
142    }
143}