loading inner collections
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
use rusqlite::Connection;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ImportData(Vec<ImportGroup>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImportGroup {
|
||||
id: u64,
|
||||
name: String,
|
||||
fields: Vec<String>,
|
||||
mapping: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ImportNote {
|
||||
name: String,
|
||||
tags: String,
|
||||
fields: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn load_groups(connection: &Connection) -> ImportData {
|
||||
let json_models: String = connection
|
||||
.query_one("select models from col", (), |row| row.get(0))
|
||||
.unwrap();
|
||||
let raw: Value = serde_json::from_str(json_models.as_str()).unwrap();
|
||||
let sets = raw.as_object().unwrap();
|
||||
let mut total_data = ImportData(vec![]);
|
||||
|
||||
for (_, collection) in sets {
|
||||
let fields = collection["flds"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| x["name"].as_str().unwrap().to_string())
|
||||
.collect();
|
||||
let group = ImportGroup {
|
||||
id: collection["id"].as_u64().unwrap(),
|
||||
name: collection["name"].as_str().unwrap().to_string(),
|
||||
fields,
|
||||
mapping: Default::default(),
|
||||
};
|
||||
println!("{:?}", group);
|
||||
total_data.0.push(group);
|
||||
}
|
||||
total_data
|
||||
}
|
||||
@@ -6,3 +6,4 @@ pub(crate) mod sqlite;
|
||||
pub(crate) mod voice;
|
||||
pub(crate) mod words;
|
||||
pub(crate) mod web_api;
|
||||
pub(crate) mod import;
|
||||
|
||||
+1
-1
@@ -264,7 +264,7 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
|
||||
row![horizontal().width(8), self.words_list(),],
|
||||
row![button("Добавить слово").style(jl_button).on_press(NewWord),
|
||||
horizontal().width(Fill),
|
||||
button("Импорт").style(jl_button).on_press(ToImport),
|
||||
button("Импорт").style(text).on_press(ToImport),
|
||||
]
|
||||
]
|
||||
.spacing(5),
|
||||
|
||||
+134
-14
@@ -1,44 +1,164 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use iced::{Element, Task};
|
||||
use iced::widget::scrollable;
|
||||
use crate::data_provider::import::{load_groups, ImportData};
|
||||
use crate::dictionary::app_data_dir;
|
||||
use crate::import::ImportMessage::*;
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::styling::*;
|
||||
use iced::widget::{column, row, button, text };
|
||||
use crate::AppState;
|
||||
use crate::import::ImportMessage::*;
|
||||
use iced::widget::scrollable;
|
||||
use iced::widget::{button, column, row, text};
|
||||
use iced::{Element, Task};
|
||||
use iced_core::Alignment::Center;
|
||||
use rfd::AsyncFileDialog;
|
||||
use rusqlite::Connection;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::BufReader;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::task::spawn_blocking;
|
||||
use zip::ZipArchive;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ImportState {
|
||||
state: Arc<Mutex<AppState>>
|
||||
state: Arc<Mutex<AppState>>,
|
||||
path: Option<PathBuf>,
|
||||
import_data: Option<ImportData>,
|
||||
selected_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ImportMessage {
|
||||
Back
|
||||
Back,
|
||||
SelectFile,
|
||||
UpdateFile(PathBuf),
|
||||
UpdateImport(ImportData),
|
||||
}
|
||||
|
||||
impl NavigatedPage<ImportMessage> for ImportState {
|
||||
fn navigate(&self, message: &ImportMessage) -> Option<Page> {
|
||||
if let Back = message {
|
||||
return Some(Page::PreviousPage)
|
||||
return Some(Page::PreviousPage);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn navigated(&mut self) {
|
||||
}
|
||||
fn navigated(&mut self) {}
|
||||
|
||||
fn update(&mut self, message: ImportMessage) -> Task<RootMessage> {
|
||||
match message {
|
||||
Back => {}
|
||||
UpdateFile(path) => {
|
||||
self.path = Some(path);
|
||||
return self.load_package();
|
||||
}
|
||||
SelectFile => return Self::select_import_file(),
|
||||
UpdateImport(import) => {
|
||||
self.import_data = Some(import);
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<'_, ImportMessage> {
|
||||
back_overlay(scrollable(column![]).into(), Back)
|
||||
back_overlay(
|
||||
scrollable(
|
||||
column![
|
||||
row![
|
||||
button("Выбрать файл").style(jl_button).on_press(SelectFile),
|
||||
text!("{}", {
|
||||
if let Some(path) = &self.path {
|
||||
path.to_string_lossy().to_string()
|
||||
} else {
|
||||
"Файл не выбран".to_string()
|
||||
}
|
||||
})
|
||||
]
|
||||
.align_y(Center)
|
||||
.spacing(DEFAULT_SPACING)
|
||||
]
|
||||
.spacing(DEFAULT_SPACING),
|
||||
)
|
||||
.into(),
|
||||
Back,
|
||||
)
|
||||
}
|
||||
}
|
||||
impl ImportState {
|
||||
pub fn new(state: Arc<Mutex<AppState>>) -> ImportState {
|
||||
ImportState{
|
||||
state
|
||||
ImportState {
|
||||
state,
|
||||
path: None,
|
||||
import_data: None,
|
||||
selected_index: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn select_import_file() -> Task<RootMessage> {
|
||||
Task::perform(
|
||||
async {
|
||||
let file = AsyncFileDialog::new()
|
||||
.add_filter("Anki", &["apkg"])
|
||||
.pick_file()
|
||||
.await;
|
||||
if let Some(file) = file {
|
||||
return Some(file.path().into());
|
||||
}
|
||||
None
|
||||
},
|
||||
|result| {
|
||||
if let Some(path) = result {
|
||||
RootMessage::Import(UpdateFile(path))
|
||||
} else {
|
||||
RootMessage::None
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn load_package(&self) -> Task<RootMessage> {
|
||||
let path = self.path.clone().unwrap();
|
||||
Task::perform(
|
||||
async {
|
||||
spawn_blocking(move || {
|
||||
Self::extract_import_file(path)?;
|
||||
let data = Self::read_import_file()?;
|
||||
return Ok(data);
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
},
|
||||
|result: Result<ImportData, ()>| {
|
||||
if let Ok(import) = result {
|
||||
RootMessage::Import(UpdateImport(import))
|
||||
} else {
|
||||
RootMessage::None
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn extract_import_file(path: PathBuf) -> Result<(), ()> {
|
||||
let file = File::open(path).map_err(|_| ())?;
|
||||
let reader = BufReader::new(file);
|
||||
let mut archive = ZipArchive::new(reader).map_err(|_| ())?;
|
||||
let db_file = archive.by_name("collection.anki2");
|
||||
if let Ok(mut file) = db_file {
|
||||
let temp_file_path = app_data_dir().join("import");
|
||||
let mut temp_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&temp_file_path)
|
||||
.map_err(|_| ())?;
|
||||
std::io::copy(&mut file, &mut temp_file).map_err(|_| ())?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
fn read_import_file() -> Result<ImportData, ()> {
|
||||
let temp_file_path = app_data_dir().join("import");
|
||||
let connection = Connection::open(&temp_file_path).map_err(|_| ())?;
|
||||
let data = load_groups(&connection);
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user