<commit_before>extern crate objc;

use super::consts::*;
use super::cocoa_util::*;
use pref::*;
use core_graphics::*;
use tickeys::Tickeys;
use std::sync::{ONCE_INIT, Once};

use objc::runtime::*;
use cocoa::base::{class,id,nil};
use cocoa::foundation::NSString;
use cocoa::appkit::NSApp;

// naive way of make this a singleton
static mut SHOWING_GUI:bool = false;

#[allow(non_snake_case)]
#[allow(unused_variables)]
pub trait SettingsController //<NSWindowDelegate, NSTableViewDelegate, NSTableViewDataSource>
{
	fn get_instance(_: Self, ptr_to_app: *mut Tickeys) -> id
	{
		Self::__register_objc_class_once();

	    unsafe
	    {
	    	if SHOWING_GUI { return nil };

			let nib_name = NSString::alloc(nil).init_str("Settings");
			let inst: id = msg_send![class(stringify!(SettingsController)), alloc];
	       	let inst: id = msg_send![inst, initWithWindowNibName: nib_name];
	       	inst.retain();

	       	let _:id = msg_send![inst, setUser_data: ptr_to_app];
			let _: id = msg_send![inst, showWindow: nil];

			SHOWING_GUI = true;
	       	inst
    	}
	}

	fn __register_objc_class_once()
	{
		static REGISTER_APPDELEGATE: Once = ONCE_INIT;
		REGISTER_APPDELEGATE.call_once(||
		{
			println!("SettingsController::__register_objc_class_once");
			let superCls = objc::runtime::Class::get("NSWindowController").unwrap();
			let mut decl = objc::declare::ClassDecl::new(superCls, stringify!(SettingsController)).unwrap();

			decl_prop!(decl, usize, user_data);
			decl_prop!(decl, id, popup_audio_scheme);
			decl_prop!(decl, id, slide_volume);
			decl_prop!(decl, id, slide_pitch);
			decl_prop!(decl, id, label_version);
			decl_prop!(decl, id, filterListTable);

			unsafe
			{
				//methods
				decl.add_method(sel!(quit:), Self::quit_ as extern fn(&mut Object, Sel, id));
				decl.add_method(sel!(value_changed:), Self::value_changed_ as extern fn(&mut Object, Sel, id));
				decl.add_method(sel!(follow_link:), Self::follow_link_ as extern fn(&mut Object, Sel, id));
				decl.add_method(sel!(windowWillClose:), Self::windowWillClose as extern fn(&Object, Sel, id));
				decl.add_method(sel!(windowDidLoad), Self::windowDidLoad as extern fn(&mut Object, Sel));
				decl.add_method(sel!(tableView:shouldEditTableColumn:row:), Self::tableViewShouldEditTableColumnRow as extern fn(&mut Object, Sel, id, id, i32) -> bool);

				decl.add_method(sel!(btnAddClicked:), Self::btnAddClicked as extern fn(&mut Object, Sel, id));
				decl.add_method(sel!(btnRemoveClicked:), Self::btnRemoveClicked as extern fn(&mut Object, Sel, id));

				//tableViewSource & tableViewDelegate
				decl.add_method(sel!(numberOfRowsInTableView:), Self::numberOfRowsInTableView as extern fn(&mut Object, Sel, id)->i32);
				decl.add_method(sel!(tableView:objectValueForTableColumn:row:), Self::tableViewObjectValueForTableColumn as extern fn(&mut Object, Sel, id, id, i32)->id);

			}

			decl.register();
		});
	}

	extern fn windowDidLoad(this: &mut Object, _cmd: Sel)
	{
		println!("windowDidLoad");
		unsafe
		{
			let window: id = msg_send![this, window];

			//hide window btns
			let btnMin: id = msg_send![window, standardWindowButton:1];
			let _: id = msg_send![btnMin, setHidden: true];
			let btnZoom: id = msg_send![window, standardWindowButton:2];
			let _: id = msg_send![btnZoom, setHidden: true];

			let _: id = msg_send![window, setLevel: CGWindowLevelForKey(CGWindowLevelKey::kCGFloatingWindowLevelKey)];

			Self::load_values(this);
		}

	}

	extern fn quit_(this: &mut Object, _cmd: Sel, sender: id)
	{
		println!("Quit");
		app_terminate();
	}

	extern fn follow_link_(this: &mut Object, _cmd: Sel, sender: id)
	{
		unsafe
		{
			let tag:i64 = msg_send![sender, tag];
			let url = match tag
			{
				0 => WEBSITE,
				1 => DONATE_URL,
				_ => panic!("SettingsController::follow_link_")
			};

			let workspace: id = msg_send![class("NSWorkspace"), sharedWorkspace];
			let url:id = msg_send![class("NSURL"),
			URLWithString: NSString::alloc(nil).init_str(url)];

			msg_send![workspace, openURL: url]
		}
	}

	extern fn value_changed_(this: &mut Object, _cmd:Sel, sender: id)
	{
		println!("SettingsController::value_changed_");

		const TAG_POPUP_SCHEME: i64 = 0;
		const TAG_SLIDE_VOLUME: i64 = 1;
		const TAG_SLIDE_PITCH: i64 = 2;

		unsafe
		{
			let user_defaults: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
			let tickeys_ptr:&mut Tickeys = msg_send![this, user_data];
			let tag:i64 = msg_send![sender, tag];

			match tag
			{
				TAG_POPUP_SCHEME =>
				{

					let value:i32 = msg_send![sender, indexOfSelectedItem];

					let sch;// = &schemes[value as usize];
					{
						let schemes = tickeys_ptr.get_schemes();//= load_audio_schemes();
						sch = schemes[value as usize].name.clone();
					}

					let scheme_dir = "data/".to_string() + &sch;//.to_string();
					//scheme_dir.push_str(&sch.name);
					tickeys_ptr.load_scheme(&get_res_path(&scheme_dir), &sch);

					let _:id = msg_send![user_defaults,
						setObject: NSString::alloc(nil).init_str(sch.as_ref())
						forKey: NSString::alloc(nil).init_str("audio_scheme")];
				},

				TAG_SLIDE_VOLUME =>
				{
					let value:f32 = msg_send![sender, floatValue];
					tickeys_ptr.set_volume(value);

					let _:id = msg_send![user_defaults, setFloat: value
						forKey: NSString::alloc(nil).init_str("volume")];
				},

				TAG_SLIDE_PITCH =>
				{
					let mut value:f32 = msg_send![sender, floatValue];
					if value > 1f32
					{
						//just map [1, 1.5] -> [1, 2]
						value = value * (2.0f32/1.5f32);
					}
					tickeys_ptr.set_pitch(value);

					let _:id = msg_send![user_defaults, setFloat: value
						forKey: NSString::alloc(nil).init_str("pitch")];
				}

				_ => {panic!("WTF");}
			}
		}

	}

	extern fn windowWillClose(this: &Object, _cmd: Sel, note: id)
	{
		println!("SettingsController::windowWillClose");
		unsafe
		{
			let app_ptr: *mut Tickeys = msg_send![this, user_data];
			SHOWING_GUI = false;

			let user_defaults: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
			let _:id = msg_send![user_defaults, synchronize];
			let _:id = msg_send![this, release];
		}
	}

	extern fn btnAddClicked(this: &mut Object, _cmd: Sel, sender: id)
	{
		println!("Add Item");
		unsafe
		{
			let open: id = msg_send![class("NSOpenPanel"), openPanel];
			let apps_dir: id = msg_send![class("NSURL"), URLWithString: nsstr("/Applications")];
			let allowed_types: id = msg_send![class("NSMutableArray"), arrayWithCapacity:2];
			let _: id = msg_send![allowed_types, addObject: nsstr("app")];

			let _: id = msg_send![open, setDirectoryURL: apps_dir];
			let _: id = msg_send![open, setAllowedFileTypes: allowed_types];
			let _: id = msg_send![open, setAllowsMultipleSelection: true];

			let ret: i32 = msg_send![open, runModal];
			if ret == 1 //Ok
			{
				let files: id = msg_send![open, URLs];
				let n: i32 = msg_send![files, count];

				let filterList: id = Self::filterList();
				for i in 0..n
				{
					let appName: id = nsurl_filename(msg_send![files, objectAtIndex: i]);

					let contains: bool = msg_send![filterList, containsObject: appName];
					if !contains
					{
						let _: id = msg_send![filterList, addObject: appName];
					}
				}

				//reload table ddata
				let filterListTable: id = msg_send![this, filterListTable];
				let _: id = msg_send![filterListTable, reloadData];

				//save
				let ud: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
				let _: id = msg_send![ud, setObject:filterList forKey: nsstr("FilterList")];
			}
		}
	}

	extern fn btnRemoveClicked(this: &mut Object, _cmd: Sel, sender: id)
	{
		println!("Remove Item");
		unsafe
		{
			let filterList = Self::filterList();
			let table: id = msg_send![this, filterListTable];

			let selectedRow: i32 = msg_send![table, selectedRow];

			if selectedRow >= 0
			{
				let _: id = msg_send![filterList, removeObjectAtIndex:selectedRow];
				let _: id = msg_send![table, reloadData];

				let ud: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
				let _: id = msg_send![ud, setObject:filterList forKey: nsstr("FilterList")];
			}
		}
	}

	unsafe fn load_values(this: id)
	{
		println!("loadValues");
		let app_ptr: &mut Tickeys = msg_send![this, user_data];
		let user_defaults: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
		let popup_audio_scheme: id = msg_send![this, popup_audio_scheme];
		let _: id = msg_send![popup_audio_scheme, removeAllItems];

		let schemes = app_ptr.get_schemes();//load_audio_schemes();
		let pref = Pref::load(schemes);

		for i in 0..schemes.len()
		{
			let s = &schemes[i];

			let _: id = msg_send![popup_audio_scheme, addItemWithTitle: l10n_str(&s.display_name)];
			if  *s.name == pref.scheme
			{
				let _:id = msg_send![popup_audio_scheme, selectItemAtIndex:i];
			}
		}

		let slide_volume: id = msg_send![this, slide_volume];
		let _:id = msg_send![slide_volume, setFloatValue: pref.volume];

		let slide_pitch: id = msg_send![this, slide_pitch];
		let value =  if pref.pitch > 1f32
		{
			pref.pitch * (1.5f32/2.0f32)
		} else
		{
			pref.pitch
		};
		let _:id = msg_send![slide_pitch, setFloatValue: value];

		let label_version: id = msg_send![this, label_version];
		let _:id = msg_send![label_version,
			setStringValue: NSString::alloc(nil).init_str(format!("v{}",CURRENT_VERSION).as_ref())];

		//let _:id = msg_send![this, show]

		println!("makeKeyAndOrderFront:");
		let win:id = msg_send![this, window];
		let _:id = msg_send![win, makeKeyAndOrderFront:nil];
		let _:id = msg_send![NSApp(), activateIgnoringOtherApps:true];
	}

	//-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
	extern fn numberOfRowsInTableView(this: &mut Object, _cmd: Sel, tableView: id) -> i32
	{
		unsafe
		{
			msg_send![Self::filterList(), count]
		}
	}

	fn filterList() -> id
	{
		unsafe
		{
			//strong coupling...
			let appDelegate: id = msg_send![NSApp(), delegate];
			msg_send![appDelegate, filterList]
		}
	}

	//-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
	extern fn tableViewObjectValueForTableColumn(this: &mut Object, _cmd: Sel, tableView: id, tableColumn: id, row: i32) -> id
	{
		unsafe
		{
			msg_send![Self::filterList(), objectAtIndex: row]
		}
	}

	//-(BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
	extern fn tableViewShouldEditTableColumnRow(this: &mut Object, _cmd: Sel, tableView: id, tableColumn: id, row: i32) -> bool
	{
		false
	}
}

impl SettingsController for id
{
}
<commit_msg>remove v before version string<commit_after>extern crate objc;

use super::consts::*;
use super::cocoa_util::*;
use pref::*;
use core_graphics::*;
use tickeys::Tickeys;
use std::sync::{ONCE_INIT, Once};

use objc::runtime::*;
use cocoa::base::{class,id,nil};
use cocoa::foundation::NSString;
use cocoa::appkit::NSApp;

// naive way of make this a singleton
static mut SHOWING_GUI:bool = false;

#[allow(non_snake_case)]
#[allow(unused_variables)]
pub trait SettingsController //<NSWindowDelegate, NSTableViewDelegate, NSTableViewDataSource>
{
	fn get_instance(_: Self, ptr_to_app: *mut Tickeys) -> id
	{
		Self::__register_objc_class_once();

	    unsafe
	    {
	    	if SHOWING_GUI { return nil };

			let nib_name = NSString::alloc(nil).init_str("Settings");
			let inst: id = msg_send![class(stringify!(SettingsController)), alloc];
	       	let inst: id = msg_send![inst, initWithWindowNibName: nib_name];
	       	inst.retain();

	       	let _:id = msg_send![inst, setUser_data: ptr_to_app];
			let _: id = msg_send![inst, showWindow: nil];

			SHOWING_GUI = true;
	       	inst
    	}
	}

	fn __register_objc_class_once()
	{
		static REGISTER_APPDELEGATE: Once = ONCE_INIT;
		REGISTER_APPDELEGATE.call_once(||
		{
			println!("SettingsController::__register_objc_class_once");
			let superCls = objc::runtime::Class::get("NSWindowController").unwrap();
			let mut decl = objc::declare::ClassDecl::new(superCls, stringify!(SettingsController)).unwrap();

			decl_prop!(decl, usize, user_data);
			decl_prop!(decl, id, popup_audio_scheme);
			decl_prop!(decl, id, slide_volume);
			decl_prop!(decl, id, slide_pitch);
			decl_prop!(decl, id, label_version);
			decl_prop!(decl, id, filterListTable);

			unsafe
			{
				//methods
				decl.add_method(sel!(quit:), Self::quit_ as extern fn(&mut Object, Sel, id));
				decl.add_method(sel!(value_changed:), Self::value_changed_ as extern fn(&mut Object, Sel, id));
				decl.add_method(sel!(follow_link:), Self::follow_link_ as extern fn(&mut Object, Sel, id));
				decl.add_method(sel!(windowWillClose:), Self::windowWillClose as extern fn(&Object, Sel, id));
				decl.add_method(sel!(windowDidLoad), Self::windowDidLoad as extern fn(&mut Object, Sel));
				decl.add_method(sel!(tableView:shouldEditTableColumn:row:), Self::tableViewShouldEditTableColumnRow as extern fn(&mut Object, Sel, id, id, i32) -> bool);

				decl.add_method(sel!(btnAddClicked:), Self::btnAddClicked as extern fn(&mut Object, Sel, id));
				decl.add_method(sel!(btnRemoveClicked:), Self::btnRemoveClicked as extern fn(&mut Object, Sel, id));

				//tableViewSource & tableViewDelegate
				decl.add_method(sel!(numberOfRowsInTableView:), Self::numberOfRowsInTableView as extern fn(&mut Object, Sel, id)->i32);
				decl.add_method(sel!(tableView:objectValueForTableColumn:row:), Self::tableViewObjectValueForTableColumn as extern fn(&mut Object, Sel, id, id, i32)->id);

			}

			decl.register();
		});
	}

	extern fn windowDidLoad(this: &mut Object, _cmd: Sel)
	{
		println!("windowDidLoad");
		unsafe
		{
			let window: id = msg_send![this, window];

			//hide window btns
			let btnMin: id = msg_send![window, standardWindowButton:1];
			let _: id = msg_send![btnMin, setHidden: true];
			let btnZoom: id = msg_send![window, standardWindowButton:2];
			let _: id = msg_send![btnZoom, setHidden: true];

			let _: id = msg_send![window, setLevel: CGWindowLevelForKey(CGWindowLevelKey::kCGFloatingWindowLevelKey)];

			Self::load_values(this);
		}

	}

	extern fn quit_(this: &mut Object, _cmd: Sel, sender: id)
	{
		println!("Quit");
		app_terminate();
	}

	extern fn follow_link_(this: &mut Object, _cmd: Sel, sender: id)
	{
		unsafe
		{
			let tag:i64 = msg_send![sender, tag];
			let url = match tag
			{
				0 => WEBSITE,
				1 => DONATE_URL,
				_ => panic!("SettingsController::follow_link_")
			};

			let workspace: id = msg_send![class("NSWorkspace"), sharedWorkspace];
			let url:id = msg_send![class("NSURL"),
			URLWithString: NSString::alloc(nil).init_str(url)];

			msg_send![workspace, openURL: url]
		}
	}

	extern fn value_changed_(this: &mut Object, _cmd:Sel, sender: id)
	{
		println!("SettingsController::value_changed_");

		const TAG_POPUP_SCHEME: i64 = 0;
		const TAG_SLIDE_VOLUME: i64 = 1;
		const TAG_SLIDE_PITCH: i64 = 2;

		unsafe
		{
			let user_defaults: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
			let tickeys_ptr:&mut Tickeys = msg_send![this, user_data];
			let tag:i64 = msg_send![sender, tag];

			match tag
			{
				TAG_POPUP_SCHEME =>
				{

					let value:i32 = msg_send![sender, indexOfSelectedItem];

					let sch;// = &schemes[value as usize];
					{
						let schemes = tickeys_ptr.get_schemes();//= load_audio_schemes();
						sch = schemes[value as usize].name.clone();
					}

					let scheme_dir = "data/".to_string() + &sch;//.to_string();
					//scheme_dir.push_str(&sch.name);
					tickeys_ptr.load_scheme(&get_res_path(&scheme_dir), &sch);

					let _:id = msg_send![user_defaults,
						setObject: NSString::alloc(nil).init_str(sch.as_ref())
						forKey: NSString::alloc(nil).init_str("audio_scheme")];
				},

				TAG_SLIDE_VOLUME =>
				{
					let value:f32 = msg_send![sender, floatValue];
					tickeys_ptr.set_volume(value);

					let _:id = msg_send![user_defaults, setFloat: value
						forKey: NSString::alloc(nil).init_str("volume")];
				},

				TAG_SLIDE_PITCH =>
				{
					let mut value:f32 = msg_send![sender, floatValue];
					if value > 1f32
					{
						//just map [1, 1.5] -> [1, 2]
						value = value * (2.0f32/1.5f32);
					}
					tickeys_ptr.set_pitch(value);

					let _:id = msg_send![user_defaults, setFloat: value
						forKey: NSString::alloc(nil).init_str("pitch")];
				}

				_ => {panic!("WTF");}
			}
		}

	}

	extern fn windowWillClose(this: &Object, _cmd: Sel, note: id)
	{
		println!("SettingsController::windowWillClose");
		unsafe
		{
			let app_ptr: *mut Tickeys = msg_send![this, user_data];
			SHOWING_GUI = false;

			let user_defaults: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
			let _:id = msg_send![user_defaults, synchronize];
			let _:id = msg_send![this, release];
		}
	}

	extern fn btnAddClicked(this: &mut Object, _cmd: Sel, sender: id)
	{
		println!("Add Item");
		unsafe
		{
			let open: id = msg_send![class("NSOpenPanel"), openPanel];
			let apps_dir: id = msg_send![class("NSURL"), URLWithString: nsstr("/Applications")];
			let allowed_types: id = msg_send![class("NSMutableArray"), arrayWithCapacity:2];
			let _: id = msg_send![allowed_types, addObject: nsstr("app")];

			let _: id = msg_send![open, setDirectoryURL: apps_dir];
			let _: id = msg_send![open, setAllowedFileTypes: allowed_types];
			let _: id = msg_send![open, setAllowsMultipleSelection: true];

			let ret: i32 = msg_send![open, runModal];
			if ret == 1 //Ok
			{
				let files: id = msg_send![open, URLs];
				let n: i32 = msg_send![files, count];

				let filterList: id = Self::filterList();
				for i in 0..n
				{
					let appName: id = nsurl_filename(msg_send![files, objectAtIndex: i]);

					let contains: bool = msg_send![filterList, containsObject: appName];
					if !contains
					{
						let _: id = msg_send![filterList, addObject: appName];
					}
				}

				//reload table ddata
				let filterListTable: id = msg_send![this, filterListTable];
				let _: id = msg_send![filterListTable, reloadData];

				//save
				let ud: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
				let _: id = msg_send![ud, setObject:filterList forKey: nsstr("FilterList")];
			}
		}
	}

	extern fn btnRemoveClicked(this: &mut Object, _cmd: Sel, sender: id)
	{
		println!("Remove Item");
		unsafe
		{
			let filterList = Self::filterList();
			let table: id = msg_send![this, filterListTable];

			let selectedRow: i32 = msg_send![table, selectedRow];

			if selectedRow >= 0
			{
				let _: id = msg_send![filterList, removeObjectAtIndex:selectedRow];
				let _: id = msg_send![table, reloadData];

				let ud: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
				let _: id = msg_send![ud, setObject:filterList forKey: nsstr("FilterList")];
			}
		}
	}

	unsafe fn load_values(this: id)
	{
		println!("loadValues");
		let app_ptr: &mut Tickeys = msg_send![this, user_data];
		let user_defaults: id = msg_send![class("NSUserDefaults"), standardUserDefaults];
		let popup_audio_scheme: id = msg_send![this, popup_audio_scheme];
		let _: id = msg_send![popup_audio_scheme, removeAllItems];

		let schemes = app_ptr.get_schemes();//load_audio_schemes();
		let pref = Pref::load(schemes);

		for i in 0..schemes.len()
		{
			let s = &schemes[i];

			let _: id = msg_send![popup_audio_scheme, addItemWithTitle: l10n_str(&s.display_name)];
			if  *s.name == pref.scheme
			{
				let _:id = msg_send![popup_audio_scheme, selectItemAtIndex:i];
			}
		}

		let slide_volume: id = msg_send![this, slide_volume];
		let _:id = msg_send![slide_volume, setFloatValue: pref.volume];

		let slide_pitch: id = msg_send![this, slide_pitch];
		let value =  if pref.pitch > 1f32
		{
			pref.pitch * (1.5f32/2.0f32)
		} else
		{
			pref.pitch
		};
		let _:id = msg_send![slide_pitch, setFloatValue: value];

		let label_version: id = msg_send![this, label_version];
		let _:id = msg_send![label_version,
			setStringValue: NSString::alloc(nil).init_str(format!("{}",CURRENT_VERSION).as_ref())];

		//let _:id = msg_send![this, show]

		println!("makeKeyAndOrderFront:");
		let win:id = msg_send![this, window];
		let _:id = msg_send![win, makeKeyAndOrderFront:nil];
		let _:id = msg_send![NSApp(), activateIgnoringOtherApps:true];
	}

	//-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
	extern fn numberOfRowsInTableView(this: &mut Object, _cmd: Sel, tableView: id) -> i32
	{
		unsafe
		{
			msg_send![Self::filterList(), count]
		}
	}

	fn filterList() -> id
	{
		unsafe
		{
			//strong coupling...
			let appDelegate: id = msg_send![NSApp(), delegate];
			msg_send![appDelegate, filterList]
		}
	}

	//-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
	extern fn tableViewObjectValueForTableColumn(this: &mut Object, _cmd: Sel, tableView: id, tableColumn: id, row: i32) -> id
	{
		unsafe
		{
			msg_send![Self::filterList(), objectAtIndex: row]
		}
	}

	//-(BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
	extern fn tableViewShouldEditTableColumnRow(this: &mut Object, _cmd: Sel, tableView: id, tableColumn: id, row: i32) -> bool
	{
		false
	}
}

impl SettingsController for id
{
}
<|endoftext|>


<commit_before>use std::util;
use std::uint;
use std::at_vec::build;
use std::num::{Zero, One};
use std::iterator::IteratorUtil;
use std::vec;
use extra::treemap::TreeMap;
use nalgebra::traits::norm::Norm;
use nalgebra::traits::division_ring::DivisionRing;
use nalgebra::traits::dim::Dim;
use nalgebra::traits::sub_dot::SubDot;
use nalgebra::traits::scalar_op::{ScalarMul, ScalarDiv};
use narrow::algorithm::simplex::Simplex;

#[deriving(Eq)]
pub struct JohnsonSimplex<V, N>
{
  recursion_templates: @[RecursionTemplate],
  points:              ~[V],
  exchange_points:     ~[V],
  cofactors:           ~[N]
}

#[deriving(Eq)]
struct RecursionTemplate
{
  permutation_list: ~[uint],
  offsets:          ~[uint],
  sub_cofactors:    ~[uint],
  num_cofactors:    uint,
  num_leaves:       uint // useful only for printing…
}

impl<V: Copy         +
        SubDot<N>    +
        ScalarMul<N> +
        ScalarDiv<N> +
        Zero         +
        Add<V, V>    +
        Dim,
     N: Ord          +
        Copy         +
        Eq           +
        DivisionRing +
        Ord          +
        Bounded>
JohnsonSimplex<V, N>
{
  pub fn new(initial_point: V) -> JohnsonSimplex<V, N>
  {
    let _dim       = Dim::dim::<V>();
    let perm_list  = JohnsonSimplex::make_permutation_lists::<V, N>();
    let expoints   = vec::with_capacity(_dim);
    let mut points = vec::with_capacity(_dim);

    points.push(initial_point);

    JohnsonSimplex {
      recursion_templates: perm_list
      , points:            points
      , exchange_points:   expoints
      , cofactors:         vec::from_elem(perm_list[_dim].num_cofactors, Zero::zero())
    }
  }

  fn make_permutation_lists() -> @[RecursionTemplate]
  {
    do build |push_to_at_vec|
    {
      for uint::iterate(0u, Dim::dim::<V>() + 1u) |dim|
      {
        push_to_at_vec(
          JohnsonSimplex::make_permutation_list::<V, N>(dim))
      }
    }
  }

  // This is the tricky part of the algorithm. This generates all datas needed
  // to run the johson subalgorithm fastly. This should _not_ be run every time
  // the algorithm is executed. Instead, it should be pre-computed, or computed
  // only once for all. The resulting GC-managed list is intented to be shared
  // between all other simplicis with the same dimension.
  fn make_permutation_list(dim: uint) -> RecursionTemplate
  {
    // FIXME: the permutation list should be computed once for all, at compile
    // time. I dont know how to do that though…

    // The number of points on the biggest subsimplex
    let max_num_points      = dim + 1;

    let mut pts             = ~[]; // the result
    let mut offsets         = ~[];
    let mut sub_cofactors   = ~[];

    // the beginning of the last subsimplices list
    let mut last_dim_begin  = 0;

    // the end of the last subsimplices list
    let mut last_dim_end    = dim + 1 + 1;

    // the number of points of the last subsimplices
    let mut last_num_points = dim + 1;

    let mut map             = TreeMap::new::<~[uint], uint>();

    let mut cofactor_index  = 0;

    for uint::iterate(0, max_num_points) |i|
    { pts.push(i) }

    // initially push the whole simplex (will be removed at the end)
    pts.push(0);

    offsets.push(max_num_points + 1);

    // ... then remove one point each time
    for uint::iterate(0, dim + 1) |i|
    {
      // for each sub-simplex ...
      let mut curr      = last_dim_begin;
      let mut num_added = 0;

      while (curr != last_dim_end)
      {
        // ... iterate on it ...
        for uint::iterate(0, last_num_points) |j|
        {
          // ... and build all the sublist with one last point
          let mut sublist = ~[];

          // then extract the sub-simplex
          for uint::iterate(0, last_num_points) |k|
          {
            // we remove the j'th point
            if pts[curr + j] != pts[curr + k]
            { sublist.push(pts[curr + k]); }
          }

          // keep a trace of the removed point
          sublist.push(pts[curr + j]);

          match map.find(&sublist)
          {
            Some(&v) => sub_cofactors.push(v),
            None     => {
                          for sublist.iter().advance |&e|
                          {
                            pts.push(e);
                            num_added = num_added + 1;
                          }
                          sub_cofactors.push(cofactor_index);
                          map.insert(sublist, cofactor_index);
                          cofactor_index = cofactor_index + 1;
                        }
          }
        }

        let mut parent = ~[];
        for uint::iterate(0, last_num_points + 1) |k|
        { parent.push(pts[curr + k]) }

        match map.find(&parent)
        {
          Some(&p) => sub_cofactors.push(p),
          None => {
            sub_cofactors.push(cofactor_index);
            // There is no need to keep a place for the full simplex cofactor.
            // So we dont increase the cofactor buffer index for the first
            // iteration.
            cofactor_index = cofactor_index + if i == 0 { 0 } else { 1 };
          }
        }

        curr = curr + last_num_points + 1;
      }

      // initialize the next iteration with one less point
      last_dim_begin = last_dim_end ;
      last_dim_end = last_dim_end + num_added;
      offsets.push(last_dim_end);
      last_num_points = last_num_points - 1;
    }

    // cofactor indices for leaves
    for uint::iterate(0, max_num_points) |i|
    { sub_cofactors.push(*map.find(&~[max_num_points - 1 - i]).unwrap()) }

    // end to begin offsets
    offsets.unshift(0u);
    vec::reverse(offsets);
    offsets.pop();

    let rev_offsets = offsets.map(|&e| pts.len() - e);
    let num_leaves = rev_offsets[0];

    // reverse points and cofectors
    vec::reverse(pts);
    vec::reverse(sub_cofactors);

    // remove the full simplex
    let num_pts = pts.len();
    pts.truncate(num_pts - max_num_points - 1);
    sub_cofactors.truncate(num_pts - max_num_points - 1);

    RecursionTemplate {
      offsets:          rev_offsets,
      permutation_list: pts,
      num_cofactors:    sub_cofactors[0] + 1,
      sub_cofactors:    sub_cofactors,
      num_leaves:       num_leaves
    }
  }

  pub fn reset(&mut self, pt: V)
  {
    self.points.clear();
    self.points.push(pt);
  }

  fn do_project_origin(&mut self, reduce: bool) -> V
  {
    // FIXME: for optimization: do special case when there are only 1 point ?
    let _0                   = Zero::zero::<N>();
    let _1                   = One::one::<N>();
    let max_num_pts          = self.points.len();
    let recursion            = &self.recursion_templates[max_num_pts - 1];
    let mut curr_num_pts     = 1u;
    let mut curr             = max_num_pts;

    for uint::iterate(0u, max_num_pts) |i|
    { self.cofactors[recursion.num_cofactors - 1 - i] = copy _1; }

    /*
     * first loop: compute all the cofactors
     */
    let mut recursion_offsets_skip_2 = recursion.offsets.iter();
    recursion_offsets_skip_2.next(); // Skip the two first entries
    recursion_offsets_skip_2.next();
    for recursion_offsets_skip_2.advance |&end|
    {
      // for each sub-simplex ...
      let mut _i = curr;
      while (_i != end)
      {
        let mut cofactor = Zero::zero::<N>();
        let j_pid        = recursion.permutation_list[curr];
        let k_pid        = recursion.permutation_list[curr + 1u];

        // ... with curr_num_pts points ...
        for uint::iterate(0u, curr_num_pts) |i|
        {
          // ... compute its cofactor.
          let i_pid        = recursion.permutation_list[curr + 1 + i];
          let sub_cofactor = copy self.cofactors[recursion.sub_cofactors[curr + 1 + i]];
          let delta = sub_cofactor *
                      self.points[k_pid].sub_dot(&self.points[j_pid],
                                                 &self.points[i_pid]);

          cofactor = cofactor + delta;
        }

        self.cofactors[recursion.sub_cofactors[curr]] = cofactor;
        curr = curr + curr_num_pts + 1; // points + removed point + cofactor id

        _i = _i + curr_num_pts + 1;
      }

      curr_num_pts = curr_num_pts + 1;
    }

    /*
     * second loop: find the subsimplex containing the projection
     */
    let _invalid_cofactor = Bounded::max_value();
    let mut offsets_iter = recursion.offsets.rev_iter();
    offsets_iter.next(); // skip the first offset
    for offsets_iter.advance |&end|
    {
      // for each sub-simplex ...
      let mut _i = curr;
      while (_i != end)
      {
        let mut foundit = true;
        // ... with curr_num_pts points permutations ...
        for uint::iterate(0u, curr_num_pts) |i|
        {
          // ... see if its cofactor is positive
          let cof_id = _i - (i + 1) * curr_num_pts;
          let cof    = copy self.cofactors[recursion.sub_cofactors[cof_id]];
          if cof > _0
          {
            // invalidate the children cofactor
            if curr_num_pts > 1
            {
              let subcofid = recursion.sub_cofactors[cof_id + 1];

              if self.cofactors[subcofid] > _0
              { self.cofactors[subcofid] = copy _invalid_cofactor }
            }

            // dont concider this sub-simplex if it has been invalidated by its
            // parent(s)
            if cof == _invalid_cofactor
            { foundit = false }
          }
          else // we found a negative cofactor: no projection possible here
          { foundit = false }
        }

        if foundit
        {
          // we found a projection!
          // re-run the same iteration but, this time, compute the projection
          let mut total_cof = copy _0;
          let mut proj      = Zero::zero::<V>();

          for uint::iterate(0u, curr_num_pts) |i|
          {
            // ... see if its cofactor is positive
            let id    = _i - (i + 1) * curr_num_pts;
            let cof   = copy self.cofactors[recursion.sub_cofactors[id]];

            total_cof = total_cof + cof;
            proj = proj +
              self.points[recursion.permutation_list[id]].scalar_mul(&cof);
          }

          if reduce // we need to reduce the simplex
          {
            // FIXME: is it possible to do that without copy?
            // Maybe with some kind of cross-vector-swap?
            for uint::iterate(0u, curr_num_pts) |i|
            {
              let id = _i - (i + 1) * curr_num_pts;
              self.exchange_points.push(copy self.points[recursion.permutation_list[id]]);
            }

            util::swap(&mut self.exchange_points, &mut self.points);
            self.exchange_points.clear();
          }

          proj.scalar_div_inplace(&total_cof);

          return proj
        }

        _i = _i - curr_num_pts * curr_num_pts;
      }

      curr = end;
      curr_num_pts = curr_num_pts - 1;
    }

    util::unreachable();
  }
}

impl<V: Copy         +
        SubDot<N>    +
        ScalarMul<N> +
        ScalarDiv<N> +
        Zero         +
        Add<V, V>    +
        Norm<N>      +
        Eq           +
        Dim,
     N: Ord          +
        Copy         +
        Eq           +
        DivisionRing +
        Ord          +
        Bounded>
     Simplex<V, N> for JohnsonSimplex<V, N>
{
  pub fn new(initial_point: V) -> JohnsonSimplex<V, N>
  { JohnsonSimplex::new(initial_point) }

  pub fn dimension(&self) -> uint
  { self.points.len() - 1 }

  pub fn max_sq_len(&self) -> N
  { self.points.iter().transform(|v| v.sqnorm()).max().unwrap() }

  pub fn contains_point(&self, pt: &V) -> bool
  { self.points.iter().any_(|v| pt == v) }

  pub fn add_point(&mut self, pt: V)
  {
    assert!(self.points.len() <= Dim::dim::<V>());
    self.points.push(pt)
  }

  pub fn project_origin_and_reduce(&mut self) -> V
  { self.do_project_origin(true) }

  pub fn project_origin(&mut self) -> V
  { self.do_project_origin(false) }
}

impl ToStr for RecursionTemplate
{
  fn to_str(&self) -> ~str
  {
    let mut res  = ~"RecursionTemplate { ";
    let mut curr = self.num_leaves;
    let mut dim  = 1;

    res = res + "num_cofactors: " + self.num_cofactors.to_str();

    let mut recursion_offsets_skip_1 = self.offsets.iter();
    recursion_offsets_skip_1.next(); // Skip the two first entries

    for recursion_offsets_skip_1.advance |&off|
    {
      while (curr != off)
      {
        res = res + "\n(@" + self.sub_cofactors[curr].to_str() + " -> ";

        for uint::iterate(0u, dim) |i|
        {
          res = res + self.permutation_list[i + curr].to_str();
          if i != dim - 1
          { res = res + " "; }
        }

        res = res + " - ";

        for uint::iterate(1u, dim) |i|
        {
          res = res + self.sub_cofactors[i + curr].to_str();
          if i != dim - 1
          { res = res + " "; }
        }

        res  = res + ")";
        curr = curr + dim;
      }

      dim += 1;
    }

    res = res + " }\n";

    res = res + "offsets: " + self.offsets.to_str();

    res
  }
}
<commit_msg>vec::reverse(foo) -> foo.reserve()<commit_after>use std::util;
use std::uint;
use std::at_vec::build;
use std::num::{Zero, One};
use std::iterator::IteratorUtil;
use std::vec;
use extra::treemap::TreeMap;
use nalgebra::traits::norm::Norm;
use nalgebra::traits::division_ring::DivisionRing;
use nalgebra::traits::dim::Dim;
use nalgebra::traits::sub_dot::SubDot;
use nalgebra::traits::scalar_op::{ScalarMul, ScalarDiv};
use narrow::algorithm::simplex::Simplex;

#[deriving(Eq)]
pub struct JohnsonSimplex<V, N>
{
  recursion_templates: @[RecursionTemplate],
  points:              ~[V],
  exchange_points:     ~[V],
  cofactors:           ~[N]
}

#[deriving(Eq)]
struct RecursionTemplate
{
  permutation_list: ~[uint],
  offsets:          ~[uint],
  sub_cofactors:    ~[uint],
  num_cofactors:    uint,
  num_leaves:       uint // useful only for printing…
}

impl<V: Copy         +
        SubDot<N>    +
        ScalarMul<N> +
        ScalarDiv<N> +
        Zero         +
        Add<V, V>    +
        Dim,
     N: Ord          +
        Copy         +
        Eq           +
        DivisionRing +
        Ord          +
        Bounded>
JohnsonSimplex<V, N>
{
  pub fn new(initial_point: V) -> JohnsonSimplex<V, N>
  {
    let _dim       = Dim::dim::<V>();
    let perm_list  = JohnsonSimplex::make_permutation_lists::<V, N>();
    let expoints   = vec::with_capacity(_dim);
    let mut points = vec::with_capacity(_dim);

    points.push(initial_point);

    JohnsonSimplex {
      recursion_templates: perm_list
      , points:            points
      , exchange_points:   expoints
      , cofactors:         vec::from_elem(perm_list[_dim].num_cofactors, Zero::zero())
    }
  }

  fn make_permutation_lists() -> @[RecursionTemplate]
  {
    do build |push_to_at_vec|
    {
      for uint::iterate(0u, Dim::dim::<V>() + 1u) |dim|
      {
        push_to_at_vec(
          JohnsonSimplex::make_permutation_list::<V, N>(dim))
      }
    }
  }

  // This is the tricky part of the algorithm. This generates all datas needed
  // to run the johson subalgorithm fastly. This should _not_ be run every time
  // the algorithm is executed. Instead, it should be pre-computed, or computed
  // only once for all. The resulting GC-managed list is intented to be shared
  // between all other simplicis with the same dimension.
  fn make_permutation_list(dim: uint) -> RecursionTemplate
  {
    // FIXME: the permutation list should be computed once for all, at compile
    // time. I dont know how to do that though…

    // The number of points on the biggest subsimplex
    let max_num_points      = dim + 1;

    let mut pts             = ~[]; // the result
    let mut offsets         = ~[];
    let mut sub_cofactors   = ~[];

    // the beginning of the last subsimplices list
    let mut last_dim_begin  = 0;

    // the end of the last subsimplices list
    let mut last_dim_end    = dim + 1 + 1;

    // the number of points of the last subsimplices
    let mut last_num_points = dim + 1;

    let mut map             = TreeMap::new::<~[uint], uint>();

    let mut cofactor_index  = 0;

    for uint::iterate(0, max_num_points) |i|
    { pts.push(i) }

    // initially push the whole simplex (will be removed at the end)
    pts.push(0);

    offsets.push(max_num_points + 1);

    // ... then remove one point each time
    for uint::iterate(0, dim + 1) |i|
    {
      // for each sub-simplex ...
      let mut curr      = last_dim_begin;
      let mut num_added = 0;

      while (curr != last_dim_end)
      {
        // ... iterate on it ...
        for uint::iterate(0, last_num_points) |j|
        {
          // ... and build all the sublist with one last point
          let mut sublist = ~[];

          // then extract the sub-simplex
          for uint::iterate(0, last_num_points) |k|
          {
            // we remove the j'th point
            if pts[curr + j] != pts[curr + k]
            { sublist.push(pts[curr + k]); }
          }

          // keep a trace of the removed point
          sublist.push(pts[curr + j]);

          match map.find(&sublist)
          {
            Some(&v) => sub_cofactors.push(v),
            None     => {
                          for sublist.iter().advance |&e|
                          {
                            pts.push(e);
                            num_added = num_added + 1;
                          }
                          sub_cofactors.push(cofactor_index);
                          map.insert(sublist, cofactor_index);
                          cofactor_index = cofactor_index + 1;
                        }
          }
        }

        let mut parent = ~[];
        for uint::iterate(0, last_num_points + 1) |k|
        { parent.push(pts[curr + k]) }

        match map.find(&parent)
        {
          Some(&p) => sub_cofactors.push(p),
          None => {
            sub_cofactors.push(cofactor_index);
            // There is no need to keep a place for the full simplex cofactor.
            // So we dont increase the cofactor buffer index for the first
            // iteration.
            cofactor_index = cofactor_index + if i == 0 { 0 } else { 1 };
          }
        }

        curr = curr + last_num_points + 1;
      }

      // initialize the next iteration with one less point
      last_dim_begin = last_dim_end ;
      last_dim_end = last_dim_end + num_added;
      offsets.push(last_dim_end);
      last_num_points = last_num_points - 1;
    }

    // cofactor indices for leaves
    for uint::iterate(0, max_num_points) |i|
    { sub_cofactors.push(*map.find(&~[max_num_points - 1 - i]).unwrap()) }

    // end to begin offsets
    offsets.unshift(0u);
    offsets.reverse();
    offsets.pop();

    let rev_offsets = offsets.map(|&e| pts.len() - e);
    let num_leaves = rev_offsets[0];

    // reverse points and cofectors
    pts.reverse();
    sub_cofactors.reverse();

    // remove the full simplex
    let num_pts = pts.len();
    pts.truncate(num_pts - max_num_points - 1);
    sub_cofactors.truncate(num_pts - max_num_points - 1);

    RecursionTemplate {
      offsets:          rev_offsets,
      permutation_list: pts,
      num_cofactors:    sub_cofactors[0] + 1,
      sub_cofactors:    sub_cofactors,
      num_leaves:       num_leaves
    }
  }

  pub fn reset(&mut self, pt: V)
  {
    self.points.clear();
    self.points.push(pt);
  }

  fn do_project_origin(&mut self, reduce: bool) -> V
  {
    // FIXME: for optimization: do special case when there are only 1 point ?
    let _0                   = Zero::zero::<N>();
    let _1                   = One::one::<N>();
    let max_num_pts          = self.points.len();
    let recursion            = &self.recursion_templates[max_num_pts - 1];
    let mut curr_num_pts     = 1u;
    let mut curr             = max_num_pts;

    for uint::iterate(0u, max_num_pts) |i|
    { self.cofactors[recursion.num_cofactors - 1 - i] = copy _1; }

    /*
     * first loop: compute all the cofactors
     */
    let mut recursion_offsets_skip_2 = recursion.offsets.iter();
    recursion_offsets_skip_2.next(); // Skip the two first entries
    recursion_offsets_skip_2.next();
    for recursion_offsets_skip_2.advance |&end|
    {
      // for each sub-simplex ...
      let mut _i = curr;
      while (_i != end)
      {
        let mut cofactor = Zero::zero::<N>();
        let j_pid        = recursion.permutation_list[curr];
        let k_pid        = recursion.permutation_list[curr + 1u];

        // ... with curr_num_pts points ...
        for uint::iterate(0u, curr_num_pts) |i|
        {
          // ... compute its cofactor.
          let i_pid        = recursion.permutation_list[curr + 1 + i];
          let sub_cofactor = copy self.cofactors[recursion.sub_cofactors[curr + 1 + i]];
          let delta = sub_cofactor *
                      self.points[k_pid].sub_dot(&self.points[j_pid],
                                                 &self.points[i_pid]);

          cofactor = cofactor + delta;
        }

        self.cofactors[recursion.sub_cofactors[curr]] = cofactor;
        curr = curr + curr_num_pts + 1; // points + removed point + cofactor id

        _i = _i + curr_num_pts + 1;
      }

      curr_num_pts = curr_num_pts + 1;
    }

    /*
     * second loop: find the subsimplex containing the projection
     */
    let _invalid_cofactor = Bounded::max_value();
    let mut offsets_iter = recursion.offsets.rev_iter();
    offsets_iter.next(); // skip the first offset
    for offsets_iter.advance |&end|
    {
      // for each sub-simplex ...
      let mut _i = curr;
      while (_i != end)
      {
        let mut foundit = true;
        // ... with curr_num_pts points permutations ...
        for uint::iterate(0u, curr_num_pts) |i|
        {
          // ... see if its cofactor is positive
          let cof_id = _i - (i + 1) * curr_num_pts;
          let cof    = copy self.cofactors[recursion.sub_cofactors[cof_id]];
          if cof > _0
          {
            // invalidate the children cofactor
            if curr_num_pts > 1
            {
              let subcofid = recursion.sub_cofactors[cof_id + 1];

              if self.cofactors[subcofid] > _0
              { self.cofactors[subcofid] = copy _invalid_cofactor }
            }

            // dont concider this sub-simplex if it has been invalidated by its
            // parent(s)
            if cof == _invalid_cofactor
            { foundit = false }
          }
          else // we found a negative cofactor: no projection possible here
          { foundit = false }
        }

        if foundit
        {
          // we found a projection!
          // re-run the same iteration but, this time, compute the projection
          let mut total_cof = copy _0;
          let mut proj      = Zero::zero::<V>();

          for uint::iterate(0u, curr_num_pts) |i|
          {
            // ... see if its cofactor is positive
            let id    = _i - (i + 1) * curr_num_pts;
            let cof   = copy self.cofactors[recursion.sub_cofactors[id]];

            total_cof = total_cof + cof;
            proj = proj +
              self.points[recursion.permutation_list[id]].scalar_mul(&cof);
          }

          if reduce // we need to reduce the simplex
          {
            // FIXME: is it possible to do that without copy?
            // Maybe with some kind of cross-vector-swap?
            for uint::iterate(0u, curr_num_pts) |i|
            {
              let id = _i - (i + 1) * curr_num_pts;
              self.exchange_points.push(copy self.points[recursion.permutation_list[id]]);
            }

            util::swap(&mut self.exchange_points, &mut self.points);
            self.exchange_points.clear();
          }

          proj.scalar_div_inplace(&total_cof);

          return proj
        }

        _i = _i - curr_num_pts * curr_num_pts;
      }

      curr = end;
      curr_num_pts = curr_num_pts - 1;
    }

    util::unreachable();
  }
}

impl<V: Copy         +
        SubDot<N>    +
        ScalarMul<N> +
        ScalarDiv<N> +
        Zero         +
        Add<V, V>    +
        Norm<N>      +
        Eq           +
        Dim,
     N: Ord          +
        Copy         +
        Eq           +
        DivisionRing +
        Ord          +
        Bounded>
     Simplex<V, N> for JohnsonSimplex<V, N>
{
  pub fn new(initial_point: V) -> JohnsonSimplex<V, N>
  { JohnsonSimplex::new(initial_point) }

  pub fn dimension(&self) -> uint
  { self.points.len() - 1 }

  pub fn max_sq_len(&self) -> N
  { self.points.iter().transform(|v| v.sqnorm()).max().unwrap() }

  pub fn contains_point(&self, pt: &V) -> bool
  { self.points.iter().any_(|v| pt == v) }

  pub fn add_point(&mut self, pt: V)
  {
    assert!(self.points.len() <= Dim::dim::<V>());
    self.points.push(pt)
  }

  pub fn project_origin_and_reduce(&mut self) -> V
  { self.do_project_origin(true) }

  pub fn project_origin(&mut self) -> V
  { self.do_project_origin(false) }
}

impl ToStr for RecursionTemplate
{
  fn to_str(&self) -> ~str
  {
    let mut res  = ~"RecursionTemplate { ";
    let mut curr = self.num_leaves;
    let mut dim  = 1;

    res = res + "num_cofactors: " + self.num_cofactors.to_str();

    let mut recursion_offsets_skip_1 = self.offsets.iter();
    recursion_offsets_skip_1.next(); // Skip the two first entries

    for recursion_offsets_skip_1.advance |&off|
    {
      while (curr != off)
      {
        res = res + "\n(@" + self.sub_cofactors[curr].to_str() + " -> ";

        for uint::iterate(0u, dim) |i|
        {
          res = res + self.permutation_list[i + curr].to_str();
          if i != dim - 1
          { res = res + " "; }
        }

        res = res + " - ";

        for uint::iterate(1u, dim) |i|
        {
          res = res + self.sub_cofactors[i + curr].to_str();
          if i != dim - 1
          { res = res + " "; }
        }

        res  = res + ")";
        curr = curr + dim;
      }

      dim += 1;
    }

    res = res + " }\n";

    res = res + "offsets: " + self.offsets.to_str();

    res
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use bit_vec::BitVec;
use byteorder::{LittleEndian as LE, ReadBytesExt, WriteBytesExt};
use futures_util::future::Either;
use mysql_common::value::serialize_bin_many;

use std::io::Write;

use crate::{
    connection_like::{
        streamless::Streamless, ConnectionLike, ConnectionLikeWrapper, StmtCacheResult,
    },
    consts::{ColumnType, Command},
    error::*,
    io,
    prelude::FromRow,
    queryable::{query_result::QueryResult, BinaryProtocol},
    Column, Params, Row,
    Value::{self, *},
};

/// Inner statement representation.
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct InnerStmt {
    /// Positions and names of named parameters
    pub named_params: Option<Vec<String>>,
    pub params: Option<Vec<Column>>,
    pub columns: Option<Vec<Column>>,
    pub statement_id: u32,
    pub num_columns: u16,
    pub num_params: u16,
    pub warning_count: u16,
}

impl InnerStmt {
    // TODO: Consume payload?
    pub fn new(payload: &[u8], named_params: Option<Vec<String>>) -> Result<InnerStmt> {
        let mut payload = &payload[..];

        if payload.read_u8()? != 0x00 {
            panic!("Invalid statement packet status");
        }

        let statement_id = payload.read_u32::<LE>()?;
        let num_columns = payload.read_u16::<LE>()?;
        let num_params = payload.read_u16::<LE>()?;
        payload.read_u8()?;
        let warning_count = payload.read_u16::<LE>()?;
        Ok(InnerStmt {
            named_params,
            statement_id,
            num_columns,
            num_params,
            warning_count,
            params: None,
            columns: None,
        })
    }
}

/// Prepared statement
pub struct Stmt<T> {
    conn_like: Option<Either<T, Streamless<T>>>,
    inner: InnerStmt,
    /// None => In use elsewhere
    /// Some(Cached) => Should not be closed
    /// Some(NotCached(_)) => Should be closed
    cached: Option<StmtCacheResult>,
}

pub fn new<T>(conn_like: T, inner: InnerStmt, cached: StmtCacheResult) -> Stmt<T>
where
    T: ConnectionLike + Sized + 'static,
{
    Stmt::new(conn_like, inner, cached)
}

impl<T> Stmt<T>
where
    T: ConnectionLike + Sized + 'static,
{
    fn new(conn_like: T, inner: InnerStmt, cached: StmtCacheResult) -> Stmt<T> {
        Stmt {
            conn_like: Some(Either::Left(conn_like)),
            inner,
            cached: Some(cached),
        }
    }

    async fn send_long_data_for_index(
        self,
        params: Vec<Value>,
        index: usize,
    ) -> Result<(Self, Vec<Value>)> {
        let mut this = self;
        for chunk in 0.. {
            let data_cap = crate::consts::MAX_PAYLOAD_LEN - 10;
            let buf = match params[index] {
                Bytes(ref x) => {
                    let statement_id = this.inner.statement_id;
                    let mut chunks = x.chunks(data_cap);
                    match chunks.nth(chunk) {
                        Some(chunk) => {
                            let mut buf = Vec::with_capacity(chunk.len() + 6);
                            buf.write_u32::<LE>(statement_id).unwrap();
                            buf.write_u16::<LE>(index as u16).unwrap();
                            buf.write_all(chunk).unwrap();
                            Some(buf)
                        }
                        _ => None,
                    }
                }
                _ => unreachable!(),
            };
            match buf {
                Some(buf) => {
                    let chunk_len = buf.len() - 6;
                    this = this
                        .write_command_data(Command::COM_STMT_SEND_LONG_DATA, buf)
                        .await?;
                    if chunk_len < data_cap {
                        break;
                    }
                }
                None => break,
            }
        }
        Ok((this, params))
    }

    async fn send_long_data(
        self,
        mut params: Vec<Value>,
        large_bitmap: BitVec<u8>,
    ) -> Result<(Self, Vec<Value>)> {
        let mut bits = large_bitmap.into_iter().enumerate();
        let mut this = self;
        loop {
            match bits.next() {
                Some((index, true)) => {
                    let (this_, params_) = this.send_long_data_for_index(params, index).await?;
                    this = this_;
                    params = params_;
                }
                Some((_, false)) => {}
                None => return Ok((this, params)),
            }
        }
    }

    async fn execute_positional<U>(self, params: U) -> Result<QueryResult<Self, BinaryProtocol>>
    where
        U: ::std::ops::Deref<Target = [Value]>,
        U: IntoIterator<Item = Value>,
        U: Send + 'static,
    {
        if self.inner.num_params as usize != params.len() {
            let error = DriverError::StmtParamsMismatch {
                required: self.inner.num_params,
                supplied: params.len() as u16,
            }
            .into();
            return Err(error);
        }

        let params_def = self.inner.params.as_ref().unwrap();
        let bin_payload = serialize_bin_many(&*params_def, &*params)?;
        let (row_data, null_bitmap, large_bitmap) = bin_payload;
        let (this, params) = self
            .send_long_data(params.into_iter().collect(), large_bitmap.clone())
            .await?;

        let mut data = Vec::new();
        write_data(
            &mut data,
            this.inner.statement_id,
            row_data,
            params,
            this.inner.params.as_ref().unwrap(),
            null_bitmap,
        );
        let this = this
            .write_command_data(Command::COM_STMT_EXECUTE, data)
            .await?;
        let this = this.read_result_set(None).await?;
        Ok(this)
    }

    async fn execute_named(self, params: Params) -> Result<QueryResult<Self, BinaryProtocol>> {
        if self.inner.named_params.is_none() {
            let error = DriverError::NamedParamsForPositionalQuery.into();
            return Err(error);
        }

        let positional_params =
            match params.into_positional(self.inner.named_params.as_ref().unwrap()) {
                Ok(positional_params) => positional_params,
                Err(error) => return Err(error.into()),
            };

        match positional_params {
            Params::Positional(params) => self.execute_positional(params).await,
            _ => unreachable!(),
        }
    }

    async fn execute_empty(self) -> Result<QueryResult<Self, BinaryProtocol>> {
        if self.inner.num_params > 0 {
            let error = DriverError::StmtParamsMismatch {
                required: self.inner.num_params,
                supplied: 0,
            }
            .into();
            return Err(error);
        }

        let mut data = Vec::with_capacity(4 + 1 + 4);
        data.write_u32::<LE>(self.inner.statement_id).unwrap();
        data.write_u8(0u8).unwrap();
        data.write_u32::<LE>(1u32).unwrap();

        let this = self
            .write_command_data(Command::COM_STMT_EXECUTE, data)
            .await?;
        this.read_result_set(None).await
    }

    /// See `Queryable::execute`
    pub async fn execute<P>(self, params: P) -> Result<QueryResult<Self, BinaryProtocol>>
    where
        P: Into<Params>,
    {
        let params = params.into();
        match params {
            Params::Positional(params) => self.execute_positional(params).await,
            Params::Named(_) => self.execute_named(params).await,
            Params::Empty => self.execute_empty().await,
        }
    }

    /// See `Queryable::first`
    pub async fn first<P, R>(self, params: P) -> Result<(Self, Option<R>)>
    where
        P: Into<Params> + 'static,
        R: FromRow,
    {
        let result = self.execute(params).await?;
        let (this, mut rows) = result.collect_and_drop::<Row>().await?;
        if rows.len() > 1 {
            Ok((this, Some(FromRow::from_row(rows.swap_remove(0)))))
        } else {
            Ok((this, rows.pop().map(FromRow::from_row)))
        }
    }

    /// See `Queryable::batch`
    pub async fn batch<I, P>(self, params_iter: I) -> Result<Self>
    where
        I: IntoIterator<Item = P>,
        I::IntoIter: Send + 'static,
        Params: From<P>,
        P: 'static,
    {
        let mut params_iter = params_iter.into_iter().map(Params::from);
        let mut this = self;
        loop {
            match params_iter.next() {
                Some(params) => {
                    this = this.execute(params).await?.drop_result().await?;
                }
                None => break Ok(this),
            }
        }
    }

    /// This will close statement (if it's not in the cache) and resolve to a wrapped queryable.
    pub async fn close(mut self) -> Result<T> {
        let cached = self.cached.take();
        match self.conn_like {
            Some(Either::Left(conn_like)) => {
                if let Some(StmtCacheResult::NotCached(stmt_id)) = cached {
                    conn_like.close_stmt(stmt_id).await
                } else {
                    Ok(conn_like)
                }
            }
            _ => unreachable!(),
        }
    }

    pub(crate) fn unwrap(mut self) -> (T, Option<StmtCacheResult>) {
        match self.conn_like {
            Some(Either::Left(conn_like)) => (conn_like, self.cached.take()),
            _ => unreachable!(),
        }
    }
}

impl<T: ConnectionLike + 'static> ConnectionLikeWrapper for Stmt<T> {
    type ConnLike = T;

    fn take_stream(self) -> (Streamless<Self>, io::Stream)
    where
        Self: Sized,
    {
        let Stmt {
            conn_like,
            inner,
            cached,
        } = self;
        match conn_like {
            Some(Either::Left(conn_like)) => {
                let (streamless, stream) = conn_like.take_stream();
                let this = Stmt {
                    conn_like: Some(Either::Right(streamless)),
                    inner,
                    cached,
                };
                (Streamless::new(this), stream)
            }
            _ => unreachable!(),
        }
    }

    fn return_stream(&mut self, stream: io::Stream) {
        let conn_like = self.conn_like.take().unwrap();
        match conn_like {
            Either::Right(streamless) => {
                self.conn_like = Some(Either::Left(streamless.return_stream(stream)));
            }
            _ => unreachable!(),
        }
    }

    fn conn_like_ref(&self) -> &Self::ConnLike {
        match self.conn_like {
            Some(Either::Left(ref conn_like)) => conn_like,
            _ => unreachable!(),
        }
    }

    fn conn_like_mut(&mut self) -> &mut Self::ConnLike {
        match self.conn_like {
            Some(Either::Left(ref mut conn_like)) => conn_like,
            _ => unreachable!(),
        }
    }
}

fn write_data(
    writer: &mut Vec<u8>,
    stmt_id: u32,
    row_data: Vec<u8>,
    params: Vec<Value>,
    params_def: &[Column],
    null_bitmap: BitVec<u8>,
) {
    let capacity = 9 + null_bitmap.storage().len() + 1 + params.len() * 2 + row_data.len();
    writer.reserve(capacity);
    writer.write_u32::<LE>(stmt_id).unwrap();
    writer.write_u8(0u8).unwrap();
    writer.write_u32::<LE>(1u32).unwrap();
    writer.write_all(null_bitmap.storage().as_ref()).unwrap();
    writer.write_u8(1u8).unwrap();
    for i in 0..params.len() {
        let result = match params[i] {
            NULL => writer.write_all(&[params_def[i].column_type() as u8, 0u8]),
            Bytes(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_VAR_STRING as u8, 0u8]),
            Int(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_LONGLONG as u8, 0u8]),
            UInt(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_LONGLONG as u8, 128u8]),
            Float(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_DOUBLE as u8, 0u8]),
            Date(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_DATETIME as u8, 0u8]),
            Time(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_TIME as u8, 0u8]),
        };
        result.unwrap();
    }
    writer.write_all(row_data.as_ref()).unwrap();
}
<commit_msg>Reuse logic from mysql_common for InnerStmt::new<commit_after>// Copyright (c) 2017 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use bit_vec::BitVec;
use byteorder::{LittleEndian as LE, ReadBytesExt, WriteBytesExt};
use futures_util::future::Either;
use mysql_common::value::serialize_bin_many;

use std::io::Write;

use crate::{
    connection_like::{
        streamless::Streamless, ConnectionLike, ConnectionLikeWrapper, StmtCacheResult,
    },
    consts::{ColumnType, Command},
    error::*,
    io,
    prelude::FromRow,
    queryable::{query_result::QueryResult, BinaryProtocol},
    Column, Params, Row,
    Value::{self, *},
};

/// Inner statement representation.
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct InnerStmt {
    /// Positions and names of named parameters
    pub named_params: Option<Vec<String>>,
    pub params: Option<Vec<Column>>,
    pub columns: Option<Vec<Column>>,
    pub statement_id: u32,
    pub num_columns: u16,
    pub num_params: u16,
    pub warning_count: u16,
}

impl InnerStmt {
    // TODO: Consume payload?
    pub fn new(payload: &[u8], named_params: Option<Vec<String>>) -> Result<InnerStmt> {
        let packet: mysql_common::packets::StmtPacket =
            mysql_common::packets::parse_stmt_packet(payload)?;

        Ok(InnerStmt {
            named_params,
            statement_id: packet.statement_id(),
            num_columns: packet.num_columns(),
            num_params: packet.num_params(),
            warning_count: packet.warning_count(),
            params: None,
            columns: None,
        })
    }
}

/// Prepared statement
pub struct Stmt<T> {
    conn_like: Option<Either<T, Streamless<T>>>,
    inner: InnerStmt,
    /// None => In use elsewhere
    /// Some(Cached) => Should not be closed
    /// Some(NotCached(_)) => Should be closed
    cached: Option<StmtCacheResult>,
}

pub fn new<T>(conn_like: T, inner: InnerStmt, cached: StmtCacheResult) -> Stmt<T>
where
    T: ConnectionLike + Sized + 'static,
{
    Stmt::new(conn_like, inner, cached)
}

impl<T> Stmt<T>
where
    T: ConnectionLike + Sized + 'static,
{
    fn new(conn_like: T, inner: InnerStmt, cached: StmtCacheResult) -> Stmt<T> {
        Stmt {
            conn_like: Some(Either::Left(conn_like)),
            inner,
            cached: Some(cached),
        }
    }

    async fn send_long_data_for_index(
        self,
        params: Vec<Value>,
        index: usize,
    ) -> Result<(Self, Vec<Value>)> {
        let mut this = self;
        for chunk in 0.. {
            let data_cap = crate::consts::MAX_PAYLOAD_LEN - 10;
            let buf = match params[index] {
                Bytes(ref x) => {
                    let statement_id = this.inner.statement_id;
                    let mut chunks = x.chunks(data_cap);
                    match chunks.nth(chunk) {
                        Some(chunk) => {
                            let mut buf = Vec::with_capacity(chunk.len() + 6);
                            buf.write_u32::<LE>(statement_id).unwrap();
                            buf.write_u16::<LE>(index as u16).unwrap();
                            buf.write_all(chunk).unwrap();
                            Some(buf)
                        }
                        _ => None,
                    }
                }
                _ => unreachable!(),
            };
            match buf {
                Some(buf) => {
                    let chunk_len = buf.len() - 6;
                    this = this
                        .write_command_data(Command::COM_STMT_SEND_LONG_DATA, buf)
                        .await?;
                    if chunk_len < data_cap {
                        break;
                    }
                }
                None => break,
            }
        }
        Ok((this, params))
    }

    async fn send_long_data(
        self,
        mut params: Vec<Value>,
        large_bitmap: BitVec<u8>,
    ) -> Result<(Self, Vec<Value>)> {
        let mut bits = large_bitmap.into_iter().enumerate();
        let mut this = self;
        loop {
            match bits.next() {
                Some((index, true)) => {
                    let (this_, params_) = this.send_long_data_for_index(params, index).await?;
                    this = this_;
                    params = params_;
                }
                Some((_, false)) => {}
                None => return Ok((this, params)),
            }
        }
    }

    async fn execute_positional<U>(self, params: U) -> Result<QueryResult<Self, BinaryProtocol>>
    where
        U: ::std::ops::Deref<Target = [Value]>,
        U: IntoIterator<Item = Value>,
        U: Send + 'static,
    {
        if self.inner.num_params as usize != params.len() {
            let error = DriverError::StmtParamsMismatch {
                required: self.inner.num_params,
                supplied: params.len() as u16,
            }
            .into();
            return Err(error);
        }

        let params_def = self.inner.params.as_ref().unwrap();
        let bin_payload = serialize_bin_many(&*params_def, &*params)?;
        let (row_data, null_bitmap, large_bitmap) = bin_payload;
        let (this, params) = self
            .send_long_data(params.into_iter().collect(), large_bitmap.clone())
            .await?;

        let mut data = Vec::new();
        write_data(
            &mut data,
            this.inner.statement_id,
            row_data,
            params,
            this.inner.params.as_ref().unwrap(),
            null_bitmap,
        );
        let this = this
            .write_command_data(Command::COM_STMT_EXECUTE, data)
            .await?;
        let this = this.read_result_set(None).await?;
        Ok(this)
    }

    async fn execute_named(self, params: Params) -> Result<QueryResult<Self, BinaryProtocol>> {
        if self.inner.named_params.is_none() {
            let error = DriverError::NamedParamsForPositionalQuery.into();
            return Err(error);
        }

        let positional_params =
            match params.into_positional(self.inner.named_params.as_ref().unwrap()) {
                Ok(positional_params) => positional_params,
                Err(error) => return Err(error.into()),
            };

        match positional_params {
            Params::Positional(params) => self.execute_positional(params).await,
            _ => unreachable!(),
        }
    }

    async fn execute_empty(self) -> Result<QueryResult<Self, BinaryProtocol>> {
        if self.inner.num_params > 0 {
            let error = DriverError::StmtParamsMismatch {
                required: self.inner.num_params,
                supplied: 0,
            }
            .into();
            return Err(error);
        }

        let mut data = Vec::with_capacity(4 + 1 + 4);
        data.write_u32::<LE>(self.inner.statement_id).unwrap();
        data.write_u8(0u8).unwrap();
        data.write_u32::<LE>(1u32).unwrap();

        let this = self
            .write_command_data(Command::COM_STMT_EXECUTE, data)
            .await?;
        this.read_result_set(None).await
    }

    /// See `Queryable::execute`
    pub async fn execute<P>(self, params: P) -> Result<QueryResult<Self, BinaryProtocol>>
    where
        P: Into<Params>,
    {
        let params = params.into();
        match params {
            Params::Positional(params) => self.execute_positional(params).await,
            Params::Named(_) => self.execute_named(params).await,
            Params::Empty => self.execute_empty().await,
        }
    }

    /// See `Queryable::first`
    pub async fn first<P, R>(self, params: P) -> Result<(Self, Option<R>)>
    where
        P: Into<Params> + 'static,
        R: FromRow,
    {
        let result = self.execute(params).await?;
        let (this, mut rows) = result.collect_and_drop::<Row>().await?;
        if rows.len() > 1 {
            Ok((this, Some(FromRow::from_row(rows.swap_remove(0)))))
        } else {
            Ok((this, rows.pop().map(FromRow::from_row)))
        }
    }

    /// See `Queryable::batch`
    pub async fn batch<I, P>(self, params_iter: I) -> Result<Self>
    where
        I: IntoIterator<Item = P>,
        I::IntoIter: Send + 'static,
        Params: From<P>,
        P: 'static,
    {
        let mut params_iter = params_iter.into_iter().map(Params::from);
        let mut this = self;
        loop {
            match params_iter.next() {
                Some(params) => {
                    this = this.execute(params).await?.drop_result().await?;
                }
                None => break Ok(this),
            }
        }
    }

    /// This will close statement (if it's not in the cache) and resolve to a wrapped queryable.
    pub async fn close(mut self) -> Result<T> {
        let cached = self.cached.take();
        match self.conn_like {
            Some(Either::Left(conn_like)) => {
                if let Some(StmtCacheResult::NotCached(stmt_id)) = cached {
                    conn_like.close_stmt(stmt_id).await
                } else {
                    Ok(conn_like)
                }
            }
            _ => unreachable!(),
        }
    }

    pub(crate) fn unwrap(mut self) -> (T, Option<StmtCacheResult>) {
        match self.conn_like {
            Some(Either::Left(conn_like)) => (conn_like, self.cached.take()),
            _ => unreachable!(),
        }
    }
}

impl<T: ConnectionLike + 'static> ConnectionLikeWrapper for Stmt<T> {
    type ConnLike = T;

    fn take_stream(self) -> (Streamless<Self>, io::Stream)
    where
        Self: Sized,
    {
        let Stmt {
            conn_like,
            inner,
            cached,
        } = self;
        match conn_like {
            Some(Either::Left(conn_like)) => {
                let (streamless, stream) = conn_like.take_stream();
                let this = Stmt {
                    conn_like: Some(Either::Right(streamless)),
                    inner,
                    cached,
                };
                (Streamless::new(this), stream)
            }
            _ => unreachable!(),
        }
    }

    fn return_stream(&mut self, stream: io::Stream) {
        let conn_like = self.conn_like.take().unwrap();
        match conn_like {
            Either::Right(streamless) => {
                self.conn_like = Some(Either::Left(streamless.return_stream(stream)));
            }
            _ => unreachable!(),
        }
    }

    fn conn_like_ref(&self) -> &Self::ConnLike {
        match self.conn_like {
            Some(Either::Left(ref conn_like)) => conn_like,
            _ => unreachable!(),
        }
    }

    fn conn_like_mut(&mut self) -> &mut Self::ConnLike {
        match self.conn_like {
            Some(Either::Left(ref mut conn_like)) => conn_like,
            _ => unreachable!(),
        }
    }
}

fn write_data(
    writer: &mut Vec<u8>,
    stmt_id: u32,
    row_data: Vec<u8>,
    params: Vec<Value>,
    params_def: &[Column],
    null_bitmap: BitVec<u8>,
) {
    let capacity = 9 + null_bitmap.storage().len() + 1 + params.len() * 2 + row_data.len();
    writer.reserve(capacity);
    writer.write_u32::<LE>(stmt_id).unwrap();
    writer.write_u8(0u8).unwrap();
    writer.write_u32::<LE>(1u32).unwrap();
    writer.write_all(null_bitmap.storage().as_ref()).unwrap();
    writer.write_u8(1u8).unwrap();
    for i in 0..params.len() {
        let result = match params[i] {
            NULL => writer.write_all(&[params_def[i].column_type() as u8, 0u8]),
            Bytes(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_VAR_STRING as u8, 0u8]),
            Int(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_LONGLONG as u8, 0u8]),
            UInt(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_LONGLONG as u8, 128u8]),
            Float(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_DOUBLE as u8, 0u8]),
            Date(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_DATETIME as u8, 0u8]),
            Time(..) => writer.write_all(&[ColumnType::MYSQL_TYPE_TIME as u8, 0u8]),
        };
        result.unwrap();
    }
    writer.write_all(row_data.as_ref()).unwrap();
}
<|endoftext|>


<commit_before>extern crate libmodbus_rs;

use libmodbus_rs::{Modbus, ModbusServer, ModbusMapping, ModbusTCP};

fn run() -> Result<(), String> {
    let mut modbus = Modbus::new_tcp("127.0.0.1", 1502).expect("could not create modbus TCP context");
    // modbus.set_debug(true).expect("could not set DEBUG mode");

    let modbus_mapping = ModbusMapping::new(500, 500, 500, 500).expect("could not create Modbus Mapping");

    let mut socket = modbus.tcp_listen(1).expect("could not listen");
    modbus.tcp_accept(&mut socket).expect("could not create socket");

    loop {
        let mut query = vec![0u8; Modbus::TCP_MAX_ADU_LENGTH as usize];

        match modbus.receive(&mut query) {
                Ok(num) => modbus.reply(&query, num, &modbus_mapping),
                Err(err) => {
                    println!("ERROR while parsing: {}", err);
                    break;
                },
            }
            .expect("could not receive message");
    }
    println!("Quit the loop: Connection reset by peer");

    // ModbusMapping and Modbus struct are dropped here, automatically by Rusts Drop trait.
    // Hence no manual `modbus_mapping_free(mb_mapping)`, `modbus_close(ctx)` nor `modbus_free(ctx)` needed, like in C.

    Ok(())
}

fn main() {
    if let Err(ref err) = run() {
        println!("Error: {}", err);

        std::process::exit(1)
    }
}
<commit_msg>Fix typo in message<commit_after>extern crate libmodbus_rs;

use libmodbus_rs::{Modbus, ModbusServer, ModbusMapping, ModbusTCP};

fn run() -> Result<(), String> {
    let mut modbus = Modbus::new_tcp("127.0.0.1", 1502).expect("could not create modbus TCP context");
    // modbus.set_debug(true).expect("could not set DEBUG mode");

    let modbus_mapping = ModbusMapping::new(500, 500, 500, 500).expect("could not create Modbus Mapping");

    let mut socket = modbus.tcp_listen(1).expect("could not listen");
    modbus.tcp_accept(&mut socket).expect("could not create socket");

    loop {
        let mut query = vec![0u8; Modbus::TCP_MAX_ADU_LENGTH as usize];

        match modbus.receive(&mut query) {
                Ok(num) => modbus.reply(&query, num, &modbus_mapping),
                Err(err) => {
                    println!("ERROR while parsing: {}", err);
                    break;
                },
            }
            .expect("could not receive message");
    }
    println!("Quit the loop: ");

    // ModbusMapping and Modbus struct are dropped here, automatically by Rusts Drop trait.
    // Hence no manual `modbus_mapping_free(mb_mapping)`, `modbus_close(ctx)` nor `modbus_free(ctx)` needed, like in C.

    Ok(())
}

fn main() {
    if let Err(ref err) = run() {
        println!("Error: {}", err);

        std::process::exit(1)
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use git2::Repository;
use git2::ObjectType;
use git2::BranchType;
use rand::Rng;
use termion::color;
use rand;

use yaml_rust::yaml::Hash;
use yaml_rust::{Yaml, YamlEmitter};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct State {
    pub task: String,
    pub focus: String,
    pub filter: Option<Filter>,
    pub message: String,
    pub include: Vec<String>,
    pub property: Vec<Property>,
    pub diff: Vec<String>,
    pub save_update: Option<String>,
    pub new_task: Option<String>,
    pub dragged: Option<String>
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Property {
    pub name: String,
    pub value: String
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Filter {
    pub name: String,
    pub value: String
}

pub enum StateError {
}

impl State {

    pub fn blank() -> State {
        State {
            task: String::from("master"),
            focus: String::new(),
            message: String::new(),
            include: vec![],
            diff: vec![],
            property: vec![],
            save_update: None,
            new_task: None,
            filter: None,
            dragged: None
        }
    }

    pub fn reset(&mut self) {
        self.message = String::new();
        self.property = vec![];
        self.include = vec![];
        self.save_update = None;
        self.new_task = None;
    }

    pub fn reset_with_status(&mut self) {
        self.message = String::new();
        self.property = self.property.iter().filter_map(|p| if p.name == "Status" { Some(p.clone()) } else { None }).collect();
        self.include = vec![];
        self.save_update = None;
        self.new_task = None;
    }

    pub fn apply(&mut self, mut last_state: Option<State>, rng: &mut rand::ThreadRng) -> Result<State, StateError> {
        let new_last_state = self.clone();
        if let Some(ref mut last) = last_state {
            println!("{}{:?}{}", color::Fg(color::LightBlack), last, color::Fg(color::Reset));
            println!("{:?}", self);
            println!("▶ ");
            let repo = Repository::open_from_env().unwrap();
            println!("Applying to repo: {:?}", repo.path());
            if self.task != last.task {
                println!("  {}Task changing{}  {} => {}", color::Fg(color::LightYellow), color::Fg(color::Reset), last.task, self.task);
                if self.dragged.is_some() {
                    println!("  {}Saving last state due to dragging{}", color::Fg(color::LightGreen), color::Fg(color::Reset));
                    last.save_update(repo, rng);
                    self.reset_with_status();
                } else {
                    let switching_to_task = self.task.clone();
                    self.task = last.task.clone();
                    self.save_update(repo, rng);
                    self.task = switching_to_task;
                    self.reset();
                }
            } else {
                println!("  {}Updating task {}{}", color::Fg(color::LightGreen), self.task, color::Fg(color::Reset));
                //self.apply_index(&repo);

                if self.save_update.is_some() || self.new_task.is_some() {
                    self.save_update(repo, rng);
                    self.reset();
                }
            }
        }
        Ok(new_last_state)
    }

    fn save_update(&mut self, repo: Repository, rng: &mut rand::ThreadRng) {
        let mut yaml = String::new();
        self.convert_to_yaml(&mut yaml);
        let message = format!("{}\n{}", self.message, yaml);

        let mut index = repo.index().unwrap();
        index.read(false).unwrap();
        let author = repo.signature().unwrap();
        let tree_oid = index.write_tree().unwrap();
        let tree = repo.find_tree(tree_oid).unwrap();

        let branch = repo.find_branch(&self.task, BranchType::Local);
        match branch {
            Ok(branch) => {
                let head = branch.into_reference();
                let commit = head.peel(ObjectType::Commit).unwrap();
                repo.commit(Some(&head.name().unwrap()), &author, &author, &message, &tree, &[&commit.as_commit().unwrap()]).unwrap();
            }
            Err(_) => {
                println!("Initial Commit");
                repo.commit(Some("refs/heads/master"), &author, &author, &message, &tree, &[]).unwrap();
            }
        };

        println!("  {}Saved changes to {}{} {:?}", color::Fg(color::LightRed), self.task, color::Fg(color::Reset), self);
        if self.new_task.is_some() {
            let num = rng.gen::<u16>();
            let new_task = format!("{:X}", num);
            println!("  {}Creating task {}{}", color::Fg(color::LightBlue), new_task, color::Fg(color::Reset));
            if let Ok(master_branch) = repo.find_branch("master", BranchType::Local) {
                let master = master_branch.into_reference();
                let commit_obj = master.peel(ObjectType::Commit).unwrap();
                let commit = commit_obj.as_commit().unwrap();
                repo.branch(&new_task, &commit, false).unwrap();
                self.task = new_task;
                self.reset();
            }
        }
    }

    // Constructing the properties YAML from State
    fn convert_to_yaml(&self, mut yaml: &mut String) {
        let mut tasks = Hash::new();
        let mut properties = Hash::new();
        let mut emitter = YamlEmitter::new(&mut yaml);
        for property in self.property.clone() {
            properties.insert(Yaml::String(property.name), Yaml::String(property.value));
        }
        tasks.insert(Yaml::String(self.task.clone()), Yaml::Hash(properties));
        emitter.dump(&Yaml::Hash(tasks)).unwrap();
    }

    fn apply_index(&self, repo: &Repository) {
        let branch = repo.find_branch(&self.task, BranchType::Local);
        let head = match branch {
            Ok(branch) => branch.into_reference(),
            _ => repo.head().unwrap()
        };
        let commit = head.peel(ObjectType::Commit).unwrap();
        let mut index = repo.index().unwrap();

        // apply git index changes only if task is the working directory
        if self.task == repo.head().unwrap().shorthand().unwrap() {
            let to_remove = index.iter().fold(vec![], |mut acc, entry| {
                let entry_path = String::from_utf8_lossy(entry.path.as_ref());
                match self.include.iter().find(|i| i.as_ref() == entry_path) {
                    None => acc.push(entry_path.into_owned()),
                    _ => {}
                };
                acc
            });
            repo.reset_default(Some(&commit), to_remove.iter()).unwrap();
            for change in &self.include {
                let path = Path::new(&change);
                index.add_path(path).unwrap();
            }
            index.write().unwrap();
        }
    }

}
<commit_msg>Behaves as expected when switching task with no last state<commit_after>use std::path::Path;
use git2::Repository;
use git2::ObjectType;
use git2::BranchType;
use rand::Rng;
use termion::color;
use rand;

use yaml_rust::yaml::Hash;
use yaml_rust::{Yaml, YamlEmitter};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct State {
    pub task: String,
    pub focus: String,
    pub filter: Option<Filter>,
    pub message: String,
    pub include: Vec<String>,
    pub property: Vec<Property>,
    pub diff: Vec<String>,
    pub save_update: Option<String>,
    pub new_task: Option<String>,
    pub dragged: Option<String>
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Property {
    pub name: String,
    pub value: String
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Filter {
    pub name: String,
    pub value: String
}

pub enum StateError {
}

impl State {

    pub fn blank() -> State {
        State {
            task: String::from("master"),
            focus: String::new(),
            message: String::new(),
            include: vec![],
            diff: vec![],
            property: vec![],
            save_update: None,
            new_task: None,
            filter: None,
            dragged: None
        }
    }

    pub fn reset(&mut self) {
        self.message = String::new();
        self.property = vec![];
        self.include = vec![];
        self.save_update = None;
        self.new_task = None;
    }

    pub fn reset_with_status(&mut self) {
        self.message = String::new();
        self.property = self.property.iter().filter_map(|p| if p.name == "Status" { Some(p.clone()) } else { None }).collect();
        self.include = vec![];
        self.save_update = None;
        self.new_task = None;
    }

    pub fn apply(&mut self, mut last_state: Option<State>, rng: &mut rand::ThreadRng) -> Result<State, StateError> {
        let new_last_state = self.clone();
        let repo = Repository::open_from_env().unwrap();
        if let Some(ref mut last) = last_state {
            println!("{}{:?}{}", color::Fg(color::LightBlack), last, color::Fg(color::Reset));
            println!("{:?}", self);
            println!("▶ ");
            println!("Applying to repo: {:?}", repo.path());
            if self.task != last.task {
                println!("  {}Task changing{}  {} => {}", color::Fg(color::LightYellow), color::Fg(color::Reset), last.task, self.task);
                if self.dragged.is_some() {
                    println!("  {}Saving last state due to dragging{}", color::Fg(color::LightGreen), color::Fg(color::Reset));
                    last.save_update(repo, rng);
                    self.reset_with_status();
                } else {
                    let switching_to_task = self.task.clone();
                    self.task = last.task.clone();
                    self.save_update(repo, rng);
                    self.task = switching_to_task;
                    self.reset();
                }
            } else {
                println!("  {}Updating task {}{}", color::Fg(color::LightGreen), self.task, color::Fg(color::Reset));
                //self.apply_index(&repo);

                if self.save_update.is_some() || self.new_task.is_some() {
                    self.save_update(repo, rng);
                    self.reset();
                }
            }
        } else {
            let head = repo.head().unwrap();
            let task = head.shorthand().unwrap();
            if self.task != task {
                println!("  Changing task with no last state, will reset");
                self.reset();
            }
        }
        Ok(new_last_state)
    }

    fn save_update(&mut self, repo: Repository, rng: &mut rand::ThreadRng) {
        let mut yaml = String::new();
        self.convert_to_yaml(&mut yaml);
        let message = format!("{}\n{}", self.message, yaml);

        let mut index = repo.index().unwrap();
        index.read(false).unwrap();
        let author = repo.signature().unwrap();
        let tree_oid = index.write_tree().unwrap();
        let tree = repo.find_tree(tree_oid).unwrap();

        let branch = repo.find_branch(&self.task, BranchType::Local);
        match branch {
            Ok(branch) => {
                let head = branch.into_reference();
                let commit = head.peel(ObjectType::Commit).unwrap();
                repo.commit(Some(&head.name().unwrap()), &author, &author, &message, &tree, &[&commit.as_commit().unwrap()]).unwrap();
            }
            Err(_) => {
                println!("Initial Commit");
                repo.commit(Some("refs/heads/master"), &author, &author, &message, &tree, &[]).unwrap();
            }
        };

        println!("  {}Saved changes to {}{} {:?}", color::Fg(color::LightRed), self.task, color::Fg(color::Reset), self);
        if self.new_task.is_some() {
            let num = rng.gen::<u16>();
            let new_task = format!("{:X}", num);
            println!("  {}Creating task {}{}", color::Fg(color::LightBlue), new_task, color::Fg(color::Reset));
            if let Ok(master_branch) = repo.find_branch("master", BranchType::Local) {
                let master = master_branch.into_reference();
                let commit_obj = master.peel(ObjectType::Commit).unwrap();
                let commit = commit_obj.as_commit().unwrap();
                repo.branch(&new_task, &commit, false).unwrap();
                self.task = new_task;
                self.reset();
            }
        }
    }

    // Constructing the properties YAML from State
    fn convert_to_yaml(&self, mut yaml: &mut String) {
        let mut tasks = Hash::new();
        let mut properties = Hash::new();
        let mut emitter = YamlEmitter::new(&mut yaml);
        for property in self.property.clone() {
            properties.insert(Yaml::String(property.name), Yaml::String(property.value));
        }
        tasks.insert(Yaml::String(self.task.clone()), Yaml::Hash(properties));
        emitter.dump(&Yaml::Hash(tasks)).unwrap();
    }

    fn apply_index(&self, repo: &Repository) {
        let branch = repo.find_branch(&self.task, BranchType::Local);
        let head = match branch {
            Ok(branch) => branch.into_reference(),
            _ => repo.head().unwrap()
        };
        let commit = head.peel(ObjectType::Commit).unwrap();
        let mut index = repo.index().unwrap();

        // apply git index changes only if task is the working directory
        if self.task == repo.head().unwrap().shorthand().unwrap() {
            let to_remove = index.iter().fold(vec![], |mut acc, entry| {
                let entry_path = String::from_utf8_lossy(entry.path.as_ref());
                match self.include.iter().find(|i| i.as_ref() == entry_path) {
                    None => acc.push(entry_path.into_owned()),
                    _ => {}
                };
                acc
            });
            repo.reset_default(Some(&commit), to_remove.iter()).unwrap();
            for change in &self.include {
                let path = Path::new(&change);
                index.add_path(path).unwrap();
            }
            index.write().unwrap();
        }
    }

}
<|endoftext|>