[meta] Introduce the fmtln! macro to ease writing formatted strings;

This commit is contained in:
Benjamin Bouvier
2019-02-13 17:27:33 +01:00
parent c8e09cb37f
commit 603d80615f
5 changed files with 211 additions and 173 deletions

View File

@@ -21,84 +21,92 @@ fn gen_constructor(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatte
ParentGroup::None => "builder: Builder",
ParentGroup::Shared => "shared: &settings::Flags, builder: Builder",
};
fmt.line("impl Flags {");
fmtln!(fmt, "impl Flags {");
fmt.indent(|fmt| {
fmt.doc_comment(format!("Create flags {} settings group.", group.name));
fmt.line("#[allow(unused_variables)]");
fmt.line(format!("pub fn new({}) -> Self {{", args));
fmtln!(fmt, "#[allow(unused_variables)]");
fmtln!(fmt, "pub fn new({}) -> Self {{", args);
fmt.indent(|fmt| {
fmt.line(format!("let bvec = builder.state_for(\"{}\");", group.name));
fmt.line(format!(
fmtln!(fmt, "let bvec = builder.state_for(\"{}\");", group.name);
fmtln!(
fmt,
"let mut {} = Self {{ bytes: [0; {}] }};",
group.name,
group.byte_size()
));
fmt.line(format!(
);
fmtln!(
fmt,
"debug_assert_eq!(bvec.len(), {});",
group.settings_size
));
fmt.line(format!(
);
fmtln!(
fmt,
"{}.bytes[0..{}].copy_from_slice(&bvec);",
group.name, group.settings_size
));
group.name,
group.settings_size
);
// Now compute the predicates.
for p in &group.predicates {
fmt.comment(format!("Precompute #{}.", p.number));
fmt.line(format!("if {} {{", p.render(group)));
fmtln!(fmt, "if {} {{", p.render(group));
fmt.indent(|fmt| {
fmt.line(format!(
fmtln!(
fmt,
"{}.bytes[{}] |= 1 << {};",
group.name,
group.bool_start_byte_offset + p.number / 8,
p.number % 8
));
);
});
fmt.line("}");
fmtln!(fmt, "}");
}
fmt.line(group.name);
fmtln!(fmt, group.name);
});
fmt.line("}");
fmtln!(fmt, "}");
});
fmt.line("}");
fmtln!(fmt, "}");
}
/// Emit Display and FromStr implementations for enum settings.
fn gen_to_and_from_str(name: &str, values: &[&'static str], fmt: &mut Formatter) {
fmt.line(format!("impl fmt::Display for {} {{", name));
fmtln!(fmt, "impl fmt::Display for {} {{", name);
fmt.indent(|fmt| {
fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {");
fmtln!(
fmt,
"fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {"
);
fmt.indent(|fmt| {
fmt.line("f.write_str(match *self {");
fmtln!(fmt, "f.write_str(match *self {");
fmt.indent(|fmt| {
for v in values.iter() {
fmt.line(format!("{}::{} => \"{}\",", name, camel_case(v), v));
fmtln!(fmt, "{}::{} => \"{}\",", name, camel_case(v), v);
}
});
fmt.line("})");
fmtln!(fmt, "})");
});
fmt.line("}");
fmtln!(fmt, "}");
});
fmt.line("}");
fmtln!(fmt, "}");
fmt.line(format!("impl str::FromStr for {} {{", name));
fmtln!(fmt, "impl str::FromStr for {} {{", name);
fmt.indent(|fmt| {
fmt.line("type Err = ();");
fmt.line("fn from_str(s: &str) -> Result<Self, Self::Err> {");
fmtln!(fmt, "type Err = ();");
fmtln!(fmt, "fn from_str(s: &str) -> Result<Self, Self::Err> {");
fmt.indent(|fmt| {
fmt.line("match s {");
fmtln!(fmt, "match s {");
fmt.indent(|fmt| {
for v in values.iter() {
fmt.line(format!("\"{}\" => Ok({}::{}),", v, name, camel_case(v)));
fmtln!(fmt, "\"{}\" => Ok({}::{}),", v, name, camel_case(v));
}
fmt.line("_ => Err(()),");
fmtln!(fmt, "_ => Err(()),");
});
fmt.line("}");
fmtln!(fmt, "}");
});
fmt.line("}");
fmtln!(fmt, "}");
});
fmt.line("}");
fmtln!(fmt, "}");
}
/// Emit real enum for the Enum settings.
@@ -111,15 +119,15 @@ fn gen_enum_types(group: &SettingGroup, fmt: &mut Formatter) {
let name = camel_case(setting.name);
fmt.doc_comment(format!("Values for `{}.{}`.", group.name, setting.name));
fmt.line("#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]");
fmt.line(format!("pub enum {} {{", name));
fmtln!(fmt, "#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]");
fmtln!(fmt, "pub enum {} {{", name);
fmt.indent(|fmt| {
for v in values.iter() {
fmt.doc_comment(format!("`{}`.", v));
fmt.line(format!("{},", camel_case(v)));
fmtln!(fmt, "{},", camel_case(v));
}
});
fmt.line("}");
fmtln!(fmt, "}");
gen_to_and_from_str(&name, values, fmt);
}
@@ -132,15 +140,15 @@ fn gen_getter(setting: &Setting, fmt: &mut Formatter) {
SpecificSetting::Bool(BoolSetting {
predicate_number, ..
}) => {
fmt.line(format!("pub fn {}(&self) -> bool {{", setting.name));
fmtln!(fmt, "pub fn {}(&self) -> bool {{", setting.name);
fmt.indent(|fmt| {
fmt.line(format!("self.numbered_predicate({})", predicate_number));
fmtln!(fmt, "self.numbered_predicate({})", predicate_number);
});
fmt.line("}");
fmtln!(fmt, "}");
}
SpecificSetting::Enum(ref values) => {
let ty = camel_case(setting.name);
fmt.line(format!("pub fn {}(&self) -> {} {{", setting.name, ty));
fmtln!(fmt, "pub fn {}(&self) -> {} {{", setting.name, ty);
fmt.indent(|fmt| {
let mut m = Match::new(format!("self.bytes[{}]", setting.byte_offset));
for (i, v) in values.iter().enumerate() {
@@ -153,53 +161,58 @@ fn gen_getter(setting: &Setting, fmt: &mut Formatter) {
m.arm("_", vec![], "panic!(\"Invalid enum value\")");
fmt.add_match(m);
});
fmt.line("}");
fmtln!(fmt, "}");
}
SpecificSetting::Num(_) => {
fmt.line(format!("pub fn {}(&self) -> u8 {{", setting.name));
fmtln!(fmt, "pub fn {}(&self) -> u8 {{", setting.name);
fmt.indent(|fmt| {
fmt.line(format!("self.bytes[{}]", setting.byte_offset));
fmtln!(fmt, "self.bytes[{}]", setting.byte_offset);
});
fmt.line("}");
fmtln!(fmt, "}");
}
}
}
fn gen_pred_getter(predicate: &Predicate, group: &SettingGroup, fmt: &mut Formatter) {
fmt.doc_comment(format!("Computed predicate `{}`.", predicate.render(group)));
fmt.line(format!("pub fn {}(&self) -> bool {{", predicate.name));
fmtln!(fmt, "pub fn {}(&self) -> bool {{", predicate.name);
fmt.indent(|fmt| {
fmt.line(format!("self.numbered_predicate({})", predicate.number));
fmtln!(fmt, "self.numbered_predicate({})", predicate.number);
});
fmt.line("}");
fmtln!(fmt, "}");
}
/// Emits getters for each setting value.
fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) {
fmt.doc_comment("User-defined settings.");
fmt.line("#[allow(dead_code)]");
fmt.line("impl Flags {");
fmtln!(fmt, "#[allow(dead_code)]");
fmtln!(fmt, "impl Flags {");
fmt.indent(|fmt| {
fmt.doc_comment("Get a view of the boolean predicates.");
fmt.line("pub fn predicate_view(&self) -> ::settings::PredicateView {");
fmtln!(
fmt,
"pub fn predicate_view(&self) -> ::settings::PredicateView {"
);
fmt.indent(|fmt| {
fmt.line(format!(
fmtln!(
fmt,
"::settings::PredicateView::new(&self.bytes[{}..])",
group.bool_start_byte_offset
));
);
});
fmt.line("}");
fmtln!(fmt, "}");
if group.settings.len() > 0 {
fmt.doc_comment("Dynamic numbered predicate getter.");
fmt.line("fn numbered_predicate(&self, p: usize) -> bool {");
fmtln!(fmt, "fn numbered_predicate(&self, p: usize) -> bool {");
fmt.indent(|fmt| {
fmt.line(format!(
fmtln!(
fmt,
"self.bytes[{} + p / 8] & (1 << (p % 8)) != 0",
group.bool_start_byte_offset
));
);
});
fmt.line("}");
fmtln!(fmt, "}");
}
for setting in &group.settings {
@@ -209,7 +222,7 @@ fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) {
gen_pred_getter(&predicate, &group, fmt);
}
});
fmt.line("}");
fmtln!(fmt, "}");
}
#[derive(Hash, PartialEq, Eq)]
@@ -234,66 +247,66 @@ fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) {
let mut descriptor_index_map: HashMap<SettingOrPreset, usize> = HashMap::new();
// Generate descriptors.
fmt.line(format!(
fmtln!(
fmt,
"static DESCRIPTORS: [detail::Descriptor; {}] = [",
group.settings.len() + group.presets.len()
));
);
fmt.indent(|fmt| {
for (idx, setting) in group.settings.iter().enumerate() {
fmt.line("detail::Descriptor {");
fmtln!(fmt, "detail::Descriptor {");
fmt.indent(|fmt| {
fmt.line(format!("name: \"{}\",", setting.name));
fmt.line(format!("offset: {},", setting.byte_offset));
fmtln!(fmt, "name: \"{}\",", setting.name);
fmtln!(fmt, "offset: {},", setting.byte_offset);
match setting.specific {
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
fmt.line(format!(
fmtln!(
fmt,
"detail: detail::Detail::Bool {{ bit: {} }},",
bit_offset
));
);
}
SpecificSetting::Enum(ref values) => {
let offset = enum_table.add(values);
fmt.line(format!(
fmtln!(
fmt,
"detail: detail::Detail::Enum {{ last: {}, enumerators: {} }},",
values.len() - 1,
offset
));
);
}
SpecificSetting::Num(_) => {
fmt.line("detail: detail::Detail::Num,");
fmtln!(fmt, "detail: detail::Detail::Num,");
}
}
descriptor_index_map.insert(SettingOrPreset::Setting(setting), idx);
});
fmt.line("},");
fmtln!(fmt, "},");
}
for (idx, preset) in group.presets.iter().enumerate() {
fmt.line("detail::Descriptor {");
fmtln!(fmt, "detail::Descriptor {");
fmt.indent(|fmt| {
fmt.line(format!("name: \"{}\",", preset.name));
fmt.line(format!("offset: {},", (idx as u8) * group.settings_size));
fmt.line("detail: detail::Detail::Preset,");
fmtln!(fmt, "name: \"{}\",", preset.name);
fmtln!(fmt, "offset: {},", (idx as u8) * group.settings_size);
fmtln!(fmt, "detail: detail::Detail::Preset,");
});
fmt.line("},");
fmtln!(fmt, "},");
descriptor_index_map.insert(SettingOrPreset::Preset(preset), idx);
}
});
fmt.line("];");
fmtln!(fmt, "];");
// Generate enumerators.
fmt.line(format!(
"static ENUMERATORS: [&str; {}] = [",
enum_table.len()
));
fmtln!(fmt, "static ENUMERATORS: [&str; {}] = [", enum_table.len());
fmt.indent(|fmt| {
for enum_val in enum_table.iter() {
fmt.line(format!("\"{}\",", enum_val));
fmtln!(fmt, "\"{}\",", enum_val);
}
});
fmt.line("];");
fmtln!(fmt, "];");
// Generate hash table.
let mut hash_entries: Vec<SettingOrPreset> = Vec::new();
@@ -312,40 +325,39 @@ fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) {
.collect::<Vec<SettingOrPreset>>(),
);
let hash_table = generate_table(&hash_entries, |entry| simple_hash(entry.name()));
fmt.line(format!(
"static HASH_TABLE: [u16; {}] = [",
hash_table.len()
));
fmtln!(fmt, "static HASH_TABLE: [u16; {}] = [", hash_table.len());
fmt.indent(|fmt| {
for h in &hash_table {
match *h {
Some(setting_or_preset) => fmt.line(format!(
Some(setting_or_preset) => fmtln!(
fmt,
"{},",
&descriptor_index_map
.get(setting_or_preset)
.unwrap()
.to_string()
)),
None => fmt.line("0xffff,"),
),
None => fmtln!(fmt, "0xffff,"),
}
}
});
fmt.line("];");
fmtln!(fmt, "];");
// Generate presets.
fmt.line(format!(
fmtln!(
fmt,
"static PRESETS: [(u8, u8); {}] = [",
group.presets.len()
));
);
fmt.indent(|fmt| {
for preset in &group.presets {
fmt.comment(preset.name);
for (mask, value) in preset.layout(&group) {
fmt.line(format!("(0b{:08b}, 0b{:08b}),", mask, value));
fmtln!(fmt, "(0b{:08b}, 0b{:08b}),", mask, value);
}
}
});
fmt.line("];");
fmtln!(fmt, "];");
}
fn gen_template(group: &SettingGroup, fmt: &mut Formatter) {
@@ -360,63 +372,70 @@ fn gen_template(group: &SettingGroup, fmt: &mut Formatter) {
.collect();
let default_bytes_str = default_bytes.join(", ");
fmt.line("static TEMPLATE: detail::Template = detail::Template {");
fmtln!(
fmt,
"static TEMPLATE: detail::Template = detail::Template {"
);
fmt.indent(|fmt| {
fmt.line(format!("name: \"{}\",", group.name));
fmt.line("descriptors: &DESCRIPTORS,");
fmt.line("enumerators: &ENUMERATORS,");
fmt.line("hash_table: &HASH_TABLE,");
fmt.line(format!("defaults: &[{}],", default_bytes_str));
fmt.line("presets: &PRESETS,");
fmtln!(fmt, "name: \"{}\",", group.name);
fmtln!(fmt, "descriptors: &DESCRIPTORS,");
fmtln!(fmt, "enumerators: &ENUMERATORS,");
fmtln!(fmt, "hash_table: &HASH_TABLE,");
fmtln!(fmt, "defaults: &[{}],", default_bytes_str);
fmtln!(fmt, "presets: &PRESETS,");
});
fmt.line("};");
fmtln!(fmt, "};");
fmt.doc_comment(format!(
"Create a `settings::Builder` for the {} settings group.",
group.name
));
fmt.line("pub fn builder() -> Builder {");
fmtln!(fmt, "pub fn builder() -> Builder {");
fmt.indent(|fmt| {
fmt.line("Builder::new(&TEMPLATE)");
fmtln!(fmt, "Builder::new(&TEMPLATE)");
});
fmt.line("}");
fmtln!(fmt, "}");
}
fn gen_display(group: &SettingGroup, fmt: &mut Formatter) {
fmt.line("impl fmt::Display for Flags {");
fmtln!(fmt, "impl fmt::Display for Flags {");
fmt.indent(|fmt| {
fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {");
fmtln!(
fmt,
"fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {"
);
fmt.indent(|fmt| {
fmt.line(format!("writeln!(f, \"[{}]\")?;", group.name));
fmt.line("for d in &DESCRIPTORS {");
fmtln!(fmt, "writeln!(f, \"[{}]\")?;", group.name);
fmtln!(fmt, "for d in &DESCRIPTORS {");
fmt.indent(|fmt| {
fmt.line("if !d.detail.is_preset() {");
fmtln!(fmt, "if !d.detail.is_preset() {");
fmt.indent(|fmt| {
fmt.line("write!(f, \"{} = \", d.name)?;");
fmt.line(
fmtln!(fmt, "write!(f, \"{} = \", d.name)?;");
fmtln!(
fmt,
"TEMPLATE.format_toml_value(d.detail, self.bytes[d.offset as usize], f)?;",
);
fmt.line("writeln!(f)?;");
fmtln!(fmt, "writeln!(f)?;");
});
fmt.line("}");
fmtln!(fmt, "}");
});
fmt.line("}");
fmt.line("Ok(())");
fmtln!(fmt, "}");
fmtln!(fmt, "Ok(())");
});
fmt.line("}")
fmtln!(fmt, "}")
});
fmt.line("}");
fmtln!(fmt, "}");
}
fn gen_group(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
// Generate struct.
fmt.line("#[derive(Clone)]");
fmtln!(fmt, "#[derive(Clone)]");
fmt.doc_comment(format!("Flags group `{}`.", group.name));
fmt.line("pub struct Flags {");
fmtln!(fmt, "pub struct Flags {");
fmt.indent(|fmt| {
fmt.line(format!("bytes: [u8; {}],", group.byte_size()));
fmtln!(fmt, "bytes: [u8; {}],", group.byte_size());
});
fmt.line("}");
fmtln!(fmt, "}");
gen_constructor(group, parent, fmt);
gen_enum_types(group, fmt);