futhark_bindgen/generate/
mod.rs

1use crate::*;
2
3mod ocaml;
4mod rust;
5
6pub use ocaml::OCaml;
7pub use rust::Rust;
8
9pub(crate) fn first_uppercase(s: &str) -> String {
10    let mut s = s.to_string();
11    if let Some(r) = s.get_mut(0..1) {
12        r.make_ascii_uppercase();
13    }
14    s
15}
16
17pub(crate) fn convert_struct_name(s: &str) -> &str {
18    s.strip_prefix("struct")
19        .unwrap()
20        .strip_suffix('*')
21        .unwrap()
22        .strip_prefix(|x: char| x.is_ascii_whitespace())
23        .unwrap()
24        .strip_suffix(|x: char| x.is_ascii_whitespace())
25        .unwrap()
26}
27
28/// Code generation config
29pub struct Config {
30    /// Output file
31    pub output_path: std::path::PathBuf,
32
33    /// Path to output file
34    pub output_file: std::fs::File,
35}
36
37impl Config {
38    /// Create a new config using the provided output file path
39    pub fn new(output: impl AsRef<std::path::Path>) -> Result<Config, Error> {
40        Ok(Config {
41            output_path: output.as_ref().to_path_buf(),
42            output_file: std::fs::File::create(output)?,
43        })
44    }
45}
46
47pub trait Generate {
48    /// Iterates through the manifest and generates code
49    fn generate(&mut self, pkg: &Package, config: &mut Config) -> Result<(), Error> {
50        self.bindings(pkg, config)?;
51        for (name, ty) in &pkg.manifest.types {
52            match ty {
53                manifest::Type::Array(ty) => {
54                    self.array_type(pkg, config, name, ty)?;
55                }
56                manifest::Type::Opaque(ty) => {
57                    self.opaque_type(pkg, config, name, ty)?;
58                }
59            }
60        }
61
62        for (name, entry) in &pkg.manifest.entry_points {
63            self.entry(pkg, config, name, entry)?;
64        }
65        self.format(&config.output_path)?;
66        Ok(())
67    }
68
69    /// Step 1: generate any setup code or low-level bindings
70    fn bindings(&mut self, _pkg: &Package, _config: &mut Config) -> Result<(), Error>;
71
72    /// Step 2: generate code for array types
73    fn array_type(
74        &mut self,
75        pkg: &Package,
76        config: &mut Config,
77        name: &str,
78        ty: &manifest::ArrayType,
79    ) -> Result<(), Error>;
80
81    /// Step 3: generate code for opaque types
82    fn opaque_type(
83        &mut self,
84        pkg: &Package,
85        config: &mut Config,
86        name: &str,
87        ty: &manifest::OpaqueType,
88    ) -> Result<(), Error>;
89
90    /// Step 4: generate code for entry points
91    fn entry(
92        &mut self,
93        pkg: &Package,
94        config: &mut Config,
95        name: &str,
96        entry: &manifest::Entry,
97    ) -> Result<(), Error>;
98
99    /// Step 5: Optionally, run any formatting program or post-processing on the output file
100    fn format(&mut self, _output: &std::path::Path) -> Result<(), Error> {
101        Ok(())
102    }
103}
104
105fn rust() -> Box<impl Generate> {
106    Box::<Rust>::default()
107}
108
109fn ocaml(config: &Config) -> Box<impl Generate> {
110    Box::new(OCaml::new(config).unwrap())
111}
112
113impl Config {
114    /// Automatically detect output language
115    pub fn detect(&self) -> Option<Box<dyn Generate>> {
116        match self
117            .output_path
118            .extension()
119            .map(|x| x.to_str().expect("Invalid extension"))
120        {
121            Some("rs") => Some(rust()),
122            Some("ml") => Some(ocaml(self)),
123            _ => None,
124        }
125    }
126}