This commit is contained in:
Julian Ospald 2017-09-13 17:10:51 +02:00
parent fc953316bf
commit 7f6fab0870
10 changed files with 290 additions and 87 deletions

View File

@ -13,6 +13,7 @@ gtk-sys = "^0.4.0"
libpijul = "^0.7.3" libpijul = "^0.7.3"
log = "^0.3.8" log = "^0.3.8"
base64 = "^0.6.0" base64 = "^0.6.0"
chrono = "^0.4.0"
[dependencies.gdk] [dependencies.gdk]
features = [ features = [

View File

@ -59,17 +59,92 @@
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Branch</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">2</property>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<object class="GtkComboBoxText" id="branch_select"> <object class="GtkComboBoxText" id="branch_select">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">2</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">2</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Hash</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">2</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="hash_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">2</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">2</property>
<property name="position">2</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

20
src/appstate.rs Normal file
View File

@ -0,0 +1,20 @@
use gtk;
use ui::gui::*;
pub struct AppS {
_cant_construct: (),
pub gui: Gui,
}
impl AppS {
pub fn new() -> Self {
let builder = gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/data/ui/window.glade")));
return AppS {
_cant_construct: (),
gui: Gui::new(builder),
}
}
}

View File

@ -9,6 +9,7 @@ extern crate gtk;
extern crate gtk_sys; extern crate gtk_sys;
extern crate libpijul; extern crate libpijul;
extern crate base64; extern crate base64;
extern crate chrono;
extern crate flexi_logger; extern crate flexi_logger;
#[macro_use] #[macro_use]
@ -17,9 +18,12 @@ extern crate log;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
mod ui; mod appstate;
mod errors; mod errors;
mod pijul_glue;
mod ui;
use appstate::*;
use ui::entry::*; use ui::entry::*;
use std::rc::Rc; use std::rc::Rc;

View File

@ -0,0 +1,18 @@
use libpijul;
use libpijul::fs_representation::*;
use errors::*;
pub fn get_branches(path: &str) -> Result<Vec<String>> {
let mut vec = Vec::new();
let repo = libpijul::Repository::open(pristine_dir(path), None).unwrap();
let txn = repo.txn_begin().unwrap();
let branches = txn.iter_branches(None).map(|x| String::from(x.name.as_str()));
vec.extend(branches);
return Ok(vec);
}

4
src/pijul_glue/mod.rs Normal file
View File

@ -0,0 +1,4 @@
//! The pijul glue subsystem.
pub mod branches;
pub mod patches;

79
src/pijul_glue/patches.rs Normal file
View File

@ -0,0 +1,79 @@
use libpijul;
use libpijul::patch;
use libpijul::fs_representation::*;
use std::path::Path;
use errors::*;
use base64;
pub struct Patch {
pub name: String,
pub authors: String,
pub created: String,
pub signed: bool,
hash: libpijul::Hash,
}
impl Patch {
pub fn from_patch(patch: patch::Patch, hash: libpijul::Hash) -> Self {
let p = Patch {
name: patch.header().name.clone(),
authors: patch.header().authors.join(", "),
created: format!("{}", patch.header().timestamp.format("%F %T")),
signed: patch_is_signed(patch),
hash: hash,
};
return p;
}
pub fn hash_to_str(&self) -> String {
let config = base64::Config::new(base64::CharacterSet::Standard,
false,
false,
base64::LineWrap::NoWrap);
let s = self.hash.to_base64(config);
return s;
}
}
pub fn str_to_hash(s: String) -> Option<libpijul::Hash> {
return libpijul::Hash::from_base64(s.as_ref());
}
pub fn get_patch_headers(path: &str, branch: &str) -> Result<Vec<Patch>> {
let mut vec = Vec::new();
let repo = libpijul::Repository::open(pristine_dir(path), None)?;
let txn = repo.txn_begin().unwrap();
let branch = txn.get_branch(branch).unwrap();
let patches_ids = txn.iter_patches(&branch, None).map(|x| x.0);
for pid in patches_ids {
let hash_ref = txn.external_hash(pid);
let mp = libpijul::fs_representation::read_patch(Path::new(path),
hash_ref);
match mp {
Ok(p) => {
vec.push(Patch::from_patch(p, hash_ref.to_owned()));
},
Err(e) => warn!("Could not get patch for patch_id {:?}\nError: {:?}", pid, e),
}
}
return Ok(vec);
}
pub fn patch_is_signed(patch: patch::Patch) -> bool {
match patch {
patch::Patch::Unsigned(_) => return false,
patch::Patch::Signed{patch: _, signature: _ } => return true,
}
}

View File

@ -1,49 +1,15 @@
use gtk; use appstate::*;
use gtk::prelude::*;
use std::rc::Rc;
use gtk::WidgetExt; use gtk::WidgetExt;
use libpijul; use gtk::prelude::*;
use libpijul::fs_representation::*; use gtk;
use std::path::Path; use pijul_glue::branches::*;
use errors::*; use pijul_glue::patches::*;
use std::rc::Rc;
pub struct AppS { const HASH_COLUMN: i32 = 3;
_cant_construct: (),
pub gui: Gui,
}
impl AppS {
pub fn new() -> Self {
let builder = gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/data/ui/window.glade")));
return AppS {
_cant_construct: (),
gui: Gui::new(builder),
}
}
}
pub struct Gui {
_cant_construct: (),
pub window: gtk::Window,
pub branch_select: gtk::ComboBoxText,
pub patch_tree: gtk::TreeView,
}
impl Gui {
pub fn new(builder: gtk::Builder) -> Self {
return Gui {
_cant_construct: (),
window: builder.get_object("main").unwrap(),
branch_select: builder.get_object("branch_select").unwrap(),
patch_tree: builder.get_object("patch_tree").unwrap(),
}
}
}
pub fn init(appstate: Rc<AppS>) { pub fn init(appstate: Rc<AppS>) {
{ {
@ -56,61 +22,74 @@ pub fn init(appstate: Rc<AppS>) {
} }
{ {
let ls = gtk::ListStore::new(&[gtk::Type::String]); let mut columns: Vec<gtk::TreeViewColumn> = Vec::new();
let patches = get_patches("/home/hasufell/git/pijul", append_column("Patch", &mut columns, &appstate.gui.patch_tree, Some(200));
append_column("Authors", &mut columns, &appstate.gui.patch_tree, None);
append_column("Date", &mut columns, &appstate.gui.patch_tree, None);
let ls = gtk::ListStore::new(&[
// visible
gtk::Type::String, // name
gtk::Type::String, // authors
gtk::Type::String, // creation date
// not visible
gtk::Type::String, // hash
]);
let patches = get_patch_headers("/home/hasufell/git/pijul",
"master"); "master");
for patch in patches.unwrap() { for patch in patches.unwrap() {
ls.insert_with_values(None, &[0], &[&patch.as_str()]); ls.insert_with_values(None, &[0, 1, 2, 3],
&[&patch.name.as_str(),
&patch.authors.as_str(),
&patch.created.as_str(),
&patch.hash_to_str(),
]);
}
appstate.gui.patch_tree.set_model(Some(&ls));
appstate.gui.patch_tree.set_headers_visible(true);
} }
let renderer = gtk::CellRendererText::new(); let selection = appstate.gui.patch_tree.get_selection();
let col = gtk::TreeViewColumn::new(); selection.set_mode(gtk::SelectionMode::Single);
col.set_title("Patch");
col.set_resizable(true);
col.pack_start(&renderer, true);
col.add_attribute(&renderer, "text", 0);
col.set_clickable(true);
col.set_sort_column_id(0);
appstate.gui.patch_tree.append_column(&col); /* connect selection change */
appstate.gui.patch_tree.set_model(Some(&ls)); {
let apps = appstate.clone();
selection.connect_changed(move |ts| {
if let Some((model, iter)) = ts.get_selected() {
let val = model.get_value(&iter, HASH_COLUMN);
let s: String = val.downcast().unwrap().get().unwrap();
apps.gui.hash_entry.set_text(s.as_ref());
}
});
} }
appstate.gui.window.show_all(); appstate.gui.window.show_all();
} }
fn append_column(title: &str,
v: &mut Vec<gtk::TreeViewColumn>,
tree: &gtk::TreeView,
max_width: Option<i32>) {
fn get_branches(path: &str) -> Result<Vec<String>> { let id = v.len() as i32;
let mut vec = Vec::new();
let repo = libpijul::Repository::open(pristine_dir(path), None).unwrap(); let renderer = gtk::CellRendererText::new();
let txn = repo.txn_begin().unwrap(); let col = gtk::TreeViewColumn::new();
col.set_title(title);
col.set_resizable(true);
col.set_sort_column_id(id);
if let Some(max_width) = max_width {
col.set_max_width(max_width);
col.set_expand(true);
}
col.pack_start(&renderer, true);
col.add_attribute(&renderer, "text", id);
col.set_clickable(true);
let branches = txn.iter_branches(None).map(|x| String::from(x.name.as_str())); tree.append_column(&col);
vec.extend(branches); v.push(col);
return Ok(vec);
} }
fn get_patches(path: &str, branch: &str) -> Result<Vec<String>> {
let mut vec = Vec::new();
let repo = libpijul::Repository::open(pristine_dir(path), None)?;
let txn = repo.txn_begin().unwrap();
let branch = txn.get_branch(branch).unwrap();
let patches_ids = txn.iter_patches(&branch, None).map(|x| x.0);
for pid in patches_ids {
let mp = libpijul::fs_representation::read_patch(Path::new(path),
txn.external_hash(pid));
match mp {
Ok(p) => vec.push(p.header().name.clone()),
Err(e) => warn!("Could not get patch for patch_id {:?}\nError: {:?}", pid, e),
}
}
return Ok(vec);
}

22
src/ui/gui.rs Normal file
View File

@ -0,0 +1,22 @@
use gtk;
pub struct Gui {
_cant_construct: (),
pub window: gtk::Window,
pub branch_select: gtk::ComboBoxText,
pub patch_tree: gtk::TreeView,
pub hash_entry: gtk::Entry,
}
impl Gui {
pub fn new(builder: gtk::Builder) -> Self {
return Gui {
_cant_construct: (),
window: builder.get_object("main").unwrap(),
branch_select: builder.get_object("branch_select").unwrap(),
patch_tree: builder.get_object("patch_tree").unwrap(),
hash_entry: builder.get_object("hash_entry").unwrap(),
}
}
}

View File

@ -1,3 +1,4 @@
//! The UI subsystem. //! The UI subsystem.
pub mod entry; pub mod entry;
pub mod gui;