blob: af3e53681fac61fe99d7e344875a42ec00da39b1 [file] [log] [blame]
use std::env;
use std::fs;
use std::fmt::Debug;
use std::io;
use std::io::Write;
use std::path::Path;
use std::str;
/// Primary object used to write constant files.
///
/// # Example
/// ```no_run
/// # use std::path::Path;
/// # #[derive(Debug)]
/// # struct Point { x: u8, y: u8 }
/// use build_const::ConstWriter;
///
/// // use `for_build` in `build.rs`
/// let mut consts = ConstWriter::from_path(
/// &Path::new("/tmp/constants.rs")
/// ).unwrap();
///
/// // add an external dependency (`use xyz::Point`)
/// consts.add_dependency("xyz::Point");
///
/// // finish dependencies and starting writing constants
/// let mut consts = consts.finish_dependencies();
///
/// // add an array of values
/// let values: Vec<u8> = vec![1, 2, 3, 36];
/// consts.add_array("ARRAY", "u8", &values);
///
/// // Add a value that is a result of "complex" calculations
/// consts.add_value("VALUE", "u8", values.iter().sum::<u8>());
///
/// // Add a value from an external crate (must implement `Debug`)
/// consts.add_value("VALUE", "Point", &Point { x: 3, y: 7});
/// ```
pub struct ConstWriter {
f: fs::File,
}
/// Created from `ConstWriter::finish_dependencies`. See
/// documentation for `ConstWriter`.
pub struct ConstValueWriter {
f: fs::File,
}
impl ConstWriter {
/// Create a ConstWriter to be used for your crate's `build.rs`
pub fn for_build(mod_name: &str) -> io::Result<ConstWriter> {
let out_dir = env::var("OUT_DIR").unwrap();
let mod_name = format!("{}.rs", mod_name);
let dest_path = Path::new(&out_dir).join(mod_name);
Ok(ConstWriter {
f: fs::File::create(&dest_path)?
})
}
/// Create a new ConstWriter to write to an path. If a file
/// already exists at the path then it will be deleted.
pub fn from_path(path: &Path) -> io::Result<ConstWriter> {
let f = fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(path)?;
Ok(ConstWriter {
f: f,
})
}
/// finish writing dependencies and start writing constants
pub fn finish_dependencies(self) -> ConstValueWriter {
ConstValueWriter { f: self.f }
}
/// Add a dependency to your constants file.
pub fn add_dependency(&mut self, lib: &str) {
write!(self.f, "pub use {};\n", lib).unwrap();
}
/// Add a raw string to the constants file.
///
/// This method only changes `raw` by adding a `\n` at the end.
pub fn add_raw(&mut self, raw: &str) {
write!(self.f, "{}\n", raw).unwrap();
}
}
impl ConstValueWriter {
/// Add a value to the constants file.
///
/// You have to manually specify the `name`, type (`ty`) and `value`
/// of the constant you want to add.
///
/// The `value` uses the `Debug` trait to determine the formating of
/// the value being added. If `Debug` is not accurate or will not work,
/// you must use `add_value_raw` instead and format it yourself.
pub fn add_value<T: Debug>(&mut self, name: &str, ty: &str, value: T) {
self.add_value_raw(name, ty, &format!("{:?}", value));
}
/// Add a pre-formatted value to the constants file.
///
/// `add_value` depends on `Debug` being implemented in such a way
/// that it accurately represents the type's creation. Sometimes that
/// cannot be relied on and `add_value_raw` has to be used instead.
pub fn add_value_raw(&mut self, name: &str, ty: &str, raw_value: &str) {
write!(
self.f, "pub const {}: {} = {};\n",
name,
ty,
raw_value,
).unwrap();
}
/// Add an array of len > 0 to the constants
///
/// You have to manually specify the `name`, type (`ty`) of the **items** and
/// `values` of the array constant you want to add. The length of the array
/// is determined automatically.
///
/// Example: `const.add_array("foo", "u16", &[1,2,3])`
///
/// The `value` of each item uses the `Debug` trait to determine the
/// formatting of the value being added. If `Debug` is not accurate or will
/// not work, you must use `add_array_raw` instead and format it yourself.
pub fn add_array<T: Debug>(&mut self, name: &str, ty: &str, values: &[T]) {
write_array(&mut self.f, name, ty, values);
}
/// Add an array of pre-formatted values to the constants file. The length of the array is
/// determined automatically.
///
/// `add_array` depends on `Debug` being implemented for each item in such a way that it
/// accurately represents the item's creation. Sometimes that cannot be relied on and
/// `add_array_raw` has to be used instead.
pub fn add_array_raw(&mut self, name: &str, ty: &str, raw_values: &[&str]) {
write_array_raw(&mut self.f, name, ty, raw_values);
}
/// Add a raw string to the constants file.
///
/// This method only changes `raw` by adding a `\n` at the end.
pub fn add_raw(&mut self, raw: &str) {
write!(self.f, "{}\n", raw).unwrap();
}
/// Finish writing to the constants file and consume self.
pub fn finish(&mut self) {
self.f.flush().unwrap();
}
}
// Public Functions
/// Write an array and return the array's full type representation.
///
/// This can be used to create nested array constant types.
pub fn write_array<T: Debug, W: Write>(w: &mut W, name: &str, ty: &str, values: &[T])
-> String
{
assert!(
!values.is_empty(),
"attempting to add an array of len zero. If this is intentional, use \
add_value_raw instead."
);
let full_ty = write_array_header(w, name, ty, values.len());
for v in values.iter() {
write_array_item_raw(w, &format!("{:?}", v));
}
write_array_end(w);
full_ty
}
/// Write an array of raw values and return the array's full type representation.
///
/// This can be used to create nested array constant types.
pub fn write_array_raw<W: Write>(
w: &mut W, name: &str, ty: &str, raw_values: &[&str]
)
-> String
{
assert!(
!raw_values.is_empty(),
"attempting to add an array of len zero. If this is intentional, use \
add_value_raw instead."
);
let full_ty = write_array_header(w, name, ty, raw_values.len());
for &v in raw_values {
write_array_item_raw(w, v);
}
write_array_end(w);
full_ty
}
// Helpers
/// Write the array header and return the array's full type.
fn write_array_header<W: Write>(w: &mut W, name: &str, ty: &str, len: usize) -> String {
let full_ty = format!("[{}; {}]", ty, len);
write!(w, "pub const {}: {} = [\n", name, &full_ty).unwrap();
full_ty
}
fn write_array_item_raw<W: Write>(w: &mut W, raw_item: &str) {
write!(w, " {},\n", raw_item).unwrap()
}
fn write_array_end<W: Write>(w: &mut W) {
write!(w, "];\n").unwrap();
}