From 4f8a1636c15e4831c897be2ef596a9799f11a9cc Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 9 Apr 2024 17:03:41 -0400 Subject: [PATCH] Set up a view model for the game review and highlight current node --- otg/core/src/lib.rs | 3 + otg/core/src/types.rs | 102 +++-------------------- otg/core/src/view_models/game_review.rs | 105 ++++++++++++++++++++++++ otg/core/src/view_models/mod.rs | 20 +++++ otg/gtk/src/app_window.rs | 11 ++- otg/gtk/src/components/goban.rs | 45 +--------- otg/gtk/src/components/review_tree.rs | 90 +++++++------------- otg/gtk/src/lib.rs | 7 +- otg/gtk/src/main.rs | 2 +- otg/gtk/src/views/game_review.rs | 69 +++++++++++----- sgf/src/game.rs | 12 ++- 11 files changed, 244 insertions(+), 222 deletions(-) create mode 100644 otg/core/src/view_models/game_review.rs create mode 100644 otg/core/src/view_models/mod.rs diff --git a/otg/core/src/lib.rs b/otg/core/src/lib.rs index 5964ce9..996e642 100644 --- a/otg/core/src/lib.rs +++ b/otg/core/src/lib.rs @@ -32,3 +32,6 @@ mod types; pub use types::{ BoardError, Color, Config, ConfigOption, DepthTree, LibraryPath, Player, Rank, Size, }; + +mod view_models; +pub use view_models::GameReviewViewModel; diff --git a/otg/core/src/types.rs b/otg/core/src/types.rs index c89b55c..cc9147b 100644 --- a/otg/core/src/types.rs +++ b/otg/core/src/types.rs @@ -1,6 +1,7 @@ use crate::goban::{Coordinate, Goban}; use config::define_config; use config_derive::ConfigOption; +use nary_tree::NodeRef; use serde::{Deserialize, Serialize}; use sgf::GameTree; use std::{ @@ -252,6 +253,12 @@ impl Deref for DepthTree { } } +impl Default for DepthTree { + fn default() -> Self { + Self(nary_tree::Tree::new()) + } +} + #[derive(Debug)] pub struct SizeNode { /// Use this to map back to the node in the original game tree. This way we know how to @@ -273,32 +280,9 @@ impl SizeNode { } impl DepthTree { - // My previous work to convert from a node tree to this tree-with-width dependend on the node tree - // being a recursive data structure. Now I need to find a way to convert a slab tree to this width - // tree. - // - // It all feels like a lot of custom weirdness. I shouldn't need a bunch of custom data structures, - // so I want to eliminate the "Tree" above and keep using the slab tree. I think I should be able - // to build these Node objects without needing a custom data structure. - fn new() -> Self { - Self(nary_tree::Tree::new()) - /* - Tree { - nodes: vec![Node { - id: 0, - node: root, - parent: None, - depth: 0, - width: RefCell::new(None), - children: vec![], - }], - } - */ - } - /* pub fn node(&self, idx: usize) -> &T { - &self.nodes[idx].node + &self.nodes[idx].content } // Add a node to the parent specified by parent_idx. Return the new index. This cannot be used @@ -309,7 +293,7 @@ impl DepthTree { self.nodes.push(Node { id: next_idx, - node, + content: node, parent: Some(parent_idx), depth: parent.depth + 1, width: RefCell::new(None), @@ -487,7 +471,7 @@ impl<'a> From<&'a GameTree> for DepthTree { Self(tree) } - None => Self::new(), + None => Self::default(), } } } @@ -529,7 +513,7 @@ pub struct BFSIter<'a, T> { } impl<'a, T> Iterator for BFSIter<'a, T> { - type Item = &'a T; + type Item = NodeRef<'a, T>; fn next(&mut self) -> Option { let retval = self.queue.pop_front(); @@ -538,7 +522,7 @@ impl<'a, T> Iterator for BFSIter<'a, T> { .children() .for_each(|noderef| self.queue.push_back(noderef)); } - retval.map(|retval| retval.data()) + retval } } @@ -732,66 +716,4 @@ mod test { assert_eq!(tree.position(test_tree.node_g), (1, 4)); */ } - - #[ignore] - #[test] - fn breadth_first_iter() { - /* - let mut node_a = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_b = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_c = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_d = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_e = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_f = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_g = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_h = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_i = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - - let game = GameRecord::new( - GameType::Go, - Size { - width: 19, - height: 19, - }, - Player { - name: Some("Black".to_owned()), - rank: None, - team: None, - }, - Player { - name: Some("White".to_owned()), - rank: None, - team: None, - }, - ); - - node_c.children.push(GameNode::MoveNode(node_d.clone())); - node_c.children.push(GameNode::MoveNode(node_e.clone())); - node_c.children.push(GameNode::MoveNode(node_f.clone())); - - node_b.children.push(GameNode::MoveNode(node_c.clone())); - - node_h.children.push(GameNode::MoveNode(node_i.clone())); - - node_a.children.push(GameNode::MoveNode(node_b.clone())); - node_a.children.push(GameNode::MoveNode(node_g.clone())); - node_a.children.push(GameNode::MoveNode(node_h.clone())); - - let game_tree = GameNode::MoveNode(node_a.clone()); - - let tree = Tree::from(&game_tree); - - let mut iter = tree.bfs_iter(); - - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_a.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_b.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_g.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_h.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_c.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_i.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_d.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_e.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_f.id)); - */ - } } diff --git a/otg/core/src/view_models/game_review.rs b/otg/core/src/view_models/game_review.rs new file mode 100644 index 0000000..46f2675 --- /dev/null +++ b/otg/core/src/view_models/game_review.rs @@ -0,0 +1,105 @@ +/* +Copyright 2024, Savanni D'Gerinel + +This file is part of On the Grid. + +On the Grid is free software: you can redistribute it and/or modify it under the terms of +the GNU General Public License as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +On the Grid is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with On the Grid. If not, see . +*/ + +// Currenty my game review is able to show the current game and its tree. Now, I want to start +// tracking where in the tree that I am. This should be a combination of the abstract Tree and the +// gameTree. Chances are, if I just keep track of where I am in the abstract tree, I can find the +// relevant node in the game tree and then reproduce the line to get to that node. +// +// Moving through the game review tree shouldn't require a full invocatian. This object, and most +// other view models, should be exported to the UI. + +use crate::{types::SizeNode, DepthTree, Goban}; +use nary_tree::{NodeId, NodeRef}; +use sgf::{GameRecord, Player}; +use std::sync::{Arc, RwLock}; + +struct GameReviewViewModelPrivate { + current_position: Option, + game: GameRecord, + review_tree: DepthTree, +} + +#[derive(Clone)] +pub struct GameReviewViewModel { + imp: Arc>, +} + +impl GameReviewViewModel { + pub fn new(game: GameRecord) -> Self { + let (review_tree, current_position) = if !game.trees.is_empty() { + let review_tree = DepthTree::from(&game.trees[0]); + let current_position = game.mainline().unwrap().last().map(|nr| nr.node_id()); + (review_tree, current_position) + } else { + (DepthTree::default(), None) + }; + + Self { + imp: Arc::new(RwLock::new(GameReviewViewModelPrivate { + current_position, + game, + review_tree, + })), + } + } + + pub fn black_player(&self) -> Player { + self.imp.read().unwrap().game.black_player.clone() + } + + pub fn white_player(&self) -> Player { + self.imp.read().unwrap().game.white_player.clone() + } + + pub fn game_view(&self) -> Goban { + let imp = self.imp.read().unwrap(); + let mainline = imp.game.mainline(); + match mainline { + Some(mainline) => Goban::default() + .apply_moves(mainline.map(|nr| nr.data())) + .unwrap(), + None => Goban::default(), + } + } + + pub fn map_tree(&self, f: F) + where + F: Fn(NodeRef<'_, SizeNode>, Option), + { + let imp = self.imp.read().unwrap(); + + for node in imp.review_tree.bfs_iter() { + f(node, imp.current_position); + } + } + + pub fn move_forward(&self) { + unimplemented!() + } + + pub fn move_backward(&self) { + unimplemented!() + } + + pub fn next_variant(&self) { + unimplemented!() + } + + pub fn previous_variant(&self) { + unimplemented!() + } +} diff --git a/otg/core/src/view_models/mod.rs b/otg/core/src/view_models/mod.rs new file mode 100644 index 0000000..77317e7 --- /dev/null +++ b/otg/core/src/view_models/mod.rs @@ -0,0 +1,20 @@ +/* +Copyright 2024, Savanni D'Gerinel + +This file is part of On the Grid. + +On the Grid is free software: you can redistribute it and/or modify it under the terms of +the GNU General Public License as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +On the Grid is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with On the Grid. If not, see . +*/ + + +mod game_review; +pub use game_review::GameReviewViewModel; + diff --git a/otg/gtk/src/app_window.rs b/otg/gtk/src/app_window.rs index b40c5e6..8526cd6 100644 --- a/otg/gtk/src/app_window.rs +++ b/otg/gtk/src/app_window.rs @@ -19,10 +19,10 @@ use adw::prelude::*; use otg_core::{ settings::{SettingsRequest, SettingsResponse}, - CoreRequest, CoreResponse, + CoreRequest, CoreResponse, GameReviewViewModel, }; use sgf::GameRecord; -use std::{rc::Rc, sync::{Arc, RwLock}}; +use std::sync::{Arc, RwLock}; use crate::views::{GameReview, HomeView, SettingsView}; @@ -91,13 +91,16 @@ impl AppWindow { pub fn open_game_review(&self, game_record: GameRecord) { let header = adw::HeaderBar::new(); - let game_review = GameReview::new(self.core.clone(), game_record, self.resources.clone()); + let game_review = GameReview::new( + GameReviewViewModel::new(game_record), + self.resources.clone(), + ); let layout = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) .build(); layout.append(&header); - layout.append(&game_review); + layout.append(&game_review.widget()); let page = adw::NavigationPage::builder() .can_pop(true) diff --git a/otg/gtk/src/components/goban.rs b/otg/gtk/src/components/goban.rs index 85d52fe..5b2e524 100644 --- a/otg/gtk/src/components/goban.rs +++ b/otg/gtk/src/components/goban.rs @@ -37,16 +37,10 @@ You should have received a copy of the GNU General Public License along with On use crate::{perftrace, Resource, ResourceManager}; -use gio::resources_lookup_data; use glib::Object; -use gtk::{ - gdk_pixbuf::{Colorspace, InterpType, Pixbuf}, - prelude::*, - subclass::prelude::*, -}; -use image::{io::Reader as ImageReader, ImageError}; +use gtk::{gdk_pixbuf::Pixbuf, prelude::*, subclass::prelude::*}; use otg_core::{Color, Coordinate}; -use std::{cell::RefCell, io::Cursor, rc::Rc}; +use std::{cell::RefCell, rc::Rc}; const WIDTH: i32 = 800; const HEIGHT: i32 = 800; @@ -257,11 +251,11 @@ impl Pen { let (x_loc, y_loc) = self.stone_location(row, col); match color { Color::White => match self.white_stone { - Some(ref white_stone) => ctx.set_source_pixbuf(&white_stone, x_loc, y_loc), + Some(ref white_stone) => ctx.set_source_pixbuf(white_stone, x_loc, y_loc), None => ctx.set_source_rgb(0.9, 0.9, 0.9), }, Color::Black => match self.black_stone { - Some(ref black_stone) => ctx.set_source_pixbuf(&black_stone, x_loc, y_loc), + Some(ref black_stone) => ctx.set_source_pixbuf(black_stone, x_loc, y_loc), None => ctx.set_source_rgb(0.0, 0.0, 0.0), }, } @@ -309,34 +303,3 @@ impl Pen { ) } } - -fn load_pixbuf( - path: &str, - transparency: bool, - width: i32, - height: i32, -) -> Result, ImageError> { - let image_bytes = resources_lookup_data(path, gio::ResourceLookupFlags::NONE).unwrap(); - - let image = ImageReader::new(Cursor::new(image_bytes)) - .with_guessed_format() - .unwrap() - .decode(); - image.map(|image| { - let stride = if transparency { - image.to_rgba8().sample_layout().height_stride - } else { - image.to_rgb8().sample_layout().height_stride - }; - Pixbuf::from_bytes( - &glib::Bytes::from(image.as_bytes()), - Colorspace::Rgb, - transparency, - 8, - image.width() as i32, - image.height() as i32, - stride as i32, - ) - .scale_simple(width, height, InterpType::Nearest) - }) -} diff --git a/otg/gtk/src/components/review_tree.rs b/otg/gtk/src/components/review_tree.rs index c2297ff..420dcdc 100644 --- a/otg/gtk/src/components/review_tree.rs +++ b/otg/gtk/src/components/review_tree.rs @@ -15,50 +15,29 @@ You should have received a copy of the GNU General Public License along with On */ use cairo::Context; -use glib::Object; -use gtk::{prelude::*, subclass::prelude::*}; -use otg_core::DepthTree; -use sgf::GameRecord; -use std::{cell::RefCell, rc::Rc}; -use uuid::Uuid; +use gtk::prelude::*; +use otg_core::GameReviewViewModel; const WIDTH: i32 = 200; const HEIGHT: i32 = 800; -#[derive(Default)] -pub struct ReviewTreePrivate { - record: Rc>>, - tree: Rc>>, -} +#[derive(Clone)] +pub struct ReviewTree { + widget: gtk::DrawingArea, -#[glib::object_subclass] -impl ObjectSubclass for ReviewTreePrivate { - const NAME: &'static str = "ReviewTree"; - type Type = ReviewTree; - type ParentType = gtk::DrawingArea; -} - -impl ObjectImpl for ReviewTreePrivate {} -impl WidgetImpl for ReviewTreePrivate {} -impl DrawingAreaImpl for ReviewTreePrivate {} - -glib::wrapper! { - pub struct ReviewTree(ObjectSubclass) @extends gtk::Widget, gtk::DrawingArea; + view: GameReviewViewModel, } impl ReviewTree { - pub fn new(record: GameRecord) -> Self { - let s: Self = Object::new(); + pub fn new(view: GameReviewViewModel) -> ReviewTree { + let widget = gtk::DrawingArea::new(); - // TODO: there can be more than one tree, especially in instructional files. Either unify - // them into a single tree in the GameTree, or draw all of them here. - *s.imp().tree.borrow_mut() = Some(DepthTree::from(&record.trees[0])); - *s.imp().record.borrow_mut() = Some(record); + widget.set_width_request(WIDTH); + widget.set_height_request(HEIGHT); - s.set_width_request(WIDTH); - s.set_height_request(HEIGHT); + let s = Self { widget, view }; - s.set_draw_func({ + s.widget.set_draw_func({ let s = s.clone(); move |_, ctx, width, height| { s.redraw(ctx, width, height); @@ -68,27 +47,26 @@ impl ReviewTree { s } - pub fn redraw(&self, ctx: &Context, _width: i32, _height: i32) { - let tree: &Option = &self.imp().tree.borrow(); - match tree { - Some(ref tree) => { - for node in tree.bfs_iter() { - // draw a circle given the coordinates of the nodes - // I don't know the indent. How do I keep track of that? Do I track the position of - // the parent? do I need to just make it more intrinsically a part of the position - // code? - ctx.set_source_rgb(0.7, 0.7, 0.7); - let (row, column) = node.position(); - let y = (row as f64) * 20. + 10.; - let x = (column as f64) * 20. + 10.; - ctx.arc(x, y, 5., 0., 2. * std::f64::consts::PI); - let _ = ctx.stroke(); - } + fn redraw(&self, ctx: &Context, _width: i32, _height: i32) { + self.view.map_tree(move |node, current| { + ctx.set_source_rgb(0.7, 0.7, 0.7); + let (row, column) = node.data().position(); + let y = (row as f64) * 20. + 10.; + let x = (column as f64) * 20. + 10.; + ctx.arc(x, y, 5., 0., 2. * std::f64::consts::PI); + + if current == Some(node.node_id()) { + ctx.set_line_width(3.); + } else { + ctx.set_line_width(1.); } - None => { - // if there is no tree present, then there's nothing to draw! - } - } + + let _ = ctx.stroke(); + }); + } + + pub fn widget(&self) -> gtk::Widget { + self.widget.clone().upcast::() } } @@ -125,12 +103,6 @@ impl ReviewTree { // A bottom-up traversal: // - Figure out the number of nodes at each depth -/* -struct Tree { - width: Vec, // the total width of the tree at each depth -} -*/ - #[cfg(test)] mod test { use super::*; diff --git a/otg/gtk/src/lib.rs b/otg/gtk/src/lib.rs index 7fe1471..91c97f1 100644 --- a/otg/gtk/src/lib.rs +++ b/otg/gtk/src/lib.rs @@ -49,8 +49,8 @@ pub struct ResourceManager { resources: Rc>>, } -impl ResourceManager { - pub fn new() -> Self { +impl Default for ResourceManager { + fn default() -> Self { let mut resources = HashMap::new(); for (path, xres, yres, transparency) in [ @@ -88,7 +88,9 @@ impl ResourceManager { resources: Rc::new(RefCell::new(resources)), } } +} +impl ResourceManager { pub fn resource(&self, path: &str) -> Option { self.resources.borrow().get(path).cloned() } @@ -123,7 +125,6 @@ impl ResourceManager { .scale_simple(width, height, InterpType::Nearest) }) } - } pub fn perftrace(trace_name: &str, f: F) -> A diff --git a/otg/gtk/src/main.rs b/otg/gtk/src/main.rs index 6035d16..292ccdc 100644 --- a/otg/gtk/src/main.rs +++ b/otg/gtk/src/main.rs @@ -122,7 +122,7 @@ fn main() { app.connect_activate({ move |app| { - let resources = ResourceManager::new(); + let resources = ResourceManager::default(); let core_api = CoreApi { core: core.clone() }; let app_window = AppWindow::new(app, core_api, resources); diff --git a/otg/gtk/src/views/game_review.rs b/otg/gtk/src/views/game_review.rs index 101c53c..510e586 100644 --- a/otg/gtk/src/views/game_review.rs +++ b/otg/gtk/src/views/game_review.rs @@ -22,16 +22,15 @@ You should have received a copy of the GNU General Public License along with On // I'll get all of the information about the game from the core, and then render everything in the // UI. So this will be a heavy lift on the UI side. -use crate::{ - components::{Goban, PlayerCard, ReviewTree}, CoreApi, ResourceManager -}; -use glib::Object; -use gtk::{prelude::*, subclass::prelude::*}; -use otg_core::Color; -use sgf::GameRecord; +use crate::{components::{Goban, PlayerCard, ReviewTree}, ResourceManager}; +use gtk::{prelude::*}; +use otg_core::{Color, GameReviewViewModel}; +/* #[derive(Default)] -pub struct GameReviewPrivate {} +pub struct GameReviewPrivate { + model: Rc>>, +} #[glib::object_subclass] impl ObjectSubclass for GameReviewPrivate { @@ -52,14 +51,40 @@ impl GameReview { pub fn new(_api: CoreApi, record: GameRecord, resources: ResourceManager) -> Self { let s: Self = Object::builder().build(); + + s + } +} +*/ + +pub struct GameReview { + widget: gtk::Box, + + resources: ResourceManager, + view: GameReviewViewModel, +} + +impl GameReview { + pub fn new(view: GameReviewViewModel, resources: ResourceManager) -> Self { + let widget = gtk::Box::builder().build(); + + let s = Self { + widget, + resources, + view, + }; + + s.render(); + + s + } + + fn render(&self) { // It's actually really bad to be just throwing away errors. Panics make everyone unhappy. // This is not a fatal error, so I'll replace this `unwrap` call with something that // renders the board and notifies the user of a problem that cannot be resolved. - let board_repr = match record.mainline() { - Some(iter) => otg_core::Goban::default().apply_moves(iter).unwrap(), - None => otg_core::Goban::default(), - }; - let board = Goban::new(board_repr, resources); + let board_repr = self.view.game_view(); + let board = Goban::new(board_repr, self.resources.clone()); /* s.attach(&board, 0, 0, 2, 2); @@ -76,7 +101,7 @@ impl GameReview { // The review tree needs to know the record for being able to render all of the nodes. Once // keyboard input is being handled, the tree will have to be updated on each keystroke in // order to show the user where they are within the game record. - let review_tree = ReviewTree::new(record.clone()); + let review_tree = ReviewTree::new(self.view.clone()); // I think most keyboard focus is going to end up being handled here in GameReview, as // keystrokes need to affect both the goban and the review tree simultanesouly. Possibly @@ -88,14 +113,18 @@ impl GameReview { .spacing(4) .build(); - player_information_section.append(&PlayerCard::new(Color::Black, &record.black_player)); - player_information_section.append(&PlayerCard::new(Color::White, &record.white_player)); + player_information_section + .append(&PlayerCard::new(Color::Black, &self.view.black_player())); + player_information_section + .append(&PlayerCard::new(Color::White, &self.view.white_player())); - s.append(&board); + self.widget.append(&board); sidebar.append(&player_information_section); - sidebar.append(&review_tree); - s.append(&sidebar); + sidebar.append(&review_tree.widget()); + self.widget.append(&sidebar); + } - s + pub fn widget(&self) -> gtk::Widget { + self.widget.clone().upcast::() } } diff --git a/sgf/src/game.rs b/sgf/src/game.rs index d21812a..adc751a 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -2,8 +2,8 @@ use crate::{ parser::{self, Annotation, Evaluation, Move, SetupInstr, Size, UnknownProperty}, Color, Date, GameResult, GameType, }; -use serde::{Deserialize, Serialize}; use nary_tree::{NodeId, NodeMut, NodeRef, Tree}; +use serde::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet, VecDeque}, fmt, @@ -136,7 +136,7 @@ impl GameRecord { /// Generate a list of moves which constitute the main line of the game. This is the game as it /// was actually played out, and by convention consists of the first node in each list of /// children. - pub fn mainline(&self) -> Option> { + pub fn mainline(&self) -> Option>> { if !self.trees.is_empty() { Some(MainlineIter { next: self.trees[0].root(), @@ -405,7 +405,7 @@ pub struct MainlineIter<'a> { } impl<'a> Iterator for MainlineIter<'a> { - type Item = &'a GameNode; + type Item = NodeRef<'a, GameNode>; fn next(&mut self) -> Option { if let Some(next) = self.next.take() { @@ -413,7 +413,7 @@ impl<'a> Iterator for MainlineIter<'a> { self.next = next .first_child() .and_then(|child| self.tree.get(child.node_id())); - Some(ret.data()) + Some(ret) } else { None } @@ -634,6 +634,7 @@ mod test { assert_eq!(tree.nodes().len(), 0); } + /* #[test] fn it_can_add_moves_to_a_game() { let mut game = GameRecord::new( @@ -660,6 +661,7 @@ mod test { assert_eq!(nodes[1].id(), second_move.id); */ } +*/ #[ignore] #[test] @@ -820,6 +822,7 @@ mod path_test { let moves = game .mainline() .expect("there should be a mainline in this file") + .map(|nr| nr.data()) .collect::>(); assert_matches!(moves[0], GameNode::MoveNode(node) => { assert_eq!(node.color, Color::Black); @@ -845,6 +848,7 @@ mod path_test { let moves = game .mainline() .expect("there should be a mainline in this file") + .map(|nr| nr.data()) .collect::>(); assert_matches!(moves[1], GameNode::MoveNode(node) => { assert_eq!(node.color, Color::White);