build_const/
writer.rs

1use std::env;
2use std::fs;
3use std::fmt::{Debug, Display};
4use std::io;
5use std::io::Write;
6use std::path::Path;
7use std::str;
8
9
10/// Primary object used to write constant files.
11/// 
12/// # Example
13/// ```no_run
14/// # use std::path::Path;
15/// # #[derive(Debug)]
16/// # struct Point { x: u8, y: u8 }
17/// use build_const::ConstWriter;
18/// 
19/// // use `for_build` in `build.rs`
20/// let mut consts = ConstWriter::from_path(
21///     &Path::new("/tmp/constants.rs")
22/// ).unwrap();
23/// 
24/// // add an external dependency (`use xyz::Point`)
25/// consts.add_dependency("xyz::Point");
26/// 
27/// // finish dependencies and starting writing constants
28/// let mut consts = consts.finish_dependencies();
29///
30/// // add an array of values
31/// let values: Vec<u8> = vec![1, 2, 3, 36];
32/// consts.add_array("ARRAY", "u8", &values);
33///
34/// // Add a value that is a result of "complex" calculations
35/// consts.add_value("VALUE", "u8", values.iter().sum::<u8>());
36/// 
37/// // Add a value from an external crate (must implement `Debug`)
38/// consts.add_value("VALUE", "Point", &Point { x: 3, y: 7});
39/// ```
40pub struct ConstWriter {
41    f: fs::File,
42}
43
44/// Created from `ConstWriter::finish_dependencies`. See
45/// documentation for `ConstWriter`.
46pub struct ConstValueWriter {
47    f: fs::File,
48}
49
50impl ConstWriter {
51    /// Create a ConstWriter to be used for your crate's `build.rs`
52    pub fn for_build(mod_name: &str) -> io::Result<ConstWriter> {
53        let out_dir = env::var("OUT_DIR").unwrap();
54        let mod_name = format!("{}.rs", mod_name);
55        let dest_path = Path::new(&out_dir).join(mod_name);
56
57        Ok(ConstWriter {
58            f: fs::File::create(&dest_path)?
59        })
60    }
61
62    /// Create a new ConstWriter to write to an path. If a file
63    /// already exists at the path then it will be deleted.
64    pub fn from_path(path: &Path) -> io::Result<ConstWriter> {
65        let f = fs::OpenOptions::new()
66            .write(true)
67            .truncate(true)
68            .open(path)?;
69        Ok(ConstWriter {
70            f: f,
71        })
72    }
73
74    /// finish writing dependencies and start writing constants
75    pub fn finish_dependencies(self) -> ConstValueWriter {
76        ConstValueWriter { f: self.f }
77    }
78
79    /// Add a dependency to your constants file.
80    pub fn add_dependency(&mut self, lib: &str) {
81        write!(self.f, "pub use {};\n", lib).unwrap();
82    }
83
84    /// Add a raw string to the constants file.
85    /// 
86    /// This method only changes `raw` by adding a `\n` at the end.
87    pub fn add_raw(&mut self, raw: &str) {
88        write!(self.f, "{}\n", raw).unwrap();
89    }
90
91}
92
93impl ConstValueWriter {
94    /// Add a value to the constants file.
95    /// 
96    /// You have to manually specify the `name`, type (`ty`) and `value`
97    /// of the constant you want to add.
98    /// 
99    /// The `value` uses the `Debug` trait to determine the formating of
100    /// the value being added. If `Debug` is not accurate or will not work,
101    /// you must use `add_value_raw` instead and format it yourself.
102    pub fn add_value<T: Debug>(&mut self, name: &str, ty: &str, value: T) {
103        self.add_value_raw(name, ty, &format!("{:?}", value));
104    }
105
106    /// Add a pre-formatted value to the constants file.
107    /// 
108    /// `add_value` depends on `Debug` being implemented in such a way
109    /// that it accurately represents the type's creation. Sometimes that
110    /// cannot be relied on and `add_value_raw` has to be used instead.
111    pub fn add_value_raw(&mut self, name: &str, ty: &str, raw_value: &str) {
112        write!(
113            self.f, "pub const {}: {} = {};\n", 
114            name, 
115            ty,
116            raw_value,
117        ).unwrap();
118    }
119
120    /// Add an array of len > 0 to the constants
121    /// 
122    /// You have to manually specify the `name`, type (`ty`) of the **items** and 
123    /// `values` of the array constant you want to add. The length of the array
124    /// is determined automatically.
125    /// 
126    /// Example: `const.add_array("foo", "u16", &[1,2,3])`
127    /// 
128    /// The `value` of each item uses the `Debug` trait to determine the 
129    /// formatting of the value being added. If `Debug` is not accurate or will 
130    /// not work, you must use `add_array_raw` instead and format it yourself.
131    pub fn add_array<T: Debug>(&mut self, name: &str, ty: &str, values: &[T]) {
132        write_array(&mut self.f, name, ty, values);
133    }
134
135    /// Add an array of pre-formatted values to the constants file. The length of the array is
136    /// determined automatically.
137    /// 
138    /// `add_array` depends on `Debug` being implemented for each item in such a way that it
139    /// accurately represents the item's creation. Sometimes that cannot be relied on and
140    /// `add_array_raw` has to be used instead.
141    pub fn add_array_raw<S: AsRef<str> + Display>(&mut self, name: &str, ty: &str, raw_values: &[S]) {
142        write_array_raw(&mut self.f, name, ty, raw_values);
143    }
144
145    /// Add a raw string to the constants file.
146    /// 
147    /// This method only changes `raw` by adding a `\n` at the end.
148    pub fn add_raw(&mut self, raw: &str) {
149        write!(self.f, "{}\n", raw).unwrap();
150    }
151
152    /// Finish writing to the constants file and consume self.
153    pub fn finish(&mut self) {
154        self.f.flush().unwrap();
155    }
156
157}
158
159// Public Functions
160
161/// Write an array and return the array's full type representation.
162/// 
163/// This can be used to create nested array constant types.
164pub fn write_array<T: Debug, W: Write>(w: &mut W, name: &str, ty: &str, values: &[T]) 
165    -> String
166{
167    assert!(
168        !values.is_empty(), 
169        "attempting to add an array of len zero. If this is intentional, use \
170        add_value_raw instead."
171    );
172    let full_ty = write_array_header(w, name, ty, values.len());
173    for v in values.iter() {
174        write_array_item_raw(w, &format!("{:?}", v));
175    }
176    write_array_end(w);
177    full_ty
178}
179
180/// Write an array of raw values and return the array's full type representation.
181/// 
182/// This can be used to create nested array constant types.
183pub fn write_array_raw<W: Write, S: AsRef<str> + Display>(
184        w: &mut W, name: &str, ty: &str, raw_values: &[S]
185    ) 
186    -> String
187{
188    assert!(
189        !raw_values.is_empty(), 
190        "attempting to add an array of len zero. If this is intentional, use \
191        add_value_raw instead."
192    );
193    let full_ty = write_array_header(w, name, ty, raw_values.len());
194    for v in raw_values {
195        write_array_item_raw(w, v);
196    }
197    write_array_end(w);
198    full_ty
199}
200
201// Helpers
202
203/// Write the array header and return the array's full type.
204fn write_array_header<W: Write>(w: &mut W, name: &str, ty: &str, len: usize) -> String {
205    let full_ty = format!("[{}; {}]", ty, len);
206    write!(w, "pub const {}: {} = [\n", name, &full_ty).unwrap();
207    full_ty
208}
209
210fn write_array_item_raw<W: Write, S: AsRef<str> + Display>(w: &mut W, raw_item: S) {
211    write!(w, "    {},\n", raw_item).unwrap()
212}
213
214fn write_array_end<W: Write>(w: &mut W) {
215    write!(w, "];\n").unwrap();
216}