derive_codegen/
generate.rs

1use i_codegen_code::types as st;
2use i_codegen_derive::CodegenInternal;
3use rayon::prelude::*;
4use serde::{self, Deserialize, Serialize};
5use st::TypeRoot;
6
7use std::io::Write;
8use std::path::PathBuf;
9use std::process::Command;
10use std::{
11    collections::{BTreeMap, HashMap, HashSet},
12    fmt::Debug,
13    io::BufReader,
14};
15
16#[derive(CodegenInternal, Serialize, Deserialize, Clone, Debug)]
17#[serde(transparent)]
18#[codegen(tags = "derive-codegen-internal")]
19struct LocationID(String);
20
21#[derive(Serialize, Debug, CodegenInternal)]
22#[codegen(tags = "derive-codegen-internal")]
23struct Input {
24    declarations: Vec<InputDeclaration>,
25    functions: Vec<FunctionDeclaration>,
26}
27
28#[derive(Serialize, Debug, CodegenInternal)]
29#[codegen(tags = "derive-codegen-internal")]
30struct InputDeclaration {
31    id: String,
32    id_location: LocationID,
33    /// Contains generics, docs, and `[codegen]` attr information.
34    #[serde(flatten)]
35    attrs: Attrs,
36    container_kind: ContainerFormat,
37}
38
39#[derive(Serialize, Debug, CodegenInternal)]
40#[codegen(tags = "derive-codegen-internal")]
41struct FunctionDeclaration {
42    id: String,
43    id_location: LocationID,
44    /// Contains generics, docs, and `[codegen]` attr information.
45    #[serde(flatten)]
46    attrs: Attrs,
47    function: FunctionFormat,
48}
49
50#[derive(Serialize, Debug, CodegenInternal)]
51#[codegen(tags = "derive-codegen-internal")]
52struct FunctionFormat {
53    /// Whether this function was declared with async
54    is_async: bool,
55    self_opt: Option<Box<FunctionParameter>>,
56    params: Vec<FunctionParameter>,
57    return_type: Box<Format>,
58}
59
60#[derive(Deserialize, Debug, CodegenInternal)]
61#[codegen(tags = "derive-codegen-internal")]
62struct Output {
63    errors: Vec<OutputMessage>,
64    warnings: Vec<OutputMessage>,
65    files: Vec<OutputFile>,
66}
67
68#[derive(Deserialize, Debug, CodegenInternal)]
69#[codegen(tags = "derive-codegen-internal")]
70struct OutputFile {
71    /// Example: `./some-dir/filename.txt`
72    path: String,
73    /// Example: `"Hello world"`
74    source: String,
75}
76
77#[derive(Serialize, Deserialize, Debug, CodegenInternal)]
78#[codegen(tags = "derive-codegen-internal")]
79struct OutputMessage {
80    message: String,
81    /// Labelled spans
82    labels: Vec<(String, LocationID)>,
83}
84
85/// Serde-based serialization format for anonymous "value" types.
86/// This is just the path respecting serde names into the container
87/// It gets replaced by the knowledge
88#[derive(Serialize, Debug, CodegenInternal)]
89#[codegen(tags = "derive-codegen-internal")]
90enum Format {
91    Incomplete {
92        debug: String,
93    },
94    /// The name of a container.
95    TypeName {
96        ident: String,
97        generics: Vec<Format>,
98    },
99
100    // The formats of primitive types
101    Unit,
102    Bool,
103    I8,
104    I16,
105    I32,
106    I64,
107    I128,
108    ISIZE,
109    U8,
110    U16,
111    U32,
112    U64,
113    U128,
114    USIZE,
115    F32,
116    F64,
117    Char,
118    Str,
119    Bytes,
120
121    /// The format of `Option<T>`.
122    Option(Box<Format>),
123    /// Never actually instantiated
124    Never,
125    /// A sequence, e.g. the format of `Vec<Foo>`.
126    Seq(Box<Format>),
127    /// A map, e.g. the format of `BTreeMap<K, V>`.
128    Map {
129        key: Box<Format>,
130        value: Box<Format>,
131    },
132
133    /// A tuple, e.g. the format of `(Foo, Bar)`.
134    Tuple(Vec<Format>),
135    /// Alias for `(Foo, ... Foo)`.
136    /// E.g. the format of `[Foo; N]`.
137    TupleArray {
138        content: Box<Format>,
139        size: usize,
140    },
141}
142
143/// Serde-based serialization format for named "container" types.
144/// In Rust, those are enums and structs.
145#[derive(Serialize, Debug, CodegenInternal)]
146#[codegen(tags = "derive-codegen-internal")]
147enum ContainerFormat {
148    /// An empty struct, e.g. `struct A`.
149    UnitStruct,
150    /// A struct with a single unnamed parameter, e.g. `struct A(u16)`
151    NewTypeStruct(Box<Format>),
152    /// A struct with several unnamed parameters, e.g. `struct A(u16, u32)`
153    TupleStruct(Vec<Format>),
154    /// A struct with named parameters, e.g. `struct A { a: Foo }`.
155    Struct { fields: Vec<NamedField> },
156    /// An enum, that is, an enumeration of variants.
157    /// Each variant has a unique name and index within the enum.
158    Enum {
159        repr: EnumRepresentation,
160        variants: Vec<NamedVariant>,
161    },
162}
163
164#[derive(Serialize, Debug, CodegenInternal)]
165#[codegen(tags = "derive-codegen-internal")]
166struct NamedVariant {
167    id: String,
168    id_location: LocationID,
169    #[serde(flatten)]
170    attrs: Attrs,
171    variant_format: VariantFormat,
172}
173
174#[derive(Serialize, Debug, CodegenInternal)]
175#[codegen(tags = "derive-codegen-internal")]
176struct NamedField {
177    id: String,
178    id_location: LocationID,
179    #[serde(flatten)]
180    attrs: Attrs,
181    format: Format,
182}
183
184#[derive(Serialize, Debug, CodegenInternal)]
185#[codegen(tags = "derive-codegen-internal")]
186struct FunctionParameter {
187    id: String,
188    id_location: LocationID,
189    #[serde(flatten)]
190    attrs: Attrs,
191    format: Format,
192}
193
194#[derive(Serialize, Debug, CodegenInternal)]
195#[codegen(tags = "derive-codegen-internal")]
196/// Description of a variant in an enum.
197enum VariantFormat {
198    /// A variant without parameters, e.g. `A` in `enum X { A }`
199    Unit,
200    /// A variant with a single unnamed parameter, e.g. `A` in `enum X { A(u16) }`
201    NewType(Box<Format>),
202    /// A struct with several unnamed parameters, e.g. `A` in `enum X { A(u16, u32) }`
203    Tuple(Vec<Format>),
204    /// A struct with named parameters, e.g. `A` in `enum X { A { a: Foo } }`
205    Struct { fields: Vec<NamedField> },
206}
207
208#[derive(Serialize, Debug, CodegenInternal)]
209#[codegen(tags = "derive-codegen-internal")]
210struct Attrs {
211    /// Documentation comments like this one.
212    /// Future idea: Pass in tokens with links to other types.
213    rust_docs: Option<String>,
214    /// Only specified for enums and structs
215    /// Future: Consider whether we should monomorphize on the codegen side...
216    #[serde(skip_serializing_if = "Vec::is_empty", default)]
217    rust_generics: Vec<(String, LocationID)>,
218    /// e.g. `#[serde(rename = "newName")]`, your generator will need to describe what it supports
219    /// Not applicable to derived functions.
220    #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
221    serde_attrs: BTreeMap<String, (String, LocationID)>,
222    /// e.g. `#[serde(transparent)]`, your generator will need to describe what it supports
223    /// Not applicable to derived functions.
224    #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
225    serde_flags: BTreeMap<String, LocationID>,
226    /// e.g. `#[codegen(ts_as = "Date")]` - these are customizable for your generator's use cases.
227    #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
228    codegen_attrs: BTreeMap<String, (String, LocationID)>,
229    /// e.g. `#[codegen(hidden)]` - these are customizable for your generator's use cases.
230    #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
231    codegen_flags: BTreeMap<String, LocationID>,
232}
233
234#[derive(Serialize, Debug, CodegenInternal)]
235#[codegen(tags = "derive-codegen-internal")]
236enum EnumRepresentation {
237    /// The default
238    /// e.g `{ User: { id: 1200, name: "Smithy" } }`
239    External,
240    /// e.g `{ id: 1200, name: "Smithy" }`
241    Untagged,
242    /// e.g `{ type: "User", id: 1200, name: "Smithy" }`
243    /// e.g `{ type: "User", content: { id: 1200, name: "Smithy" } }`
244    Tagged {
245        tag: String,
246        tag_location: LocationID,
247        content: Option<String>,
248        content_location: Option<LocationID>,
249    },
250}
251
252#[derive(Clone)]
253struct SourceLineNumberIndex {
254    newlines: Vec<usize>,
255    // TODO: is this crlf useful for something?
256    #[allow(unused)]
257    is_crlf: bool,
258}
259
260impl SourceLineNumberIndex {
261    fn new(file: impl std::io::Read) -> Self {
262        use std::io::Read;
263        let mut newlines = Vec::new();
264        let mut is_crlf = false;
265
266        let mut current_byte = 0;
267        for byte_result in BufReader::new(file).bytes() {
268            match byte_result.expect("read next byte") {
269                b'\n' => {
270                    newlines.push(current_byte + 1);
271                }
272                b'\r' => {
273                    is_crlf = true;
274                }
275                _ => {}
276            }
277            current_byte += 1;
278        }
279
280        Self { is_crlf, newlines }
281    }
282
283    fn get_ln_col(&self, byte: usize) -> (usize, usize) {
284        let index = match self.newlines.binary_search(&byte) {
285            Ok(exact_at) => exact_at,
286            Err(insert_at) => insert_at - 1,
287        };
288        if index >= self.newlines.len() || index == 0 {
289            (index, 0)
290        } else {
291            let newline_byte_offset = *self.newlines.get(index).expect("in bounds");
292            if byte < newline_byte_offset {
293                panic!("expected newline returned to be before byte (byte: {byte} < newline_at: {newline_byte_offset}) found at index: {index}, all new lines: {:?}", self.newlines)
294            }
295            (index + 1, byte - newline_byte_offset)
296        }
297    }
298}
299
300#[derive(Clone)]
301struct TypeRootConverter {
302    file_name: String,
303    line_number_override: Option<u32>,
304    lines: SourceLineNumberIndex,
305}
306
307impl TypeRootConverter {
308    fn get_ln_col(&self, byte: usize) -> (usize, usize) {
309        self.lines.get_ln_col(byte)
310    }
311
312    fn location_id<T>(
313        &self,
314        st::Spanned {
315            bytes: (start, end),
316            value,
317        }: st::Spanned<T>,
318    ) -> (T, LocationID) {
319        if let Some(line) = self.line_number_override {
320            (
321                value,
322                LocationID(format!(
323                    "L({}:{} #B{}-B{})",
324                    &self.file_name, line, start, end
325                )),
326            )
327        } else {
328            let (ln, col) = self.get_ln_col(start);
329            (
330                value,
331                LocationID(format!(
332                    "L({}:{}:{} #B{}-B{})",
333                    &self.file_name, ln, col, start, end
334                )),
335            )
336        }
337    }
338    fn unname<T>(
339        &self,
340        st::Named {
341            codegen_attrs,
342            codegen_flags,
343            rust_docs,
344            rust_ident,
345            rust_generics,
346            serde_attrs,
347            serde_flags,
348            value,
349        }: st::Named<T>,
350    ) -> (st::Spanned<String>, T, Attrs) {
351        (
352            rust_ident,
353            value,
354            Attrs {
355                rust_docs,
356                rust_generics: rust_generics
357                    .into_iter()
358                    .map(|gen| self.location_id(gen))
359                    .collect(),
360                serde_attrs: {
361                    let mut bt = BTreeMap::<String, (String, LocationID)>::new();
362                    for st::Spanned {
363                        bytes: _,
364                        value: (key, value),
365                    } in serde_attrs
366                    {
367                        bt.insert(key.value, self.location_id(value));
368                    }
369                    bt
370                },
371                serde_flags: {
372                    let mut bt = BTreeMap::<String, LocationID>::new();
373                    for flag_span in serde_flags {
374                        let (a, b) = self.location_id(flag_span);
375                        bt.insert(a, b);
376                    }
377                    bt
378                },
379                codegen_attrs: {
380                    let mut bt = BTreeMap::<String, (String, LocationID)>::new();
381                    for st::Spanned {
382                        bytes: _,
383                        value: (key, value),
384                    } in codegen_attrs
385                    {
386                        bt.insert(key.value, self.location_id(value));
387                    }
388                    bt
389                },
390                codegen_flags: {
391                    let mut bt = BTreeMap::<String, LocationID>::new();
392                    for flag_span in codegen_flags {
393                        let (a, b) = self.location_id(flag_span);
394                        bt.insert(a, b);
395                    }
396                    bt
397                },
398            },
399        )
400    }
401    fn format_to_format(&self, format: st::Format) -> Format {
402        match format {
403            st::Format::Incomplete { debug } => Format::Incomplete { debug },
404            st::Format::TypeName { ident, generics } => Format::TypeName {
405                ident,
406                generics: generics
407                    .into_iter()
408                    .map(|format| self.format_to_format(format))
409                    .collect(),
410            },
411            st::Format::Unit => Format::Unit,
412            st::Format::Bool => Format::Bool,
413            st::Format::I8 => Format::I8,
414            st::Format::I16 => Format::I16,
415            st::Format::I32 => Format::I32,
416            st::Format::I64 => Format::I64,
417            st::Format::I128 => Format::I128,
418            st::Format::ISIZE => Format::ISIZE,
419            st::Format::U8 => Format::U8,
420            st::Format::U16 => Format::U16,
421            st::Format::U32 => Format::U32,
422            st::Format::U64 => Format::U64,
423            st::Format::U128 => Format::U128,
424            st::Format::USIZE => Format::USIZE,
425            st::Format::F32 => Format::F32,
426            st::Format::F64 => Format::F64,
427            st::Format::Char => Format::Char,
428            st::Format::Str => Format::Str,
429            st::Format::Bytes => Format::Bytes,
430            st::Format::Option(option_format) => {
431                Format::Option(Box::new(self.format_to_format(*option_format)))
432            }
433            st::Format::Never => Format::Never,
434            st::Format::Seq(seq_format) => {
435                Format::Seq(Box::new(self.format_to_format(*seq_format)))
436            }
437            st::Format::Map { key, value } => Format::Map {
438                key: Box::new(self.format_to_format(*key)),
439                value: Box::new(self.format_to_format(*value)),
440            },
441            st::Format::Tuple(tuple_formats) => Format::Tuple(
442                tuple_formats
443                    .into_iter()
444                    .map(|format| self.format_to_format(format))
445                    .collect(),
446            ),
447            st::Format::TupleArray { content, size } => Format::TupleArray {
448                content: Box::new(self.format_to_format(*content)),
449                size,
450            },
451        }
452    }
453
454    fn function_format_to_function_format(
455        &self,
456        function_format: st::FunctionFormat,
457    ) -> FunctionFormat {
458        let st::FunctionFormat {
459            params: args,
460            is_async,
461            ret,
462            self_opt,
463        } = function_format;
464        FunctionFormat {
465            params: args
466                .into_iter()
467                .map(|st_named_format| self.named_format_to_function_parameter(st_named_format))
468                .collect(),
469            is_async,
470            self_opt: self_opt
471                .map(|selff| Box::new(self.named_format_to_function_parameter(selff))),
472            return_type: Box::new(self.format_to_format(*ret)),
473        }
474    }
475
476    fn named_format_to_named_field(&self, named: st::Named<st::Format>) -> NamedField {
477        let (id_span, format, attrs) = self.unname(named);
478        let (id, id_location) = self.location_id(id_span);
479        NamedField {
480            attrs,
481            format: self.format_to_format(format),
482            id,
483            id_location,
484        }
485    }
486    fn named_format_to_function_parameter(
487        &self,
488        named: st::Named<st::Format>,
489    ) -> FunctionParameter {
490        let (id_span, format, attrs) = self.unname(named);
491        let (id, id_location) = self.location_id(id_span);
492        FunctionParameter {
493            attrs,
494            format: self.format_to_format(format),
495            id,
496            id_location,
497        }
498    }
499    fn container_format_to_container_format(
500        &self,
501        // needed to see serde attrs for ensuring correct interpretation
502        attrs: &Attrs,
503        container_format: st::ContainerFormat,
504    ) -> ContainerFormat {
505        match container_format {
506            st::ContainerFormat::UnitStruct => ContainerFormat::UnitStruct,
507            st::ContainerFormat::NewTypeStruct(format) => {
508                ContainerFormat::NewTypeStruct(Box::new(self.format_to_format(*format)))
509            }
510            st::ContainerFormat::TupleStruct(formats) => ContainerFormat::TupleStruct(
511                formats
512                    .into_iter()
513                    .map(|format| self.format_to_format(format))
514                    .collect(),
515            ),
516            st::ContainerFormat::Struct(fields) => ContainerFormat::Struct {
517                fields: {
518                    fields
519                        .into_par_iter()
520                        .map(|field| self.named_format_to_named_field(field))
521                        .collect()
522                },
523            },
524            st::ContainerFormat::Enum(variants) => ContainerFormat::Enum {
525                repr: {
526                    if attrs.serde_flags.contains_key("untagged") {
527                        EnumRepresentation::Untagged
528                    } else {
529                        match (
530                            attrs.serde_attrs.get("tag").cloned(),
531                            attrs.serde_attrs.get("content").cloned(),
532                        ) {
533                            (Some((tag, tag_location)), Some((content, content_location))) => {
534                                EnumRepresentation::Tagged {
535                                    tag,
536                                    tag_location,
537                                    content: Some(content),
538                                    content_location: Some(content_location),
539                                }
540                            }
541                            (Some((tag, tag_location)), None) => EnumRepresentation::Tagged {
542                                tag,
543                                tag_location,
544                                content: None,
545                                content_location: None,
546                            },
547                            (None, None) => EnumRepresentation::External,
548                            (None, Some(_)) => {
549                                // hmm...
550                                EnumRepresentation::External
551                            }
552                        }
553                    }
554                },
555                variants: {
556                    variants
557                        .into_par_iter()
558                        .map(|(_index, named_variant_format)| {
559                            let (id_span, variant_format, attrs) =
560                                self.unname(named_variant_format);
561                            let (id, id_location) = self.location_id(id_span);
562                            let variant_format = match variant_format {
563                                st::VariantFormat::Unit => VariantFormat::Unit,
564                                st::VariantFormat::NewType(format) => {
565                                    VariantFormat::NewType(Box::new(self.format_to_format(*format)))
566                                }
567                                st::VariantFormat::Tuple(formats) => VariantFormat::Tuple(
568                                    formats
569                                        .into_iter()
570                                        .map(|format| self.format_to_format(format))
571                                        .collect(),
572                                ),
573                                st::VariantFormat::Struct(fields) => VariantFormat::Struct {
574                                    fields: fields
575                                        .into_iter()
576                                        .map(|field| {
577                                            let (id_span, format, attrs) = self.unname(field);
578                                            let (id, id_location) = self.location_id(id_span);
579                                            NamedField {
580                                                attrs,
581                                                format: self.format_to_format(format),
582                                                id,
583                                                id_location,
584                                            }
585                                        })
586                                        .collect(),
587                                },
588                            };
589                            NamedVariant {
590                                id,
591                                id_location,
592                                attrs,
593                                variant_format,
594                            }
595                        })
596                        .collect()
597                },
598            },
599        }
600    }
601}
602
603enum GenCommand<'a> {
604    PipeInto(&'a mut std::process::Command),
605    Arg(&'a mut std::process::Command),
606}
607
608#[derive(Clone)]
609pub struct Generation {
610    tags: Vec<String>,
611}
612
613pub struct GenerationCmd<'a> {
614    relative_to: Option<PathBuf>,
615    selection: &'a Generation,
616    command: GenCommand<'a>,
617    output_path: Option<PathBuf>,
618}
619
620impl Generation {
621    pub fn for_tag(tag: &str) -> Self {
622        Generation {
623            tags: vec![tag.to_string()],
624        }
625    }
626
627    pub fn include_tag(&mut self, tag: impl Into<String>) -> &mut Self {
628        self.tags.push(tag.into());
629        self
630    }
631
632    pub fn pipe_into<'a>(&'a self, command: &'a mut Command) -> GenerationCmd<'a> {
633        GenerationCmd {
634            relative_to: command.get_current_dir().map(|dir| dir.to_owned()),
635            command: GenCommand::PipeInto(command),
636            output_path: None,
637            selection: self,
638        }
639    }
640
641    pub fn as_arg_of<'a>(&'a self, command: &'a mut Command) -> GenerationCmd<'a> {
642        GenerationCmd {
643            relative_to: command.get_current_dir().map(|dir| dir.to_owned()),
644            command: GenCommand::Arg(command),
645            output_path: None,
646            selection: self,
647        }
648    }
649
650    pub fn to_input_json_pretty(&self) -> String {
651        serde_json::to_string_pretty(&create_input_from_selection(self)).unwrap()
652    }
653
654    pub fn to_input_json(&self) -> String {
655        serde_json::to_string(&create_input_from_selection(self)).unwrap()
656    }
657}
658
659#[derive(Debug)]
660pub struct GenerationSummary {
661    /// Relative to this folder
662    pub relative_to: PathBuf,
663    /// Paths and their sizes
664    pub output_files: Vec<(String, usize)>,
665}
666
667impl GenerationSummary {
668    pub fn print(self) -> Self {
669        eprintln!("{self:?}");
670        self
671    }
672}
673
674impl<'a> GenerationCmd<'a> {
675    /// Relative to current directory of teh command passed in
676    pub fn with_output_path<P: Into<PathBuf>>(&mut self, path: P) -> &mut Self {
677        self.output_path = Some(path.into());
678        self
679    }
680
681    fn get_output_path(&self) -> PathBuf {
682        if let Some(ref rel) = self.relative_to {
683            rel.join(self.output_path.clone().unwrap_or_else(|| ".".into()))
684        } else {
685            self.output_path.clone().unwrap_or_else(|| ".".into())
686        }
687    }
688
689    #[track_caller]
690    pub fn print(&mut self) {
691        let output = self.generate();
692        for err in &output.warnings {
693            eprintln!("Output warning:\n{err:?}")
694        }
695        for err in &output.errors {
696            eprintln!("Output error:\n{err:?}")
697        }
698
699        let relative_to = self.get_output_path();
700        for output_file in output.files {
701            let write_path = relative_to.join(output_file.path);
702            println!("\x1b[48:2:255:165:0m{}\x1b[0m", write_path.display());
703            println!("{}", output_file.source);
704        }
705    }
706
707    #[track_caller]
708    pub fn write(&mut self) -> GenerationSummary {
709        let output = self.generate();
710        for err in output.warnings.iter() {
711            eprintln!("Output warning:\n{err:?}")
712        }
713        for err in output.errors.iter() {
714            eprintln!("Output error:\n{err:?}")
715        }
716
717        let relative_to = self.get_output_path();
718        let mut summary = GenerationSummary {
719            relative_to: relative_to.clone(),
720            output_files: Vec::new(),
721        };
722        for output_file in output.files.iter() {
723            let write_path = relative_to.join(&output_file.path);
724            if let Some(parent) = write_path.parent() {
725                std::fs::create_dir_all(parent).expect("creating missing directories");
726            }
727
728            let mut file = std::fs::File::create(write_path).expect("creating file");
729            write!(&mut file, "{}", output_file.source).expect("writing generated file");
730            summary
731                .output_files
732                .push((output_file.path.to_string(), output_file.source.len()));
733        }
734
735        summary
736    }
737
738    #[track_caller]
739    fn generate(&mut self) -> Output {
740        let inputs = create_input_from_selection(self.selection);
741        let stdout_output = match self.command {
742            GenCommand::PipeInto(ref mut cmd) => {
743                let cmd_str = format!("{cmd:?}");
744                let mut child = cmd
745                    .stdout(std::process::Stdio::piped())
746                    .stdin(std::process::Stdio::piped())
747                    .spawn()
748                    .map_err(|err| format!("Failure executing `{cmd_str}`: {err:?} "))
749                    .expect("spawning process");
750
751                child
752                    .stdin
753                    .as_mut()
754                    .unwrap()
755                    .write_all(&serde_json::to_vec(&inputs).unwrap())
756                    .unwrap();
757
758                child.wait_with_output().expect("failed to wait on child")
759            }
760            GenCommand::Arg(ref mut cmd) => {
761                let cmd_str = format!("{cmd:?} <input-json>");
762                let child = cmd
763                    .arg(&serde_json::to_string(&inputs).unwrap())
764                    .stdout(std::process::Stdio::piped())
765                    .spawn()
766                    .map_err(|err| format!("Failure executing `{cmd_str}`: {err:?} "))
767                    .expect("spawning process");
768
769                child.wait_with_output().expect("failed to wait on child")
770            }
771        };
772
773        let stdout_str = String::from_utf8_lossy(&stdout_output.stdout);
774
775        if !stdout_output.status.success() {
776            eprintln!("Non-success status from generation");
777            if !stdout_str.trim().is_empty() {
778                eprintln!("Stdout:\n{stdout_str}");
779            }
780            std::process::exit(1);
781        }
782
783        serde_json::from_str::<Output>(&stdout_str)
784            .map_err(|err| {
785                format!(
786                    "Failed to parsed output as JSON of output files: {err:?}, from:\n{stdout_str}"
787                )
788            })
789            .expect("parsing output")
790    }
791}
792
793fn create_input_from_selection(selection: &Generation) -> Input {
794    let tys = i_codegen_code::get_types_by_tags(&selection.tags);
795    let current_directory = std::env::current_dir()
796        .expect("getting current directory in order to find source files for line number mapping");
797    let type_root_converters: HashMap<String, TypeRootConverter> = tys
798        .iter()
799        .map(|root| root.file.clone())
800        .collect::<HashSet<_>>()
801        .into_par_iter()
802        .map(|file_name| {
803            let file = {
804                match std::fs::File::open(&file_name) {
805                    Ok(found) => found,
806                    Err(_) => {
807                        // try going up on directory... hacky...
808                        match std::fs::File::open(
809                            &current_directory.parent().unwrap().join(&file_name),
810                        ) {
811                            Ok(file) => file,
812                            Err(_err) => {
813                                // eprintln!("opening file {file_name:?} (relative to: {current_directory:?}): {_err:?}");
814                                return (
815                                    file_name.clone(),
816                                    TypeRootConverter {
817                                        file_name,
818                                        line_number_override: None,
819                                        lines: SourceLineNumberIndex {
820                                            newlines: vec![0usize],
821                                            is_crlf: false,
822                                        },
823                                    },
824                                );
825                            }
826                        }
827                    }
828                }
829            };
830
831            (
832                file_name.clone(),
833                TypeRootConverter {
834                    file_name,
835                    line_number_override: None,
836                    lines: SourceLineNumberIndex::new(file),
837                },
838            )
839        })
840        .collect();
841
842    let mut functions = Vec::new();
843    let mut declarations = Vec::<InputDeclaration>::new();
844    for TypeRoot {
845        extras,
846        file,
847        line,
848        inner,
849    } in tys
850    {
851        let mut converter = type_root_converters.get(&file).unwrap().clone();
852        converter.line_number_override = Some(line);
853        let (root_id_span, root_item, attrs) = converter.unname(inner);
854        let (id, id_location) = converter.location_id(root_id_span);
855        match root_item {
856            st::RootItem::Container(container_format) => {
857                declarations.push(InputDeclaration {
858                    id,
859                    id_location,
860                    container_kind: converter
861                        .container_format_to_container_format(&attrs, container_format),
862                    attrs,
863                });
864            }
865            st::RootItem::Function(function_format) => {
866                functions.push(FunctionDeclaration {
867                    id,
868                    id_location,
869                    function: converter.function_format_to_function_format(function_format),
870                    attrs,
871                });
872            }
873        }
874        // extra declarations like built-ins
875        for extra in extras {
876            let (id_span, container_format, attrs) = converter.unname(extra);
877            let (id, id_location) = converter.location_id(id_span);
878            declarations.push(InputDeclaration {
879                id,
880                id_location,
881                container_kind: converter
882                    .container_format_to_container_format(&attrs, container_format),
883                attrs,
884            });
885        }
886    }
887
888    Input {
889        declarations,
890        functions,
891    }
892}