commit before cargo fix
This commit is contained in:
parent
37d19ed98f
commit
d25ee8d327
0
.cargo/config.toml
Normal file
0
.cargo/config.toml
Normal file
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
|||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
debug/
|
debug/
|
||||||
target/
|
target/
|
||||||
|
web_temp/
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
19
Cargo.toml
19
Cargo.toml
@ -9,7 +9,7 @@ iced = { git = "https://github.com/iced-rs/iced.git", features = [
|
|||||||
"debug",
|
"debug",
|
||||||
"advanced",
|
"advanced",
|
||||||
"image",
|
"image",
|
||||||
|
"sipper",
|
||||||
] }
|
] }
|
||||||
tokio = { version = "1.45.1", features = ["full"] }
|
tokio = { version = "1.45.1", features = ["full"] }
|
||||||
reqwest = "0.12.20"
|
reqwest = "0.12.20"
|
||||||
@ -30,6 +30,23 @@ anyhow = { version = "1.0.98", features = ["backtrace"] }
|
|||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
env_logger = "0.11.8"
|
env_logger = "0.11.8"
|
||||||
iced_fonts = { version = "0.2.1", features = ["full"] }
|
iced_fonts = { version = "0.2.1", features = ["full"] }
|
||||||
|
image = "0.25.6"
|
||||||
|
lazy_static = "1.5.0"
|
||||||
|
serde_json = "1.0.140"
|
||||||
|
serde = { version = "1.0.219", features = [
|
||||||
|
"alloc",
|
||||||
|
"rc",
|
||||||
|
"derive",
|
||||||
|
"serde_derive",
|
||||||
|
] }
|
||||||
|
rfd = { version = "0.15.3", features = ["tokio"] }
|
||||||
|
iced_futures = { version = "0.13.2", features = [
|
||||||
|
"async-std",
|
||||||
|
"smol",
|
||||||
|
"tokio",
|
||||||
|
"thread-pool",
|
||||||
|
] }
|
||||||
|
num_enum = "0.7.4"
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -19,3 +19,8 @@
|
|||||||
|
|
||||||
## 周期性待办
|
## 周期性待办
|
||||||
* 把所有的警告消灭掉
|
* 把所有的警告消灭掉
|
||||||
|
|
||||||
|
|
||||||
|
## 忐忑
|
||||||
|
* 本软件的3D封装下载调用了jlc的api,不知道哪天就收到了某函,所以暂时只在本站开源了,在未想办法解决掉该可能引起纠纷的事项之前不想广泛传播,所以也请各位道友手下留情,不要随意传播本软件
|
||||||
|
中 东奔西跑黑暗
|
140
makepad_state0.ron
Normal file
140
makepad_state0.ron
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
(
|
||||||
|
dock_items:{
|
||||||
|
edit_tabs:Tabs(
|
||||||
|
tabs:[
|
||||||
|
edit_first,
|
||||||
|
],
|
||||||
|
selected:0,
|
||||||
|
closable:true,
|
||||||
|
),
|
||||||
|
split3:Splitter(
|
||||||
|
axis:Horizontal,
|
||||||
|
align:FromA(20),
|
||||||
|
a:split4,
|
||||||
|
b:run_tabs,
|
||||||
|
),
|
||||||
|
split4:Splitter(
|
||||||
|
axis:Horizontal,
|
||||||
|
align:Weighted(0.5),
|
||||||
|
a:outline_tabs,
|
||||||
|
b:design_tabs,
|
||||||
|
),
|
||||||
|
search:Tab(
|
||||||
|
name:"Search",
|
||||||
|
template:SearchTab,
|
||||||
|
kind:Search,
|
||||||
|
),
|
||||||
|
snapshot_tab:Tab(
|
||||||
|
name:"Snapshot",
|
||||||
|
template:SnapshotTab,
|
||||||
|
kind:Snapshot,
|
||||||
|
),
|
||||||
|
file_tree_tabs:Tabs(
|
||||||
|
tabs:[
|
||||||
|
file_tree_tab,
|
||||||
|
run_list_tab,
|
||||||
|
search,
|
||||||
|
snapshot_tab,
|
||||||
|
],
|
||||||
|
selected:1,
|
||||||
|
closable:true,
|
||||||
|
),
|
||||||
|
log_tabs:Tabs(
|
||||||
|
tabs:[
|
||||||
|
log_list_tab,
|
||||||
|
profiler,
|
||||||
|
],
|
||||||
|
selected:0,
|
||||||
|
closable:true,
|
||||||
|
),
|
||||||
|
file_tree_tab:Tab(
|
||||||
|
name:"Files",
|
||||||
|
template:FilesTab,
|
||||||
|
kind:StudioFileTree,
|
||||||
|
),
|
||||||
|
ai_first:Tab(
|
||||||
|
name:"",
|
||||||
|
template:AiFirstTab,
|
||||||
|
kind:AiFirst,
|
||||||
|
),
|
||||||
|
run_first:Tab(
|
||||||
|
name:"",
|
||||||
|
template:RunFirstTab,
|
||||||
|
kind:RunFirst,
|
||||||
|
),
|
||||||
|
log_list_tab:Tab(
|
||||||
|
name:"Log",
|
||||||
|
template:LogTab,
|
||||||
|
kind:LogList,
|
||||||
|
),
|
||||||
|
edit_first:Tab(
|
||||||
|
name:"",
|
||||||
|
template:EditFirstTab,
|
||||||
|
kind:EditFirst,
|
||||||
|
),
|
||||||
|
design_first:Tab(
|
||||||
|
name:"",
|
||||||
|
template:DesignFirstTab,
|
||||||
|
kind:DesignFirst,
|
||||||
|
),
|
||||||
|
outline_first:Tab(
|
||||||
|
name:"",
|
||||||
|
template:OutlineFirstTab,
|
||||||
|
kind:OutlineFirst,
|
||||||
|
),
|
||||||
|
split2:Splitter(
|
||||||
|
axis:Horizontal,
|
||||||
|
align:Weighted(0.5),
|
||||||
|
a:edit_tabs,
|
||||||
|
b:split3,
|
||||||
|
),
|
||||||
|
run_list_tab:Tab(
|
||||||
|
name:"Run",
|
||||||
|
template:RunListTab,
|
||||||
|
kind:RunList,
|
||||||
|
),
|
||||||
|
root:Splitter(
|
||||||
|
axis:Horizontal,
|
||||||
|
align:FromA(250),
|
||||||
|
a:file_tree_tabs,
|
||||||
|
b:split1,
|
||||||
|
),
|
||||||
|
outline_tabs:Tabs(
|
||||||
|
tabs:[
|
||||||
|
outline_first,
|
||||||
|
],
|
||||||
|
selected:0,
|
||||||
|
closable:true,
|
||||||
|
),
|
||||||
|
design_tabs:Tabs(
|
||||||
|
tabs:[
|
||||||
|
design_first,
|
||||||
|
],
|
||||||
|
selected:0,
|
||||||
|
closable:true,
|
||||||
|
),
|
||||||
|
run_tabs:Tabs(
|
||||||
|
tabs:[
|
||||||
|
run_first,
|
||||||
|
ai_first,
|
||||||
|
],
|
||||||
|
selected:0,
|
||||||
|
closable:true,
|
||||||
|
),
|
||||||
|
profiler:Tab(
|
||||||
|
name:"Profiler",
|
||||||
|
template:ProfilerTab,
|
||||||
|
kind:Profiler,
|
||||||
|
),
|
||||||
|
split1:Splitter(
|
||||||
|
axis:Vertical,
|
||||||
|
align:FromB(200),
|
||||||
|
a:split2,
|
||||||
|
b:log_tabs,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
processes:[
|
||||||
|
],
|
||||||
|
tab_id_to_file_node_id:{
|
||||||
|
},
|
||||||
|
)
|
@ -1,4 +1,5 @@
|
|||||||
use iced::{Length, alignment::Horizontal, widget::Column};
|
use iced::{Length, alignment::Horizontal, widget::Column};
|
||||||
|
use log::info;
|
||||||
|
|
||||||
use crate::ui::main_window::{MainWindowMsg, TabContent};
|
use crate::ui::main_window::{MainWindowMsg, TabContent};
|
||||||
|
|
||||||
@ -13,10 +14,10 @@ pub enum DbBrowserMsg {
|
|||||||
impl TabContent for DbBrowser {
|
impl TabContent for DbBrowser {
|
||||||
type TabMessage = DbBrowserMsg;
|
type TabMessage = DbBrowserMsg;
|
||||||
|
|
||||||
fn update(&self, msg: Self::TabMessage) {
|
fn update(&mut self, msg: Self::TabMessage) {
|
||||||
match msg {
|
match msg {
|
||||||
DbBrowserMsg::Nothing => {
|
DbBrowserMsg::Nothing => {
|
||||||
println!("This function not allowed");
|
info!("This function not allowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use iced::{Length, alignment::Horizontal, widget::Column};
|
use iced::{Length, alignment::Horizontal, widget::Column};
|
||||||
|
use log::info;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use crate::ui::main_window::{MainWindowMsg, TabContent};
|
use crate::ui::main_window::{MainWindowMsg, TabContent};
|
||||||
@ -65,13 +66,34 @@ impl TabContent for HomePage {
|
|||||||
|
|
||||||
type TabMessage = HomePageMsg;
|
type TabMessage = HomePageMsg;
|
||||||
|
|
||||||
fn update(&self, msg: Self::TabMessage) {
|
fn update(&mut self, msg: Self::TabMessage) {
|
||||||
match msg {
|
match msg {
|
||||||
HomePageMsg::Nothing => {
|
HomePageMsg::Nothing => {
|
||||||
println!("This way ok.");
|
info!("This way ok.");
|
||||||
|
}
|
||||||
|
HomePageMsg::OpenStepDir => {
|
||||||
|
info!("To open the dir.");
|
||||||
|
let _ = std::process::Command::new(r"explorer.exe")
|
||||||
|
.arg(self.step_dir.as_str())
|
||||||
|
.output()
|
||||||
|
.unwrap()
|
||||||
|
.stdout;
|
||||||
|
}
|
||||||
|
HomePageMsg::ChooseStepDir => {
|
||||||
|
info!("To choose the step dir.");
|
||||||
|
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||||
|
let path = path.to_str();
|
||||||
|
if let Some(path) = path {
|
||||||
|
self.step_dir = path.to_string();
|
||||||
|
let _ = crate::utils::app_settings::set_step_dir(path.to_string().as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HomePageMsg::CheckUpdate => {
|
||||||
|
info!("To check update.");
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Is the message you should process ? =====>> {msg:?}");
|
info!("Is the message you should process ? =====>> {msg:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,78 @@
|
|||||||
use iced::{Length, alignment::Horizontal, widget::Column};
|
|
||||||
|
|
||||||
use crate::ui::main_window::{MainWindowMsg, TabContent};
|
use crate::ui::main_window::{MainWindowMsg, TabContent};
|
||||||
|
use crate::utils::step_downloader;
|
||||||
|
use anyhow::Result;
|
||||||
|
use iced::{Length, alignment::Horizontal, widget::Column};
|
||||||
|
use log::info;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct JlcDownloader {}
|
pub struct JlcDownloader {
|
||||||
|
search_word: String,
|
||||||
|
}
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum JlcDownloaderMsg {
|
pub enum JlcDownloaderMsg {
|
||||||
Nothing,
|
Nothing,
|
||||||
|
KeywordChanged(String),
|
||||||
|
KeywordSearchResult(Vec<KeywordSearchItem>),
|
||||||
|
KeywordSearchError(String),
|
||||||
|
SearchPart,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TabContent for JlcDownloader {
|
impl TabContent for JlcDownloader {
|
||||||
type TabMessage = JlcDownloaderMsg;
|
type TabMessage = JlcDownloaderMsg;
|
||||||
|
|
||||||
fn update(&self, msg: Self::TabMessage) {
|
fn update(&mut self, msg: Self::TabMessage) {
|
||||||
match msg {
|
match msg {
|
||||||
JlcDownloaderMsg::Nothing => {
|
JlcDownloaderMsg::Nothing => {
|
||||||
println!("JlcDownloaderMsg::Nothing");
|
info!("JlcDownloaderMsg::Nothing");
|
||||||
|
}
|
||||||
|
JlcDownloaderMsg::KeywordChanged(k) => {
|
||||||
|
self.search_word = k;
|
||||||
|
}
|
||||||
|
JlcDownloaderMsg::SearchPart => {
|
||||||
|
info!("Clicked Search");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
info!("Whach out the msg: {msg:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content(&self) -> iced::Element<'_, MainWindowMsg> {
|
fn content(&self) -> iced::Element<'_, MainWindowMsg> {
|
||||||
let btn = iced::widget::button("JlcDownloader")
|
let h = iced::widget::row![
|
||||||
.on_press(MainWindowMsg::JlcDownloader(JlcDownloaderMsg::Nothing));
|
iced::widget::text("搜索元件:").align_y(iced::Alignment::Center),
|
||||||
|
iced::widget::text_input("元件名或嘉立创编号", &self.search_word)
|
||||||
|
.on_input(MainWindowMsg::SearchKeywordChanged),
|
||||||
|
iced::widget::button("搜索")
|
||||||
|
.on_press(MainWindowMsg::JlcDownloader(JlcDownloaderMsg::SearchPart)),
|
||||||
|
]
|
||||||
|
.height(Length::Shrink)
|
||||||
|
.spacing(20);
|
||||||
Column::new()
|
Column::new()
|
||||||
.align_x(Horizontal::Left)
|
.align_x(Horizontal::Left)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.push(btn)
|
.push(h)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub struct KeywordSearchItem {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
impl JlcDownloader {
|
||||||
|
pub async fn search_keyword(keyword: &str) -> Result<Vec<KeywordSearchItem>> {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||||
|
for i in 0..10 {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||||
|
info!("In async search_keyword print tick {i}");
|
||||||
|
}
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
pub async fn fetch_item_images(
|
||||||
|
item: &KeywordSearchItem,
|
||||||
|
) -> Result<Vec<iced::widget::image::Handle>> {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
use crate::ui::db_browser::DbBrowserMsg;
|
use crate::ui::db_browser::DbBrowserMsg;
|
||||||
use crate::ui::home_page::HomePage;
|
use crate::ui::home_page::HomePage;
|
||||||
use crate::ui::home_page::HomePageMsg;
|
use crate::ui::home_page::HomePageMsg;
|
||||||
|
use crate::ui::jlc_downloader::JlcDownloader;
|
||||||
use crate::ui::jlc_downloader::JlcDownloaderMsg;
|
use crate::ui::jlc_downloader::JlcDownloaderMsg;
|
||||||
use crate::ui::part_viewer::PartViewerMsg;
|
use crate::ui::part_viewer::PartViewerMsg;
|
||||||
|
use crate::utils::step_downloader;
|
||||||
|
use iced::Subscription;
|
||||||
|
use iced::Task;
|
||||||
|
use iced::widget::button::warning;
|
||||||
|
use log::info;
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::db_browser;
|
use super::db_browser;
|
||||||
@ -16,8 +23,6 @@ use iced::Theme;
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use iced::widget as w;
|
use iced::widget as w;
|
||||||
use iced::widget::button;
|
use iced::widget::button;
|
||||||
use iced::widget::button::Status;
|
|
||||||
use iced::widget::button::Style;
|
|
||||||
use iced::widget::row;
|
use iced::widget::row;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use iced::{
|
use iced::{
|
||||||
@ -26,7 +31,6 @@ use iced::{
|
|||||||
widget::{Column, Container, Text, column},
|
widget::{Column, Container, Text, column},
|
||||||
};
|
};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::ops::Index;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct MainWindow {
|
struct MainWindow {
|
||||||
@ -57,12 +61,29 @@ impl Default for MainWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl MainWindow {
|
||||||
|
pub fn new() -> (Self, Task<MainWindowMsg>) {
|
||||||
|
(
|
||||||
|
Self::default(),
|
||||||
|
Task::batch([
|
||||||
|
Task::perform(JlcDownloader::search_keyword("test"), |x| match x {
|
||||||
|
Ok(v) => MainWindowMsg::JlcDownloader(JlcDownloaderMsg::KeywordSearchResult(v)),
|
||||||
|
Err(e) => MainWindowMsg::JlcDownloader(JlcDownloaderMsg::KeywordSearchError(
|
||||||
|
format!("{e:?}"),
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
iced::widget::focus_next(),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_window() {
|
pub fn main_window() {
|
||||||
let _ = iced::application(MainWindow::default, MainWindow::update, MainWindow::view)
|
let _ = iced::application(MainWindow::new, MainWindow::update, MainWindow::view)
|
||||||
.theme(MainWindow::theme)
|
.theme(MainWindow::theme)
|
||||||
.default_font(iced::Font::with_name("微软雅黑"))
|
.default_font(iced::Font::with_name("微软雅黑"))
|
||||||
.title(MainWindow::title)
|
.title(MainWindow::title)
|
||||||
|
.subscription(MainWindow::subscription)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +91,7 @@ pub fn main_window() {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MainWindowMsg {
|
pub enum MainWindowMsg {
|
||||||
ThemeChanged(iced::Theme),
|
ThemeChanged(iced::Theme),
|
||||||
|
SearchKeywordChanged(String),
|
||||||
TitleChanged(String),
|
TitleChanged(String),
|
||||||
TabSelected(TabId),
|
TabSelected(TabId),
|
||||||
HomePage(HomePageMsg),
|
HomePage(HomePageMsg),
|
||||||
@ -77,17 +99,36 @@ pub enum MainWindowMsg {
|
|||||||
DbBrowser(DbBrowserMsg),
|
DbBrowser(DbBrowserMsg),
|
||||||
PartViewer(PartViewerMsg),
|
PartViewer(PartViewerMsg),
|
||||||
Nothing,
|
Nothing,
|
||||||
|
StepDownloader(step_downloader::StepDownloaderEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
|
||||||
pub(crate) enum TabId {
|
pub(crate) enum TabId {
|
||||||
#[default]
|
|
||||||
HomePage,
|
HomePage,
|
||||||
JlcDownloader,
|
JlcDownloader,
|
||||||
DbBrowser,
|
DbBrowser,
|
||||||
PartViewer,
|
PartViewer,
|
||||||
}
|
}
|
||||||
|
impl Default for TabId {
|
||||||
|
fn default() -> Self {
|
||||||
|
if let Ok(tab) = crate::utils::app_settings::get_curr_page() {
|
||||||
|
Self::from(tab)
|
||||||
|
} else {
|
||||||
|
Self::HomePage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl TabId {
|
||||||
|
pub fn save(&self) {
|
||||||
|
let v = *self;
|
||||||
|
match crate::utils::app_settings::set_curr_page(v.into()) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => {
|
||||||
|
warn!("Failed to save the current page.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Display for TabId {
|
impl Display for TabId {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
@ -102,6 +143,27 @@ impl Display for TabId {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<TabId> for u32 {
|
||||||
|
fn from(val: TabId) -> Self {
|
||||||
|
match val {
|
||||||
|
TabId::HomePage => 0,
|
||||||
|
TabId::JlcDownloader => 1,
|
||||||
|
TabId::DbBrowser => 2,
|
||||||
|
TabId::PartViewer => 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u32> for TabId {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => Self::HomePage,
|
||||||
|
1 => Self::JlcDownloader,
|
||||||
|
2 => Self::DbBrowser,
|
||||||
|
3 => Self::PartViewer,
|
||||||
|
_ => Self::HomePage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl MainWindow {
|
impl MainWindow {
|
||||||
fn title(&self) -> String {
|
fn title(&self) -> String {
|
||||||
self.title.clone()
|
self.title.clone()
|
||||||
@ -109,6 +171,17 @@ impl MainWindow {
|
|||||||
fn theme(&self) -> Theme {
|
fn theme(&self) -> Theme {
|
||||||
self.theme.clone()
|
self.theme.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn subscription(&self) -> Subscription<MainWindowMsg> {
|
||||||
|
info!("subscription run once.");
|
||||||
|
Subscription::run(step_downloader::StepDownloader::create_process)
|
||||||
|
.map(MainWindowMsg::StepDownloader)
|
||||||
|
// iced::keyboard::on_key_press(|key, modifiers| {
|
||||||
|
// info!("Press the key: {key:?}");
|
||||||
|
// info!("The modifiers: {modifiers:?}");
|
||||||
|
// Some(MainWindowMsg::Nothing)
|
||||||
|
// })
|
||||||
|
}
|
||||||
fn create_tab_btn(&self, tab: TabId) -> Element<'_, MainWindowMsg> {
|
fn create_tab_btn(&self, tab: TabId) -> Element<'_, MainWindowMsg> {
|
||||||
let bstyle = if self.curr_tab == tab {
|
let bstyle = if self.curr_tab == tab {
|
||||||
button::danger
|
button::danger
|
||||||
@ -116,13 +189,12 @@ impl MainWindow {
|
|||||||
button::primary
|
button::primary
|
||||||
};
|
};
|
||||||
let txt = format!("{tab}");
|
let txt = format!("{tab}");
|
||||||
|
|
||||||
let txt = iced::widget::text(txt);
|
let txt = iced::widget::text(txt);
|
||||||
let btn = button(txt).style(bstyle);
|
let btn = button(txt).style(bstyle);
|
||||||
btn.on_press(MainWindowMsg::TabSelected(tab.clone())).into()
|
btn.on_press(MainWindowMsg::TabSelected(tab)).into()
|
||||||
}
|
}
|
||||||
fn update(&mut self, msg: MainWindowMsg) {
|
fn update(&mut self, msg: MainWindowMsg) {
|
||||||
println!("Process the msg: {msg:?}");
|
info!("Process the msg: {msg:?}");
|
||||||
match msg {
|
match msg {
|
||||||
MainWindowMsg::ThemeChanged(theme) => {
|
MainWindowMsg::ThemeChanged(theme) => {
|
||||||
self.theme = theme.clone();
|
self.theme = theme.clone();
|
||||||
@ -134,14 +206,15 @@ impl MainWindow {
|
|||||||
self.title = title;
|
self.title = title;
|
||||||
}
|
}
|
||||||
MainWindowMsg::Nothing => {
|
MainWindowMsg::Nothing => {
|
||||||
println!("Nothing");
|
info!("Nothing");
|
||||||
iced::debug::time("Test".to_owned());
|
iced::debug::time("Test".to_owned());
|
||||||
}
|
}
|
||||||
MainWindowMsg::TabSelected(i) => {
|
MainWindowMsg::TabSelected(i) => {
|
||||||
self.curr_tab = i;
|
self.curr_tab = i;
|
||||||
|
self.curr_tab.save();
|
||||||
}
|
}
|
||||||
MainWindowMsg::HomePage(msg) => {
|
MainWindowMsg::HomePage(msg) => {
|
||||||
println!("update HomePage");
|
info!("update HomePage");
|
||||||
self.home_page.update(msg);
|
self.home_page.update(msg);
|
||||||
}
|
}
|
||||||
MainWindowMsg::JlcDownloader(msg) => {
|
MainWindowMsg::JlcDownloader(msg) => {
|
||||||
@ -153,6 +226,13 @@ impl MainWindow {
|
|||||||
MainWindowMsg::PartViewer(part_viewer_msg) => {
|
MainWindowMsg::PartViewer(part_viewer_msg) => {
|
||||||
self.part_viewer.update(part_viewer_msg);
|
self.part_viewer.update(part_viewer_msg);
|
||||||
}
|
}
|
||||||
|
MainWindowMsg::SearchKeywordChanged(k) => {
|
||||||
|
self.jlc_downloader
|
||||||
|
.update(JlcDownloaderMsg::KeywordChanged(k));
|
||||||
|
}
|
||||||
|
MainWindowMsg::StepDownloader(msg) => {
|
||||||
|
info!("StepDownloaderEvent: {msg:?}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn view(&self) -> Element<'_, MainWindowMsg> {
|
fn view(&self) -> Element<'_, MainWindowMsg> {
|
||||||
@ -175,7 +255,7 @@ impl MainWindow {
|
|||||||
|
|
||||||
pub trait TabContent {
|
pub trait TabContent {
|
||||||
type TabMessage;
|
type TabMessage;
|
||||||
fn update(&self, msg: Self::TabMessage);
|
fn update(&mut self, msg: Self::TabMessage);
|
||||||
fn view(&self) -> Element<'_, MainWindowMsg> {
|
fn view(&self) -> Element<'_, MainWindowMsg> {
|
||||||
iced::widget::Container::new(self.content())
|
iced::widget::Container::new(self.content())
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use iced::{Length, alignment::Horizontal, widget::Column};
|
use iced::{Length, alignment::Horizontal, widget::Column};
|
||||||
|
use log::info;
|
||||||
|
|
||||||
use crate::ui::main_window::{MainWindowMsg, TabContent};
|
use crate::ui::main_window::{MainWindowMsg, TabContent};
|
||||||
|
|
||||||
@ -13,10 +14,10 @@ pub enum PartViewerMsg {
|
|||||||
impl TabContent for PartViewer {
|
impl TabContent for PartViewer {
|
||||||
type TabMessage = PartViewerMsg;
|
type TabMessage = PartViewerMsg;
|
||||||
|
|
||||||
fn update(&self, msg: Self::TabMessage) {
|
fn update(&mut self, msg: Self::TabMessage) {
|
||||||
match msg {
|
match msg {
|
||||||
PartViewerMsg::Nothing => {
|
PartViewerMsg::Nothing => {
|
||||||
println!("This function not allowed.");
|
info!("This function not allowed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
src/utils/lazy.rs
Normal file
22
src/utils/lazy.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use lazy_static::lazy_static;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum UiCmd {
|
||||||
|
Repaint,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref UiBroadcast: broadcast::Sender<UiCmd> = {
|
||||||
|
let (tx, _) = broadcast::channel(10);
|
||||||
|
tx
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ui_broadcast_sender() -> broadcast::Sender<UiCmd> {
|
||||||
|
UiBroadcast.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ui_broadcast_receiver() -> broadcast::Receiver<UiCmd> {
|
||||||
|
UiBroadcast.subscribe()
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
pub mod app_settings;
|
pub mod app_settings;
|
||||||
|
pub mod lazy;
|
||||||
pub mod step_downloader;
|
pub mod step_downloader;
|
||||||
pub mod winsparkle;
|
pub mod winsparkle;
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
use iced::futures;
|
||||||
|
use log::info;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use iced::task::{Never, Sipper, sipper};
|
||||||
|
pub struct StepDownloader {
|
||||||
|
nothing: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum StepDownloaderMsg {
|
||||||
|
Nothing,
|
||||||
|
SearchKeyword(String),
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum StepDownloaderEvent {
|
||||||
|
Nothing,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StepDownloader {
|
||||||
|
pub fn create_process() -> impl Sipper<Never, StepDownloaderEvent> {
|
||||||
|
info!("Start create the process.");
|
||||||
|
sipper(async |mut output| {
|
||||||
|
loop {
|
||||||
|
output.send(StepDownloaderEvent::Nothing).await;
|
||||||
|
info!("In process print once.");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
info!("In process print tick A.");
|
||||||
|
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||||
|
// output.send(StepDownloaderEvent::Nothing).await;
|
||||||
|
info!("In process print tick A.");
|
||||||
|
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
409
src/utils/step_downloader.rs.old.rs
Normal file
409
src/utils/step_downloader.rs.old.rs
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
use anyhow::{Result, anyhow};
|
||||||
|
use image::DynamicImage;
|
||||||
|
use log::info;
|
||||||
|
use tokio::sync::mpsc::{Receiver, Sender, channel};
|
||||||
|
|
||||||
|
use crate::utils::lazy;
|
||||||
|
|
||||||
|
pub struct StepDownloader {
|
||||||
|
status: SearchStatus,
|
||||||
|
rx_status: Receiver<SearchStatus>,
|
||||||
|
tx_cmd: Sender<SearchCmd>,
|
||||||
|
rx_total: Receiver<SearchTotalInfo>,
|
||||||
|
rx_item: Receiver<SearchItemInfo>,
|
||||||
|
}
|
||||||
|
impl Default for StepDownloader {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SearchTotalInfo {
|
||||||
|
total: u32,
|
||||||
|
curr_page: u32,
|
||||||
|
}
|
||||||
|
pub struct SearchItemInfo {
|
||||||
|
name: String,
|
||||||
|
imgs: Vec<DynamicImage>,
|
||||||
|
stp_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StepDownloader {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let status = SearchStatus::Ready;
|
||||||
|
let (rx_status, tx_cmd, rx_total, rx_item) = Self::search_process();
|
||||||
|
Self {
|
||||||
|
status,
|
||||||
|
rx_status,
|
||||||
|
tx_cmd,
|
||||||
|
rx_total,
|
||||||
|
rx_item,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StepDownloader {
|
||||||
|
fn search_process() -> (
|
||||||
|
Receiver<SearchStatus>,
|
||||||
|
Sender<SearchCmd>,
|
||||||
|
Receiver<SearchTotalInfo>,
|
||||||
|
Receiver<SearchItemInfo>,
|
||||||
|
) {
|
||||||
|
log::info!("Create the search_process.");
|
||||||
|
let (tx_status, rx_status) = channel(5);
|
||||||
|
let (tx_cmd, mut rx_cmd) = channel(5);
|
||||||
|
let (tx_total, mut rx_total) = channel(10);
|
||||||
|
let (tx_item, mut rx_item) = channel(10);
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
if let Some(cmd) = rx_cmd.recv().await {
|
||||||
|
match cmd {
|
||||||
|
SearchCmd::Search(keyword, cur, lim) => {
|
||||||
|
log::info!("To search : {}", keyword.clone());
|
||||||
|
if tx_status.send(SearchStatus::Searching).await.is_err() {
|
||||||
|
log::error!("Failed to send the searching state.")
|
||||||
|
}
|
||||||
|
match Self::search_keyword(&keyword, cur, lim).await {
|
||||||
|
Ok(r) => {
|
||||||
|
//搜索到了Root列表
|
||||||
|
let total = SearchTotalInfo {
|
||||||
|
total: r.result.total,
|
||||||
|
curr_page: cur,
|
||||||
|
};
|
||||||
|
tx_total.send(total).await.unwrap();
|
||||||
|
|
||||||
|
//TODO: complete the search process.
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to search by keyword : {e:?}");
|
||||||
|
if tx_status.send(SearchStatus::Error).await.is_err() {
|
||||||
|
log::error!("Failed to send tx_status:Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("Search for {} done.", keyword.clone());
|
||||||
|
if tx_status.send(SearchStatus::Done).await.is_err() {
|
||||||
|
log::error!("Failed to send the searching state.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SearchCmd::Stop => {
|
||||||
|
log::warn!(
|
||||||
|
"Received cmd to Stop the search process in first rx_cmd.recv, This may cause error!"
|
||||||
|
);
|
||||||
|
if tx_status.send(SearchStatus::Error).await.is_err() {
|
||||||
|
log::error!("Failed to send tx_status:Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SearchCmd::Exit => {
|
||||||
|
log::warn!(
|
||||||
|
"Received cmd to Exit the search_process, that means to exit the whole Application, Whatch that!"
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(rx_status, tx_cmd, rx_total, rx_item)
|
||||||
|
}
|
||||||
|
pub fn get_status(&mut self) -> SearchStatus {
|
||||||
|
if let Ok(status) = self.rx_status.try_recv() {
|
||||||
|
self.status = status;
|
||||||
|
}
|
||||||
|
self.status.clone()
|
||||||
|
}
|
||||||
|
pub fn search(&self, keyword: &str, cur_page: u32, page_lim: u32) {
|
||||||
|
if let Ok(()) =
|
||||||
|
self.tx_cmd
|
||||||
|
.try_send(SearchCmd::Search(keyword.to_owned(), cur_page, page_lim))
|
||||||
|
{
|
||||||
|
log::info!("Success send the search {keyword} cmd.");
|
||||||
|
} else {
|
||||||
|
log::warn!("Failed to send the search {keyword} cmd.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn exit(&self) {
|
||||||
|
let _ = self.tx_cmd.try_send(SearchCmd::Exit);
|
||||||
|
}
|
||||||
|
pub fn stop(&self) {
|
||||||
|
let _ = self.tx_cmd.try_send(SearchCmd::Stop);
|
||||||
|
}
|
||||||
|
async fn search_has_device(has_device: &str) -> Result<String> {
|
||||||
|
let mut form_maps = std::collections::HashMap::new();
|
||||||
|
form_maps.insert("uuids[]", has_device);
|
||||||
|
let url = "https://pro.lceda.cn/api/devices/searchByIds";
|
||||||
|
let resp = reqwest::Client::new()
|
||||||
|
.post(url)
|
||||||
|
.form(&form_maps)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
let text = resp.text().await?;
|
||||||
|
let v: serde_json::Value = serde_json::from_str(text.as_str())?;
|
||||||
|
let id = v["result"][0]["attributes"]["3D Model"].clone();
|
||||||
|
if let serde_json::Value::String(id) = id {
|
||||||
|
return Ok(id.clone());
|
||||||
|
}
|
||||||
|
Err(anyhow::Error::msg("Failed to parse the json."))
|
||||||
|
}
|
||||||
|
async fn search_model_id(uuid: &str) -> Result<String> {
|
||||||
|
let mut form_maps = std::collections::HashMap::new();
|
||||||
|
form_maps.insert("uuids[]", uuid);
|
||||||
|
form_maps.insert("dataStr", "yes");
|
||||||
|
let url = "https://pro.lceda.cn/api/components/searchByIds?forceOnline=1";
|
||||||
|
|
||||||
|
let resp = reqwest::Client::new()
|
||||||
|
.post(url)
|
||||||
|
.form(&form_maps)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
let text = resp.text().await?;
|
||||||
|
let v: serde_json::Value = serde_json::from_str(text.as_str())?;
|
||||||
|
info!("In search_model_id: The v is : {v:#?}");
|
||||||
|
let data_str: serde_json::Value = v["result"][0]["dataStr"].clone();
|
||||||
|
info!("The data str Value: {data_str:#?}");
|
||||||
|
if let serde_json::Value::String(data_str) = data_str {
|
||||||
|
let data_str: DataStr = serde_json::from_str(data_str.as_str())?;
|
||||||
|
if let Some(model) = data_str.model {
|
||||||
|
return Ok(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(anyhow::Error::msg("Failed to parse the json."))
|
||||||
|
}
|
||||||
|
async fn download_step(id: &str) -> Result<String> {
|
||||||
|
let url = format!("https://modules.lceda.cn/qAxj6KHrDKw4blvCG8QJPs7Y/{id}");
|
||||||
|
let url = url.as_str();
|
||||||
|
let resp = reqwest::get(url).await?;
|
||||||
|
let text = resp.text().await?;
|
||||||
|
Ok(text)
|
||||||
|
}
|
||||||
|
async fn search_keyword(
|
||||||
|
keyword: &str,
|
||||||
|
cur_page: u32,
|
||||||
|
page_size: u32,
|
||||||
|
) -> Result<KeywordSearchRoot> {
|
||||||
|
let mut form_maps = std::collections::HashMap::new();
|
||||||
|
form_maps.insert("keyword", keyword);
|
||||||
|
let cur_page = &format!("{cur_page}");
|
||||||
|
let page_size = &format!("{page_size}");
|
||||||
|
form_maps.insert("curPage", cur_page);
|
||||||
|
form_maps.insert("pageSize", page_size);
|
||||||
|
let resp = reqwest::Client::new()
|
||||||
|
.post("https://pro.lceda.cn/api/eda/product/search")
|
||||||
|
.form(&form_maps)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
let text = resp.text().await?;
|
||||||
|
let j: KeywordSearchRoot = serde_json::from_str(&text)?;
|
||||||
|
Ok(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum SearchCmd {
|
||||||
|
Search(String, u32, u32),
|
||||||
|
Stop,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum SearchStatus {
|
||||||
|
Ready,
|
||||||
|
Error,
|
||||||
|
Searching,
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod StepDownloaderTest {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use log::info;
|
||||||
|
use reqwest;
|
||||||
|
|
||||||
|
// #[tokio::test]
|
||||||
|
// pub async fn test_search() {
|
||||||
|
// env_logger::try_init().unwrap();
|
||||||
|
// log::info!("To test search `STM32`");
|
||||||
|
// let d = StepDownloader::default();
|
||||||
|
// d.search("STM32");
|
||||||
|
// d.exit();
|
||||||
|
// }
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_search_func() {
|
||||||
|
assert!(env_logger::try_init().is_ok());
|
||||||
|
log::info!("Start test for call search_keyword function.");
|
||||||
|
test_search("STM32").await;
|
||||||
|
// test_search("STEM32F103").await;
|
||||||
|
// test_search("TPS54302").await;
|
||||||
|
}
|
||||||
|
async fn test_search_uuids(d: &str) {
|
||||||
|
let rst = super::StepDownloader::search_has_device(d).await;
|
||||||
|
match rst {
|
||||||
|
Ok(v) => {
|
||||||
|
log::info!("Get the uuids: {}", v.clone());
|
||||||
|
match super::StepDownloader::search_model_id(v.clone().as_str()).await {
|
||||||
|
Ok(i) => {
|
||||||
|
log::info!("Get the model_id: {}", i.clone());
|
||||||
|
match super::StepDownloader::download_step(i.as_str()).await {
|
||||||
|
Ok(t) => {
|
||||||
|
info!("Success to get the content.");
|
||||||
|
|
||||||
|
let mut file = std::fs::OpenOptions::new()
|
||||||
|
.read(false)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.append(false)
|
||||||
|
.open("web_temp/first_step.step")
|
||||||
|
.unwrap();
|
||||||
|
file.write_all(t.as_bytes()).unwrap();
|
||||||
|
let _ = file.flush();
|
||||||
|
}
|
||||||
|
Err(e) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to call search_model_id: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to call search_has_device: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn test_search(k: &str) {
|
||||||
|
let rst = super::StepDownloader::search_keyword(k, 1, 10).await;
|
||||||
|
match rst {
|
||||||
|
Ok(v) => {
|
||||||
|
log::info!("Search done! ========================");
|
||||||
|
log::info!("The result:\r\n\r\n {v:#?}");
|
||||||
|
let item = &v.result.productList[0];
|
||||||
|
let uuids = item.hasDevice.clone();
|
||||||
|
if let Some(v) = uuids {
|
||||||
|
test_search_uuids(v.as_str()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Error occured.");
|
||||||
|
log::error!("Error : {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #[tokio::test]
|
||||||
|
pub async fn test_post() {
|
||||||
|
if env_logger::try_init().is_ok() {
|
||||||
|
log::info!("The env_logger::try_init done.");
|
||||||
|
}
|
||||||
|
log::info!("Start test");
|
||||||
|
match reqwest::get("https://www.baidu.com").await {
|
||||||
|
Ok(r) => {
|
||||||
|
log::info!("Get www.baidu.com : {r:?}");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to get www.baidu.com : {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("==========================================");
|
||||||
|
let mut form = std::collections::HashMap::new();
|
||||||
|
form.insert("keyword", "STM32");
|
||||||
|
form.insert("currPage", "1");
|
||||||
|
form.insert("pageSize", "10");
|
||||||
|
let resp = reqwest::Client::new()
|
||||||
|
.post("https://pro.lceda.cn/api/eda/product/search")
|
||||||
|
.form(&form)
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match resp {
|
||||||
|
Ok(mut r) => {
|
||||||
|
if std::fs::create_dir("web_temp").is_ok() {
|
||||||
|
log::info!("Created the dir: web_temp");
|
||||||
|
} else {
|
||||||
|
log::warn!("Failed to create the dir: web_temp");
|
||||||
|
}
|
||||||
|
let mut file = std::fs::OpenOptions::new()
|
||||||
|
.read(false)
|
||||||
|
.write(true)
|
||||||
|
// .create(true)
|
||||||
|
.append(false)
|
||||||
|
.open("web_temp/first_resp.json")
|
||||||
|
.unwrap();
|
||||||
|
let enc = r.headers();
|
||||||
|
info!("Get the headers: {enc:?}");
|
||||||
|
info!("-------------------------------------------");
|
||||||
|
info!("Get the status: {:?}", r.status());
|
||||||
|
info!("-------------------------------------------");
|
||||||
|
info!("Get the http version: {:?}", r.version());
|
||||||
|
info!("-------------------------------------------");
|
||||||
|
info!("Get the url : {:?}", r.url());
|
||||||
|
// let text = r.text_with_charset("utf-8").await.unwrap();
|
||||||
|
// file.write_all(text.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
let chunk = r.chunk().await.unwrap();
|
||||||
|
if let Some(data) = chunk {
|
||||||
|
let v = data.to_vec();
|
||||||
|
let data = v.as_slice();
|
||||||
|
file.write_all(data).unwrap();
|
||||||
|
log::info!("All content has been wrote to {file:?}");
|
||||||
|
} else {
|
||||||
|
log::warn!("Failed to parse and save data to file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Error occured: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct KeywordSearchRoot {
|
||||||
|
code: u32,
|
||||||
|
success: bool,
|
||||||
|
result: KeywordSearchResult,
|
||||||
|
}
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct KeywordSearchResult {
|
||||||
|
total: u32,
|
||||||
|
productList: Vec<KeywordSearchListItem>,
|
||||||
|
}
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct KeywordSearchListItem {
|
||||||
|
id: u32,
|
||||||
|
code: String,
|
||||||
|
image: Option<KeywordChipImages>,
|
||||||
|
hasDevice: Option<String>,
|
||||||
|
}
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct KeywordChipImages(String);
|
||||||
|
impl KeywordChipImages {
|
||||||
|
pub fn imgs(&self) -> Vec<String> {
|
||||||
|
let urls = self.0.clone();
|
||||||
|
let list = urls.split("<$>");
|
||||||
|
let mut rst = Vec::new();
|
||||||
|
for i in list {
|
||||||
|
rst.push(i.to_string());
|
||||||
|
}
|
||||||
|
rst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct KeywordDevInfo {
|
||||||
|
uuid: String,
|
||||||
|
description: String,
|
||||||
|
title: String,
|
||||||
|
images: Vec<String>,
|
||||||
|
attributes: KeywordAttributes,
|
||||||
|
}
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct KeywordAttributes {
|
||||||
|
Datasheet: Option<String>,
|
||||||
|
}
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct DataStr {
|
||||||
|
model: Option<String>,
|
||||||
|
src: Option<String>,
|
||||||
|
_type: Option<String>,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user