Is it possible to use loops inside a Rust Mustache (https://github.com/erickt/rust-mustache) MapBuilder to populate the contents of the vector? I've attached a semi-working example, it works with the struct version but not with the builder version.
Edit: The code compiles now thanks to Paul. I was missing two things: |mut builder| and builder = builder.push_map.
The thing is I do not want to use the struct version as I'll need to iterate over the images vector and mutate an item based on an certain condition (and I don't want to change the original vector).
I've looked here https://github.com/erickt/rust-mustache/blob/master/src/builder.rs but none of these code snippets show a loop example. Either it is not possible or I'm doing something wrong. I also created an issue but the erickt doesn't seem very active there.
Thank you.
main.rs:
extern crate serialize;
// [dependencies.rust-mustache]
//
// # git = "https://github.com/erickt/rust-mustache.git"
// git = "https://github.com/tsurai/rust-mustache.git"
extern crate mustache;
mod with_struct;
mod with_builder;
fn main() {
with_struct::go();
with_builder::go();
}
with_builder.rs:
use std::io;
use mustache;
use mustache::MapBuilder;
struct Image {
name: String,
file: String
}
impl Image {
fn new(name: &str, file: &str) -> Image {
Image {
name: String::from_str(name),
file: String::from_str(file)
}
}
}
pub fn go() {
let images = load_images();
let template = mustache::compile_str(template());
let data = MapBuilder::new()
.insert_vec("images", |mut builder| {
// ^~~~~ Need mutableb builder
for image in images.iter() {
builder = builder.push_map(|builder| {
// ^~~~~~~~~~^~~~~ Need to re-assign the builder
builder
.insert_str("name", image.name.clone())
.insert_str("file", image.file.clone())
});
}
builder
// ^~~~~ Can now return it
})
.build();
let _ = template.render_data(&mut io::stdout(), &data);
}
fn template<'a>() -> &'a str {
"
<ul>
{{#images}}
<li>
{{name}}
</li>
{{/images}}
</ul>
"
}
fn load_images() -> Vec<Image> {
let mut images = Vec::new();
images.push(Image::new("Picture 1", "picture-1.png"));
images.push(Image::new("Picture 2", "picture-2.png"));
images.push(Image::new("Picture 3", "picture-3.png"));
images
}
with_struct.rs:
use std::io;
use mustache;
#[deriving(Encodable)]
struct Image {
name: String,
file: String
}
#[deriving(Encodable)]
struct TemplateData<'a> {
images: &'a Vec<Image>
}
impl Image {
fn new(name: &str, file: &str) -> Image {
Image {
name: String::from_str(name),
file: String::from_str(file)
}
}
}
pub fn go() {
let images = load_images();
let template = mustache::compile_str(template());
let data = TemplateData {
images: &images
};
let _ = template.render(&mut io::stdout(), &data);
}
fn template<'a>() -> &'a str {
"
<ul>
{{#images}}
<li>
{{name}}
</li>
{{/images}}
</ul>
"
}
fn load_images() -> Vec<Image> {
let mut images = Vec::new();
images.push(Image::new("Picture 1", "picture-1.png"));
images.push(Image::new("Picture 2", "picture-2.png"));
images.push(Image::new("Picture 3", "picture-3.png"));
images
}
Ollie!
You need to remove the semicolon after last insert_str (Rust discards that line, and don't use as return value, if you put semicolon in the end).
Related
I am saving in append mode a stream of events on a YAML log file, where each event is represented by an indivual document, like this:
---
type: event
id: 1
---
type: trigger
id: 2
At some point later I want to iterate on these events, parsing each via serde_yaml. To my understanding though, serde_yaml doesn't seem to support parsing multiple documents from a single reader, as none of the available methods mention it, and trying to parse multiple documents at once results in a MoreThanOneDocument error.
use std::io::{self, BufRead};
use serde_yaml;
use serde::{self, Deserialize};
#[derive(Deserialize, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Message {
Event { id: i32 },
Trigger { id: i32},
}
fn main() -> io::Result<()> {
let yaml = "---\ntype: event\nid: 1\n---\n\ntype: trigger\nid: 2";
let v: Message = serde_yaml::from_reader(yaml.as_bytes()).unwrap();
println!("{:?}", v);
Ok(())
}
I'm totally new to Rust, so maybe I completely missed the point of serde and just did not understand how to do it.
How would you parse such YAML, please?
I cooked up something that looks like a working solution, but I think I'll try to post it among the answers instead, because I don't want to bias other answers too much towards my solution. I kindly encourage you to have a look at it as well however, any feedback is welcome.
The documentation of serde_yaml::Deserializer shows an example very similar to yours. It would work like this:
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Message {
Event { id: i32 },
Trigger { id: i32 },
}
fn main() {
let yaml = "---\ntype: event\nid: 1\n---\ntype: trigger\nid: 2\n";
for document in serde_yaml::Deserializer::from_str(yaml) {
let v = Message::deserialize(document).unwrap();
println!("{:?}", v);
}
}
I really hope to find a native solution by using serde and serde_yaml only, but until then the way I got it working is as follows.
trait BufReaderYamlExt {
fn read_next_yaml(&mut self) -> io::Result<Option<String>>;
}
impl<T: io::Read> BufReaderYamlExt for io::BufReader<T> {
fn read_next_yaml(&mut self) -> io::Result<Option<String>> {
const sep : &str = "\n---\n";
let mut doc = String::with_capacity(200);
while self.read_line(&mut doc)? > 0 {
if doc.len() > sep.len() && doc.ends_with(sep) {
doc.truncate(doc.len() - sep.len());
break;
}
}
if !doc.is_empty() {
doc.shrink_to_fit();
Ok(Some(doc))
} else {
Ok(None)
}
}
}
The trait extends the BufReader with an extra method that returns an optional owned String (or None at the end of the stream) containing just the portion with a single YAML document.
By iterating on it one could then apply serde_json::from_str() to parse the document into a Message struct.
fn main() -> io::Result<()> {
let yaml = "---\ntype: event\nid: 1\n\n---\n\ntype: trigger\nid: 2\n";
let mut r = io::BufReader::new(yaml.as_bytes());
while let Some(next) = r.read_next_yaml()? {
let d: Message = serde_yaml::from_str(&next).unwrap();
println!("parsed: {:?}", d);
}
Ok(())
}
I've made available the full source on the rust playground as well.
I'm currently working on writing up a tui for a server application in a personal project, and I picked the cursive library to do it. I have run into a problem with a component of my application that is supposed to serve as the server's console window. In setting up the ability to output text to the window, I've run into a problem where I can't update the TextView.
According to the last comment in this issue, the way to do this is to use a TextContent and update that, which I set up below. While the initial content will show up in the UI, no updates to the content do. I have tried setting content on the TextView directly by giving it a name, but no luck. I've been able to get_content on the TextView itself and see that the content has been updated, it just isn't reflected in the UI.
Is there any way to do this, or do I need to make a custom view for the output?
Here is the code in question:
use std::ops::Add;
use std::borrow::{BorrowMut, Borrow};
use cursive::{View, Printer, Vec2, Rect, With};
use cursive::event::{Event, EventResult, AnyCb, Key};
use cursive::view::{Selector, ViewNotFound, ViewWrapper};
use cursive::direction::Direction;
use cursive::views::{EditView, LinearLayout, OnEventView, DummyView, TextView, TextContent};
use cursive::traits::{Nameable, Scrollable, Resizable};
use cursive::view::scroll::Scroller;
pub const CONSOLE_INPUT_NAME: &str = "console_input";
pub const CONSOLE_OUTPUT_LENGTH: u16 = 1000;
pub(crate) struct ConsoleView {
inner_view: LinearLayout,
console_output: TextContent,
console_output_history: LinkedList<String>
}
impl Default for ConsoleView {
fn default() -> Self {
let console_output = TextContent::new("This content should get replaced.");
let inner_view = LinearLayout::vertical()
.child(TextView::new_with_content(console_output.clone())
.full_screen()
.scrollable()
.wrap_with(OnEventView::new)
.on_pre_event_inner(Key::PageUp, |v, _| {
let scroller = v.get_scroller_mut();
if scroller.can_scroll_up() {
scroller.scroll_up(
scroller.last_outer_size().y.saturating_sub(1),
);
}
Some(EventResult::Consumed(None))
})
.on_pre_event_inner(Key::PageDown, |v, _| {
let scroller = v.get_scroller_mut();
if scroller.can_scroll_down() {
scroller.scroll_down(
scroller.last_outer_size().y.saturating_sub(1),
);
}
Some(EventResult::Consumed(None))
}))
.child(DummyView)
.child(EditView::new().with_name(CONSOLE_INPUT_NAME));
return Self {
inner_view,
console_output,
console_output_history: LinkedList::new()
}
}
}
impl ViewWrapper for ConsoleView {
type V = LinearLayout;
fn with_view<F, R>(&self, f: F) -> Option<R> where F: FnOnce(&Self::V) -> R {
return Some(f(self.inner_view.borrow()));
}
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R> where F: FnOnce(&mut Self::V) -> R {
return Some(f(self.inner_view.borrow_mut()));
}
fn into_inner(self) -> Result<Self::V, Self> where Self: Sized, Self::V: Sized {
return Ok(self.inner_view);
}
fn wrap_draw(&self, printer: &Printer) {
self.inner_view.draw(printer);
}
fn wrap_required_size(&mut self, req: Vec2) -> Vec2 {
return req;
}
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
return self.inner_view.on_event(ch);
}
fn wrap_layout(&mut self, size: Vec2) {
self.inner_view.layout(size);
}
fn wrap_take_focus(&mut self, source: Direction) -> bool {
return self.inner_view.take_focus(source);
}
fn wrap_call_on_any<'a>(&mut self, selector: &Selector<'_>, callback: AnyCb<'a>) {
self.inner_view.call_on_any(selector, callback);
}
fn wrap_focus_view(&mut self, selector: &Selector<'_>) -> Result<(), ViewNotFound> {
return self.inner_view.focus_view(selector);
}
fn wrap_needs_relayout(&self) -> bool {
return self.inner_view.needs_relayout();
}
fn wrap_important_area(&self, size: Vec2) -> Rect {
return self.inner_view.important_area(size);
}
}
impl ConsoleView {
pub fn print(&mut self, line: &str) {
log::info!("Printing to console: {}", line);
self.console_output_history.push_back(line.to_string());
if self.console_output_history.len() > CONSOLE_OUTPUT_LENGTH as usize {
self.console_output_history.pop_front();
}
let out = self.console_output_history.iter().fold("".to_string(), |acc, s| {
return acc.add(s.as_str()).add("\n");
});
self.console_output.set_content(out);
}
}
Here is a main() to go with to demonstrate the problem. Basically, once the UI is set up and the event loop is started, I can't update the information displayed in the UI. Given that the UI doesn't display until I start the event loop, this is a problem.
let mut siv = cursive::default();
siv.add_layer(ConsoleView::default().with_name("view_name"));
siv.add_global_callback(Key::Esc, |s| s.quit());
siv.run();
siv.call_on_name("view_name", |view: &mut ConsoleView| {
view.print("I should see this text in the app.");
});
}
I've been playing around with Rust by following along with Roguelike Tutorial, and have started to branch out a bit in hopes of creating some kind of nature simulation.
In my simple POC, I'm trying to have multiple "Creatures" wandering the map looking for entities with the ProvidesHealth component (so like plants or bushes or something that get eaten).
In the Roguelike tutorial, monsters can easily locate the player at all times because the player is shared throughout the world as a resource, but in my case, I can't figure out the best way to simulate this behavior in my Specs system.
The Creature entities have a Viewshed component to act as their vision. I originally thought I'd be able to iterate thru the Viewshed's visible_tiles and check if an entity with the ProvidesHealth entity was there, but I wasn't able to get that work.
Any thoughts on this would be greatly appreciated! I'm not sure if my approach is totally off, or I'm missing something simple.
Thanks!
// [dependencies]
// bracket-lib = { git = "https://github.com/thebracket/bracket-lib.git", rev = "927d229" }
// specs = "0.16.1"
// specs-derive = "0.4.1"
use bracket_lib::prelude::*;
use specs::prelude::*;
use specs_derive::*;
use std::{thread, time};
#[derive(Component)]
struct Position {
x: i32,
y: i32,
}
#[derive(Component)]
struct Renderable {
glyph: FontCharType,
fg: RGB,
bg: RGB,
}
#[derive(Component)]
struct Creature {}
#[derive(Component)]
struct ProvidesHealth {
pub hp_gain: i32
}
#[derive(Component)]
pub struct Viewshed {
pub visible_tiles: Vec<Point>,
pub range: i32,
pub dirty: bool
}
struct State {
ecs: World
}
impl State {
fn run_systems(&mut self) {
let mut vis = VisSystem {};
vis.run_now(&self.ecs);
let mut ai = CreatureAI {};
ai.run_now(&self.ecs);
self.ecs.maintain();
}
}
impl GameState for State {
fn tick(&mut self, ctx: &mut BTerm) {
ctx.cls();
self.run_systems();
// map defined in separate file, but isn't really
// important for this question
// draw_map(&self.ecs, ctx);
let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
for (pos, ren) in (&positions, &renderables).join() {
ctx.set(pos.x, pos.y, ren.fg, ren.bg, ren.glyph);
}
let sleep = time::Duration::from_millis(200);
thread::sleep(sleep);
}
}
fn main() -> BError {
let mut context = BTermBuilder::simple80x50()
.build()?;
let mut gs = State {
ecs: World::new(),
};
gs.ecs.register::<Position>();
gs.ecs.register::<Renderable>();
gs.ecs.register::<Creature>();
gs.ecs.register::<ProvidesHealth>();
gs.ecs.register::<Viewshed>();
// add one Creature
gs.ecs
.create_entity()
.with(Position {x: 10, y: 20})
.with(Renderable {
glyph: to_cp437('#'),
fg: RGB::named(WHITE),
bg: RGB::named(BLACK)
})
.with(Creature {})
.with(Viewshed { visible_tiles : Vec::new(), range: 6, dirty: true })
.with(HealthStats { max_hp: 100, hp: 100 })
.build();
// add one "food" item
gs.ecs
.create_entity()
.with(Position {x: 35, y: 35})
.with(Renderable {
glyph: to_cp437('*'),
fg: RGB::named(WHITE),
bg: RGB::named(BLACK),
})
.with(ProvidesHealth { hp_gain: 10 })
.build();
// map defined in separate file, but isn't really
// important for this question
let mut map = Map::new_map();
gs.ecs.insert(map);
main_loop(context, gs)
}
struct VisSystem {}
impl<'a> System<'a> for VisSystem {
type SystemData = (
WriteExpect<'a, Map>,
Entities<'a>,
WriteStorage<'a, Viewshed>,
ReadStorage<'a, Position>,
);
fn run(&mut self, data: Self::SystemData) {
let (map, entities, mut viewshed, pos) = data;
for (_ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() {
if viewshed.dirty {
viewshed.visible_tiles = field_of_view(
Point::new(pos.x, pos.y),
viewshed.range,
&*map,
)
}
}
}
}
struct CreatureAI {}
impl<'a> System<'a> for CreatureAI {
#[allow(clippy::type_complexity)]
type SystemData = (
// ...
);
fn run(&mut self, data: Self::SystemData) {
// ... not sure what to do here\
//
// by doing a join on (viewshed, position),
// i'd be able to iterate thru viewshed.visible_tiles,
// but i can't figure out how I could check if a given
// entity located at the Point has the "ProvidesHealth"
// component or not
}
}
I am trying to create a network of nodes in Rust, where I want every node in the network to be aware of every other connected node. I thought that this could be done with weak Rc's, like this:
use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;
use std::rc::Weak;
struct Node {
name: String,
known_nodes: Rc<RefCell<Vec<Weak<Node>>>>,
}
impl Node {
fn connect_to_network(&mut self) {
self.known_nodes
.borrow_mut()
.push(Rc::downgrade(&Rc::new(*self)));
}
}
fn main() {
let known_nodes = Rc::new(RefCell::new(Vec::new()));
let node_one = Node {
name: "node1",
known_nodes: known_nodes.copy(),
};
node_one.connect_to_network();
let node_two = Node {
name: "node2",
known_nodes: known_nodes.copy(),
};
node_two.connect_to_network();
}
This however yields
cannot move out of borrowed content
at:
self.known_senders.borrow_mut().push(Rc::downgrade(&Rc::new(*self)));
Because *self is moved out of borrowed content in the &Rc::new(*self).
Any ideas, on how each node can keep track of all the other nodes in the network?
You should separate your node and your network, because your network must take the ownership of your node to create an Rc (or at least, it must take an already created Rc). Here is a better design that achieves what you want:
use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
name: String,
}
#[derive(Default, Debug)]
struct Network {
nodes: Rc<RefCell<Vec<Weak<Node>>>>,
}
impl Network {
fn add_node(&mut self, node: Node) -> Rc<Node> {
let node = Rc::new(node);
self.nodes.borrow_mut().push(Rc::downgrade(&node));
node
}
}
fn main() {
let mut network = Network::default();
let node_1 = Node { name: "node_1".into() };
let node_2 = Node { name: "node_2".into() };
let _node_1 = network.add_node(node_1);
let _node_2 = network.add_node(node_2);
}
If you want to store a reference to self, you can do this:
use std::cell::RefCell;
use std::rc::Rc;
use std::rc::Weak;
type MutableNode = Rc<RefCell<Node>>;
type Network = Rc<RefCell<Vec<Weak<RefCell<Node>>>>>;
struct Node {
name: String,
others: Network,
}
impl Node {
fn new(name: String) -> MutableNode {
let node = Rc::new(RefCell::new(Node {
name,
others: Rc::new(RefCell::new(Vec::new())),
}));
{
let tmp = node.borrow();
tmp.others.borrow_mut().push(Rc::downgrade(&node));
}
node
}
fn add_node(&mut self, name: String) -> MutableNode {
let others = self.others.clone();
let node = Rc::new(RefCell::new(Node { name, others }));
self.others
.borrow_mut()
.push(Rc::downgrade(&node));
node
}
fn len(&self) -> usize {
self.others.borrow().len()
}
}
fn main() {
let node_0 = Node::new("node_0".into());
let node_1 = node_0.borrow_mut().add_node("node_1".into());
let node_2 = node_0.borrow_mut().add_node("node_2".into());
assert_eq!(node_0.borrow().len(), 3);
assert_eq!(node_1.borrow().len(), 3);
assert_eq!(node_2.borrow().len(), 3);
}
Rc::new(value:T) consume the value.Your function only borrow it, so you can't call Rc::new(*self)
I would recommend you to create a Network struct like the above answer. Or you can wrap your node in Rc<RefCell<Node>> like this:
use std::cell::RefCell;
use std::rc::Rc;
use std::rc::Weak;
#[derive(Debug)]
struct Node {
name: String,
known_nodes: Rc<RefCell<Vec<Weak<RefCell<Node>>>>>,
}
impl Node {
fn connect_to_network(&mut self,ref_to_self: Weak<RefCell<Node>>) {
self.known_nodes
.borrow_mut()
.push(ref_to_self);
}
}
fn main() {
let known_nodes = Rc::new(RefCell::new(Vec::new()));
let node_one = Rc::new(RefCell::new(Node {
name: "node1".into(),
known_nodes: known_nodes.clone(),
}));
node_one.borrow_mut().connect_to_network(Rc::downgrade(&node_one));
let node_two = Rc::new(RefCell::new(Node {
name: "node2".into(),
known_nodes: known_nodes.clone(),
}));
node_two.borrow_mut().connect_to_network(Rc::downgrade(&node_two));
println!("{:?}",known_nodes.borrow()[0].upgrade());
println!("{:?}",known_nodes.borrow()[1].upgrade());
drop(node_one);
drop(node_two);
println!("{:?}",known_nodes.borrow()[0].upgrade());
println!("{:?}",known_nodes.borrow()[1].upgrade());
}
Which in this case you don't really need connect_to_network function, you can just add each Weak<RefCell<Node>> to known_nodes directly
If you want the code to look cleaner, you can introduce a new type alias to Rc<RefCell<Node>> like this
struct Node {
name: String,
known_nodes: Rc<RefCell<Vec<Weak<RefCell<Node>>>>>,
}
type RcNode = Rc<RefCell<Node>>;
trait Connectable {
fn connect_to_network(&self);
}
impl Connectable for RcNode {
fn connect_to_network(&self){
let node = self.borrow_mut();
node.known_nodes.borrow_mut().push(Rc::downgrade(self));
}
}
so then you can call
let node_one:RcNode = Rc::new(RefCell::new(Node {
name: "node1".into(),
known_nodes: known_nodes.clone(),
}));
node_one.connect_to_network();
Say we want to have objects implementations switched at runtime, we'd do something like this:
pub trait Methods {
fn func(&self);
}
pub struct Methods_0;
impl Methods for Methods_0 {
fn func(&self) {
println!("foo");
}
}
pub struct Methods_1;
impl Methods for Methods_1 {
fn func(&self) {
println!("bar");
}
}
pub struct Object<'a> { //'
methods: &'a (Methods + 'a),
}
fn main() {
let methods: [&Methods; 2] = [&Methods_0, &Methods_1];
let mut obj = Object { methods: methods[0] };
obj.methods.func();
obj.methods = methods[1];
obj.methods.func();
}
Now, what if there are hundreds of such implementations? E.g. imagine implementations of cards for collectible card game where every card does something completely different and is hard to generalize; or imagine implementations for opcodes for a huge state machine. Sure you can argue that a different design pattern can be used -- but that's not the point of this question...
Wonder if there is any way for these Impl structs to somehow "register" themselves so they can be looked up later by a factory method? I would be happy to end up with a magical macro or even a plugin to accomplish that.
Say, in D you can use templates to register the implementations -- and if you can't for some reason, you can always inspect modules at compile-time and generate new code via mixins; there are also user-defined attributes that can help in this. In Python, you would normally use a metaclass so that every time a new child class is created, a ref to it is stored in the metaclass's registry which allows you to look up implementations by name or parameter; this can also be done via decorators if implementations are simple functions.
Ideally, in the example above you would be able to create Object as
Object::new(0)
where the value 0 is only known at runtime and it would magically return you an Object { methods: &Methods_0 }, and the body of new() would not have the implementations hard-coded like so "methods: [&Methods; 2] = [&Methods_0, &Methods_1]", instead it should be somehow inferred automatically.
So, this is probably extremely buggy, but it works as a proof of concept.
It is possible to use Cargo's code generation support to make the introspection at compile-time, by parsing (not exactly parsing in this case, but you get the idea) the present implementations, and generating the boilerplate necessary to make Object::new() work.
The code is pretty convoluted and has no error handling whatsoever, but works.
Tested on rustc 1.0.0-dev (2c0535421 2015-02-05 15:22:48 +0000)
(See on github)
src/main.rs:
pub mod implementations;
mod generated_glue {
include!(concat!(env!("OUT_DIR"), "/generated_glue.rs"));
}
use generated_glue::Object;
pub trait Methods {
fn func(&self);
}
pub struct Methods_2;
impl Methods for Methods_2 {
fn func(&self) {
println!("baz");
}
}
fn main() {
Object::new(2).func();
}
src/implementations.rs:
use super::Methods;
pub struct Methods_0;
impl Methods for Methods_0 {
fn func(&self) {
println!("foo");
}
}
pub struct Methods_1;
impl Methods for Methods_1 {
fn func(&self) {
println!("bar");
}
}
build.rs:
#![feature(core, unicode, path, io, env)]
use std::env;
use std::old_io::{fs, File, BufferedReader};
use std::collections::HashMap;
fn main() {
let target_dir = Path::new(env::var_string("OUT_DIR").unwrap());
let mut target_file = File::create(&target_dir.join("generated_glue.rs")).unwrap();
let source_code_path = Path::new(file!()).join_many(&["..", "src/"]);
let source_files = fs::readdir(&source_code_path).unwrap().into_iter()
.filter(|path| {
match path.str_components().last() {
Some(Some(filename)) => filename.split('.').last() == Some("rs"),
_ => false
}
});
let mut implementations = HashMap::new();
for source_file_path in source_files {
let relative_path = source_file_path.path_relative_from(&source_code_path).unwrap();
let source_file_name = relative_path.as_str().unwrap();
implementations.insert(source_file_name.to_string(), vec![]);
let mut file_implementations = &mut implementations[*source_file_name];
let mut source_file = BufferedReader::new(File::open(&source_file_path).unwrap());
for line in source_file.lines() {
let line_str = match line {
Ok(line_str) => line_str,
Err(_) => break,
};
if line_str.starts_with("impl Methods for Methods_") {
const PREFIX_LEN: usize = 25;
let number_len = line_str[PREFIX_LEN..].chars().take_while(|chr| {
chr.is_digit(10)
}).count();
let number: i32 = line_str[PREFIX_LEN..(PREFIX_LEN + number_len)].parse().unwrap();
file_implementations.push(number);
}
}
}
writeln!(&mut target_file, "use super::Methods;").unwrap();
for (source_file_name, impls) in &implementations {
let module_name = match source_file_name.split('.').next() {
Some("main") => "super",
Some(name) => name,
None => panic!(),
};
for impl_number in impls {
writeln!(&mut target_file, "use {}::Methods_{};", module_name, impl_number).unwrap();
}
}
let all_impls = implementations.values().flat_map(|impls| impls.iter());
writeln!(&mut target_file, "
pub struct Object;
impl Object {{
pub fn new(impl_number: i32) -> Box<Methods + 'static> {{
match impl_number {{
").unwrap();
for impl_number in all_impls {
writeln!(&mut target_file,
" {} => Box::new(Methods_{}),", impl_number, impl_number).unwrap();
}
writeln!(&mut target_file, "
_ => panic!(\"Unknown impl number: {{}}\", impl_number),
}}
}}
}}").unwrap();
}
The generated code:
use super::Methods;
use super::Methods_2;
use implementations::Methods_0;
use implementations::Methods_1;
pub struct Object;
impl Object {
pub fn new(impl_number: i32) -> Box<Methods + 'static> {
match impl_number {
2 => Box::new(Methods_2),
0 => Box::new(Methods_0),
1 => Box::new(Methods_1),
_ => panic!("Unknown impl number: {}", impl_number),
}
}
}