1 use std::env;
2 use std::fs;
3 use std::fmt::{Debug, Display};
4 use std::io;
5 use std::io::Write;
6 use std::path::Path;
7 use 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 /// ```
40 pub struct ConstWriter {
41     f: fs::File,
42 }
43 
44 /// Created from `ConstWriter::finish_dependencies`. See
45 /// documentation for `ConstWriter`.
46 pub struct ConstValueWriter {
47     f: fs::File,
48 }
49 
50 impl ConstWriter {
51     /// Create a ConstWriter to be used for your crate's `build.rs`
for_build(mod_name: &str) -> io::Result<ConstWriter>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.
from_path(path: &Path) -> io::Result<ConstWriter>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
finish_dependencies(self) -> ConstValueWriter75     pub fn finish_dependencies(self) -> ConstValueWriter {
76         ConstValueWriter { f: self.f }
77     }
78 
79     /// Add a dependency to your constants file.
add_dependency(&mut self, lib: &str)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.
add_raw(&mut self, raw: &str)87     pub fn add_raw(&mut self, raw: &str) {
88         write!(self.f, "{}\n", raw).unwrap();
89     }
90 
91 }
92 
93 impl 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.
add_value<T: Debug>(&mut self, name: &str, ty: &str, value: T)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.
add_value_raw(&mut self, name: &str, ty: &str, raw_value: &str)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.
add_array<T: Debug>(&mut self, name: &str, ty: &str, values: &[T])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.
add_array_raw<S: AsRef<str> + Display>(&mut self, name: &str, ty: &str, raw_values: &[S])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.
add_raw(&mut self, raw: &str)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.
finish(&mut 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.
write_array<T: Debug, W: Write>(w: &mut W, name: &str, ty: &str, values: &[T]) -> String164 pub 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.
write_array_raw<W: Write, S: AsRef<str> + Display>( w: &mut W, name: &str, ty: &str, raw_values: &[S] ) -> String183 pub 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.
write_array_header<W: Write>(w: &mut W, name: &str, ty: &str, len: usize) -> String204 fn 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 
write_array_item_raw<W: Write, S: AsRef<str> + Display>(w: &mut W, raw_item: S)210 fn 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 
write_array_end<W: Write>(w: &mut W)214 fn write_array_end<W: Write>(w: &mut W) {
215     write!(w, "];\n").unwrap();
216 }
217