rust-qt-binding-generator/demo/rust/src/implementation/file_system_tree.rs

311 lines
9.3 KiB
Rust

// Copyright 2017 Jos van den Oever <jos@vandenoever.info>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License or (at your option) version 3 or any later version
// accepted by the membership of KDE e.V. (or its successor approved
// by the membership of KDE e.V.), which shall act as a proxy
// defined in Section 14 of version 3 of the license.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use interface::*;
use std::fs::*;
use std::fs::read_dir;
use std::path::PathBuf;
use std::ffi::OsString;
use std::default::Default;
use std::thread;
use std::sync::mpsc::{Receiver, Sender, channel};
use std::marker::Sync;
pub struct DirEntry {
name: OsString,
metadata: Option<Metadata>,
path: Option<PathBuf>,
icon: Vec<u8>,
}
impl Item for DirEntry {
fn new(name: &str) -> DirEntry {
DirEntry {
name: OsString::from(name),
metadata: metadata(name).ok(),
path: None,
icon: Vec::new(),
}
}
fn can_fetch_more(&self) -> bool {
self.metadata.as_ref().map_or(false, |m| m.is_dir())
}
fn file_name(&self) -> String {
self.name.to_string_lossy().to_string()
}
fn file_path(&self) -> Option<String> {
self.path.as_ref().map(|p| p.to_string_lossy().into())
}
fn file_permissions(&self) -> i32 {
42
}
fn file_type(&self) -> i32 {
0
}
fn file_size(&self) -> Option<u64> {
self.metadata.as_ref().map(|m| m.len())
}
fn icon(&self) -> &[u8] {
&self.icon
}
fn retrieve(id: usize, parents: Vec<&Self>, outgoing: &Sender<(usize, PathBuf)>) {
let path: PathBuf = parents.into_iter().map(|e| &e.name).collect();
if let Err(e) = outgoing.send((id, path)) {
eprintln!("{}", e);
}
}
fn read_files(
incoming: Receiver<(usize, PathBuf)>,
outgoing: Sender<(usize, Vec<Self>)>,
mut emit: FileSystemTreeEmitter,
) {
thread::spawn(move || while let Ok((id, path)) = incoming.recv() {
let mut v = Vec::new();
if let Ok(it) = read_dir(&path) {
for i in it.filter_map(|v| v.ok()) {
let de = DirEntry {
name: i.file_name(),
metadata: i.metadata().ok(),
path: Some(i.path()),
icon: Vec::new(),
};
v.push(de);
}
}
v.sort_by(|a, b| a.name.cmp(&b.name));
if let Ok(()) = outgoing.send((id, v)) {
emit.new_data_ready(Some(id));
} else {
return;
}
});
}
}
impl Default for DirEntry {
fn default() -> DirEntry {
DirEntry {
name: OsString::new(),
metadata: None,
path: None,
icon: Vec::new(),
}
}
}
pub trait Item: Default {
fn new(name: &str) -> Self;
fn can_fetch_more(&self) -> bool;
fn retrieve(id: usize, parents: Vec<&Self>, outgoing: &Sender<(usize, PathBuf)>);
fn read_files(
incoming: Receiver<(usize, PathBuf)>,
outgoing: Sender<(usize, Vec<Self>)>,
emit: FileSystemTreeEmitter,
);
fn file_name(&self) -> String;
fn file_path(&self) -> Option<String>;
fn file_permissions(&self) -> i32;
fn file_type(&self) -> i32;
fn file_size(&self) -> Option<u64>;
fn icon(&self) -> &[u8];
}
pub type FileSystemTree = RGeneralItemModel<DirEntry>;
struct Entry<T: Item> {
parent: Option<usize>,
row: usize,
children: Option<Vec<usize>>,
data: T,
}
pub struct RGeneralItemModel<T: Item> {
emit: FileSystemTreeEmitter,
model: FileSystemTreeTree,
entries: Vec<Entry<T>>,
path: Option<String>,
outgoing: Sender<(usize, PathBuf)>,
incoming: Receiver<(usize, Vec<T>)>,
}
impl<T: Item> RGeneralItemModel<T>
where
T: Sync + Send,
{
fn reset(&mut self) {
self.model.begin_reset_model();
self.entries.clear();
if let Some(ref path) = self.path {
let root = Entry {
parent: None,
row: 0,
children: None,
data: T::new(path),
};
self.entries.push(root);
}
self.model.end_reset_model();
}
fn get(&self, index: usize) -> &Entry<T> {
&self.entries[index]
}
fn retrieve(&self, index: usize) {
let parents = self.get_parents(index);
T::retrieve(index, parents, &self.outgoing);
}
fn process_incoming(&mut self) {
while let Ok((id, entries)) = self.incoming.try_recv() {
if self.entries[id].children.is_some() {
continue;
}
let mut new_entries = Vec::new();
let mut children = Vec::new();
for (r, d) in entries.into_iter().enumerate() {
new_entries.push(Entry {
parent: Some(id),
row: r,
children: None,
data: d,
});
children.push(self.entries.len() + r);
}
if new_entries.is_empty() {
self.entries[id].children = Some(children);
} else {
self.model.begin_insert_rows(
Some(id),
0,
new_entries.len() - 1,
);
self.entries[id].children = Some(children);
self.entries.append(&mut new_entries);
self.model.end_insert_rows();
}
}
}
fn get_parents(&self, id: usize) -> Vec<&T> {
let mut pos = Some(id);
let mut e = Vec::new();
while let Some(p) = pos {
e.push(p);
pos = self.entries[p].parent;
}
e.into_iter().rev().map(|i| &self.entries[i].data).collect()
}
}
impl<T: Item> FileSystemTreeTrait for RGeneralItemModel<T>
where
T: Sync + Send,
{
fn new(mut emit: FileSystemTreeEmitter, model: FileSystemTreeTree) -> Self {
let (outgoing, thread_incoming) = channel();
let (thread_outgoing, incoming) = channel::<(usize, Vec<T>)>();
T::read_files(thread_incoming, thread_outgoing, emit.clone());
let mut tree: RGeneralItemModel<T> = RGeneralItemModel {
emit,
model,
entries: Vec::new(),
path: None,
outgoing,
incoming,
};
tree.reset();
tree
}
fn emit(&mut self) -> &mut FileSystemTreeEmitter {
&mut self.emit
}
fn path(&self) -> Option<&str> {
self.path.as_ref().map(|s| &s[..])
}
fn set_path(&mut self, value: Option<String>) {
if self.path != value {
self.path = value;
self.emit.path_changed();
self.reset();
}
}
fn can_fetch_more(&self, index: Option<usize>) -> bool {
let entry = self.get(index.unwrap_or(0));
entry.children.is_none() && entry.data.can_fetch_more()
}
fn fetch_more(&mut self, index: Option<usize>) {
self.process_incoming();
if !self.can_fetch_more(index) {
return;
}
self.retrieve(index.unwrap_or(0));
}
fn row_count(&self, index: Option<usize>) -> usize {
if self.entries.is_empty() {
return 0;
}
if let Some(i) = index {
let entry = self.get(i);
if let Some(ref children) = entry.children {
children.len()
} else {
self.retrieve(i);
0
}
} else {
1
}
}
fn index(&self, index: Option<usize>, row: usize) -> usize {
if let Some(index) = index {
self.get(index).children.as_ref().unwrap()[row]
} else {
0
}
}
fn parent(&self, index: usize) -> Option<usize> {
self.entries[index].parent
}
fn row(&self, index: usize) -> usize {
self.entries[index].row
}
fn check_row(&self, index: usize, _row: usize) -> Option<usize> {
if index < self.entries.len() {
Some(self.row(index))
} else {
None
}
}
fn file_name(&self, index: usize) -> String {
self.get(index).data.file_name()
}
fn file_permissions(&self, index: usize) -> i32 {
self.get(index).data.file_permissions()
}
#[allow(unused_variables)]
fn file_icon(&self, index: usize) -> &[u8] {
self.get(index).data.icon()
}
fn file_path(&self, index: usize) -> Option<String> {
self.get(index).data.file_path()
}
fn file_type(&self, index: usize) -> i32 {
self.get(index).data.file_type()
}
fn file_size(&self, index: usize) -> Option<u64> {
self.get(index).data.file_size()
}
}