|
@@ -3,25 +3,88 @@ mod db;
|
|
|
mod menu;
|
|
mod menu;
|
|
|
mod model;
|
|
mod model;
|
|
|
|
|
|
|
|
|
|
+use std::cell::RefCell;
|
|
|
use std::collections::HashMap;
|
|
use std::collections::HashMap;
|
|
|
use std::i64;
|
|
use std::i64;
|
|
|
|
|
+use std::rc::Rc;
|
|
|
|
|
|
|
|
use iced::widget::{row, text};
|
|
use iced::widget::{row, text};
|
|
|
use iced::{Element, Length, Task};
|
|
use iced::{Element, Length, Task};
|
|
|
-use sqlx::SqlitePool;
|
|
|
|
|
|
|
|
|
|
-use crate::data::{
|
|
|
|
|
- TemplateCollection, TemplateEntry, TemplateEnvironmentVariable, TemplateRequest,
|
|
|
|
|
- TemplateWorkspace, TemplateWorkspaceEnvironment,
|
|
|
|
|
-};
|
|
|
|
|
-use crate::menu::{WorkspaceMenu, WorkspaceMenuMessage};
|
|
|
|
|
-use crate::model::{RequestParams, WorkspaceEntry, WorkspaceEntryType};
|
|
|
|
|
|
|
+use crate::data::{RCell, TemplateWorkspace, WorkspaceEnvironment};
|
|
|
|
|
+use crate::db::{Workspace, WorkspaceEntryCreate};
|
|
|
|
|
+use crate::menu::{EntryMenuMessage, WorkspaceMenu, WorkspaceMenuMessage};
|
|
|
|
|
+use crate::model::WorkspaceEntryItem;
|
|
|
|
|
|
|
|
fn main() -> iced::Result {
|
|
fn main() -> iced::Result {
|
|
|
tracing_subscriber::fmt::init();
|
|
tracing_subscriber::fmt::init();
|
|
|
iced::run("restEZ", update, view)
|
|
iced::run("restEZ", update, view)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+pub struct AppState {
|
|
|
|
|
+ /// Sqlite database. Just an Arc so cheap to clone.
|
|
|
|
|
+ db: sqlx::sqlite::SqlitePool,
|
|
|
|
|
+
|
|
|
|
|
+ /// Workspace buffer. Populated by entries only when necessary.
|
|
|
|
|
+ workspaces: Vec<Rc<RefCell<TemplateWorkspace>>>,
|
|
|
|
|
+ ws_buffer: String,
|
|
|
|
|
+
|
|
|
|
|
+ ws_menu: WorkspaceMenu,
|
|
|
|
|
+
|
|
|
|
|
+ ws_current: Option<RCell<TemplateWorkspace>>,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl AppState {
|
|
|
|
|
+ pub async fn new() -> Self {
|
|
|
|
|
+ tracing::info!("Connecting to DB");
|
|
|
|
|
+ let db = db::init("sqlite:/home/biblius/codium/rusty/restez/restez.db").await;
|
|
|
|
|
+
|
|
|
|
|
+ let workspaces: Vec<_> = db::list_workspaces(db.clone())
|
|
|
|
|
+ .await
|
|
|
|
|
+ .unwrap()
|
|
|
|
|
+ .into_iter()
|
|
|
|
|
+ .map(|ws| Rc::new(RefCell::new(TemplateWorkspace::from(ws))))
|
|
|
|
|
+ .collect();
|
|
|
|
|
+
|
|
|
|
|
+ let ws_menu = WorkspaceMenu {
|
|
|
|
|
+ choices: (0..workspaces.len()).collect(),
|
|
|
|
|
+ expanded: false,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ tracing::info!("State loaded");
|
|
|
|
|
+
|
|
|
|
|
+ Self {
|
|
|
|
|
+ db,
|
|
|
|
|
+ workspaces,
|
|
|
|
|
+ ws_buffer: String::with_capacity(64),
|
|
|
|
|
+ ws_menu,
|
|
|
|
|
+ ws_current: None,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl Default for AppState {
|
|
|
|
|
+ fn default() -> Self {
|
|
|
|
|
+ iced::futures::executor::block_on(AppState::new())
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Debug, Clone)]
|
|
|
|
|
+enum Message {
|
|
|
|
|
+ AddWorkspace,
|
|
|
|
|
+ WorkspaceAdded(Workspace),
|
|
|
|
|
+ ReloadWorkspaces(Vec<Workspace>),
|
|
|
|
|
+ NewWsBufferContentChange(String),
|
|
|
|
|
+ Noop,
|
|
|
|
|
+ WorkspaceMenu(WorkspaceMenuMessage),
|
|
|
|
|
+
|
|
|
|
|
+ WorkspaceEnvsInit(HashMap<i64, WorkspaceEnvironment>),
|
|
|
|
|
+ WorkspaceEntriesInit(Vec<WorkspaceEntryItem>),
|
|
|
|
|
+
|
|
|
|
|
+ EntryMenu(EntryMenuMessage),
|
|
|
|
|
+ WorkspaceEntryCreated(WorkspaceEntryItem),
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
macro_rules! unwrap {
|
|
macro_rules! unwrap {
|
|
|
($result:ident, $msg:path) => {
|
|
($result:ident, $msg:path) => {
|
|
|
match $result {
|
|
match $result {
|
|
@@ -44,11 +107,14 @@ macro_rules! unwrap {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn update(state: &mut AppState, message: Message) -> Task<Message> {
|
|
fn update(state: &mut AppState, message: Message) -> Task<Message> {
|
|
|
|
|
+ tracing::debug!("Message: {message:#?}");
|
|
|
|
|
+
|
|
|
match message {
|
|
match message {
|
|
|
- Message::WorkspaceAdded => {
|
|
|
|
|
- return Task::perform(list_workspaces(state.db.clone()), |ws| {
|
|
|
|
|
- unwrap!(ws, Message::ReloadWorkspaces)
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ Message::WorkspaceAdded(ws) => {
|
|
|
|
|
+ tracing::info!("Workspace added: {:?}", ws);
|
|
|
|
|
+ state.workspaces.push(Rc::new(RefCell::new(ws.into())));
|
|
|
|
|
+ state.workspaces.sort_by_key(|ws| ws.borrow().name.clone());
|
|
|
|
|
+ state.ws_menu.choices = (0..state.workspaces.len()).collect()
|
|
|
}
|
|
}
|
|
|
Message::NewWsBufferContentChange(content) => {
|
|
Message::NewWsBufferContentChange(content) => {
|
|
|
state.ws_buffer = content;
|
|
state.ws_buffer = content;
|
|
@@ -58,359 +124,164 @@ fn update(state: &mut AppState, message: Message) -> Task<Message> {
|
|
|
return Task::none();
|
|
return Task::none();
|
|
|
}
|
|
}
|
|
|
tracing::info!("Adding workspace: {}", state.ws_buffer);
|
|
tracing::info!("Adding workspace: {}", state.ws_buffer);
|
|
|
- return Task::perform(
|
|
|
|
|
- create_workspace(state.db.clone(), std::mem::take(&mut state.ws_buffer)),
|
|
|
|
|
- |_| Message::WorkspaceAdded,
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ let db = state.db.clone();
|
|
|
|
|
+ let ws_buffer = std::mem::take(&mut state.ws_buffer);
|
|
|
|
|
+ return Task::perform(db::create_workspace(db, ws_buffer), |ws| {
|
|
|
|
|
+ unwrap!(ws, Message::WorkspaceAdded)
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
Message::ReloadWorkspaces(mut ws) => {
|
|
Message::ReloadWorkspaces(mut ws) => {
|
|
|
- ws.sort_by(|a, b| a.name.cmp(&b.name));
|
|
|
|
|
- state.workspaces = ws.into_iter().map(TemplateWorkspace::from).collect();
|
|
|
|
|
|
|
+ ws.sort_by_key(|a| a.name.clone());
|
|
|
|
|
+ state.workspaces = ws
|
|
|
|
|
+ .into_iter()
|
|
|
|
|
+ .map(|ws| Rc::new(RefCell::new(TemplateWorkspace::from(ws))))
|
|
|
|
|
+ .collect();
|
|
|
state.ws_menu.choices = (0..state.workspaces.len()).collect()
|
|
state.ws_menu.choices = (0..state.workspaces.len()).collect()
|
|
|
}
|
|
}
|
|
|
Message::WorkspaceMenu(msg) => {
|
|
Message::WorkspaceMenu(msg) => {
|
|
|
state.ws_menu.update(&msg);
|
|
state.ws_menu.update(&msg);
|
|
|
match msg {
|
|
match msg {
|
|
|
WorkspaceMenuMessage::Select(i) => {
|
|
WorkspaceMenuMessage::Select(i) => {
|
|
|
- let workspace = state.workspaces.swap_remove(i);
|
|
|
|
|
- let id = workspace.id;
|
|
|
|
|
|
|
+ let workspace = state.workspaces[i].clone();
|
|
|
|
|
+
|
|
|
|
|
+ let id = workspace.borrow().id;
|
|
|
|
|
+
|
|
|
state.ws_current = Some(workspace);
|
|
state.ws_current = Some(workspace);
|
|
|
- return Task::perform(get_entries(state.db.clone(), id as i64), |entries| {
|
|
|
|
|
- unwrap!(entries, Message::WorkspaceEntriesLoaded)
|
|
|
|
|
- })
|
|
|
|
|
|
|
+
|
|
|
|
|
+ return Task::perform(
|
|
|
|
|
+ db::get_workspace_entries(state.db.clone(), id as i64),
|
|
|
|
|
+ |entries| unwrap!(entries, Message::WorkspaceEntriesInit),
|
|
|
|
|
+ )
|
|
|
.chain(Task::perform(
|
|
.chain(Task::perform(
|
|
|
- get_environments(state.db.clone(), id as i64),
|
|
|
|
|
- |envs| unwrap!(envs, Message::WorkspaceEnvsLoaded),
|
|
|
|
|
|
|
+ db::get_environments(state.db.clone(), id as i64),
|
|
|
|
|
+ |envs| unwrap!(envs, Message::WorkspaceEnvsInit),
|
|
|
));
|
|
));
|
|
|
}
|
|
}
|
|
|
_ => {}
|
|
_ => {}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- Message::WorkspaceEnvsLoaded(envs) => {
|
|
|
|
|
- let Some(workspace) = &mut state.ws_current else {
|
|
|
|
|
|
|
+ Message::WorkspaceEnvsInit(envs) => {
|
|
|
|
|
+ let Some(ref workspace) = state.ws_current else {
|
|
|
tracing::warn!("Workspace env loaded, but no active workspace");
|
|
tracing::warn!("Workspace env loaded, but no active workspace");
|
|
|
return Task::none();
|
|
return Task::none();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ let mut workspace = workspace.borrow_mut();
|
|
|
|
|
+
|
|
|
workspace.environments = envs;
|
|
workspace.environments = envs;
|
|
|
|
|
|
|
|
if let Some(env) = workspace.environments.values().next() {
|
|
if let Some(env) = workspace.environments.values().next() {
|
|
|
workspace.env_current = Some(env.id);
|
|
workspace.env_current = Some(env.id);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- Message::WorkspaceEntriesLoaded(items) => {
|
|
|
|
|
- let Some(workspace) = &mut state.ws_current else {
|
|
|
|
|
|
|
+ Message::WorkspaceEntriesInit(items) => {
|
|
|
|
|
+ let Some(ref workspace) = state.ws_current else {
|
|
|
tracing::warn!("Workspace entries loaded, but no active workspace");
|
|
tracing::warn!("Workspace entries loaded, but no active workspace");
|
|
|
return Task::none();
|
|
return Task::none();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ let mut workspace = workspace.borrow_mut();
|
|
|
|
|
+
|
|
|
workspace.update_entries(items);
|
|
workspace.update_entries(items);
|
|
|
}
|
|
}
|
|
|
- Message::Noop => {}
|
|
|
|
|
- }
|
|
|
|
|
- Task::none()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-fn view(state: &AppState) -> Element<'_, Message> {
|
|
|
|
|
- let menus = state.ws_menu.view(&state.workspaces, &state.ws_buffer);
|
|
|
|
|
|
|
+ Message::EntryMenu(msg) => match msg {
|
|
|
|
|
+ EntryMenuMessage::Add((menu_id, params)) => {
|
|
|
|
|
+ let Some(ws) = state.ws_current.as_ref() else {
|
|
|
|
|
+ return Task::none();
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- let main = match state.ws_current {
|
|
|
|
|
- Some(ref ws) => {
|
|
|
|
|
- // let sidebar = column![text(ws.name)].width(Length::Fill);
|
|
|
|
|
- match ws.req_current {
|
|
|
|
|
- Some(ref _req) => {
|
|
|
|
|
- // TODO: Display request
|
|
|
|
|
- iced::widget::container("TODO: Request editor")
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ let ws_id = ws.borrow().id;
|
|
|
|
|
|
|
|
- None => {
|
|
|
|
|
- // TODO: Display workspace stuff
|
|
|
|
|
- iced::widget::container("TODO: Workspace stuff")
|
|
|
|
|
|
|
+ match menu_id {
|
|
|
|
|
+ Some(id) => {
|
|
|
|
|
+ if let Some(menu) = ws.borrow_mut().menus.get_mut(&id) {
|
|
|
|
|
+ tracing::debug!("Updating menu: {id} ({msg:?})");
|
|
|
|
|
+ menu.update(&msg);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ None => ws.borrow_mut().menu.update(&msg),
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- None => iced::widget::container("Select a workspace to start working."),
|
|
|
|
|
- }
|
|
|
|
|
- .center(Length::Fill)
|
|
|
|
|
- .padding(10);
|
|
|
|
|
-
|
|
|
|
|
- iced::widget::column![
|
|
|
|
|
- menus,
|
|
|
|
|
- main,
|
|
|
|
|
- row![text(format!(
|
|
|
|
|
- "Workspace: {}",
|
|
|
|
|
- state.ws_current.as_ref().map_or("", |w| &w.name)
|
|
|
|
|
- ))]
|
|
|
|
|
- ]
|
|
|
|
|
- .into()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#[derive(Debug, Clone)]
|
|
|
|
|
-enum Message {
|
|
|
|
|
- ReloadWorkspaces(Vec<model::Workspace>),
|
|
|
|
|
- WorkspaceAdded,
|
|
|
|
|
- AddWorkspace,
|
|
|
|
|
- NewWsBufferContentChange(String),
|
|
|
|
|
- Noop,
|
|
|
|
|
- WorkspaceMenu(WorkspaceMenuMessage),
|
|
|
|
|
-
|
|
|
|
|
- WorkspaceEnvsLoaded(HashMap<i64, TemplateWorkspaceEnvironment>),
|
|
|
|
|
- WorkspaceEntriesLoaded(Vec<TemplateEntry>),
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-pub struct AppState {
|
|
|
|
|
- /// Sqlite database. Just an Arc so cheap to clone.
|
|
|
|
|
- db: sqlx::sqlite::SqlitePool,
|
|
|
|
|
-
|
|
|
|
|
- /// Workspace buffer. Populated by entries only when necessary.
|
|
|
|
|
- workspaces: Vec<TemplateWorkspace>,
|
|
|
|
|
- ws_buffer: String,
|
|
|
|
|
- ws_menu: WorkspaceMenu,
|
|
|
|
|
- ws_current: Option<TemplateWorkspace>,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-impl AppState {
|
|
|
|
|
- pub async fn new() -> Self {
|
|
|
|
|
- tracing::info!("Connecting to DB");
|
|
|
|
|
- let db = db::init("sqlite:/home/biblius/codium/rusty/restez/restez.db").await;
|
|
|
|
|
-
|
|
|
|
|
- let workspaces: Vec<TemplateWorkspace> = list_workspaces(db.clone())
|
|
|
|
|
- .await
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .into_iter()
|
|
|
|
|
- .map(TemplateWorkspace::from)
|
|
|
|
|
- .collect();
|
|
|
|
|
-
|
|
|
|
|
- let ws_menu = WorkspaceMenu {
|
|
|
|
|
- choices: (0..workspaces.len()).collect(),
|
|
|
|
|
- expanded: false,
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- tracing::info!("State loaded");
|
|
|
|
|
-
|
|
|
|
|
- Self {
|
|
|
|
|
- db,
|
|
|
|
|
- workspaces,
|
|
|
|
|
- ws_buffer: String::with_capacity(64),
|
|
|
|
|
- ws_menu,
|
|
|
|
|
- ws_current: None,
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-impl Default for AppState {
|
|
|
|
|
- fn default() -> Self {
|
|
|
|
|
- iced::futures::executor::block_on(AppState::new())
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async fn create_workspace(db: SqlitePool, name: String) -> Result<model::Workspace, String> {
|
|
|
|
|
- match sqlx::query_as!(
|
|
|
|
|
- model::Workspace,
|
|
|
|
|
- "INSERT INTO workspaces (name) VALUES (?) RETURNING id, name",
|
|
|
|
|
- name
|
|
|
|
|
- )
|
|
|
|
|
- .fetch_one(&db)
|
|
|
|
|
- .await
|
|
|
|
|
- {
|
|
|
|
|
- Ok(workspace) => Ok(workspace),
|
|
|
|
|
- Err(e) => Err(e.to_string()),
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-async fn list_workspaces(db: SqlitePool) -> Result<Vec<model::Workspace>, String> {
|
|
|
|
|
- match sqlx::query_as!(model::Workspace, "SELECT id, name FROM workspaces")
|
|
|
|
|
- .fetch_all(&db)
|
|
|
|
|
- .await
|
|
|
|
|
- {
|
|
|
|
|
- Ok(workspaces) => Ok(workspaces),
|
|
|
|
|
- Err(e) => Err(e.to_string()),
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async fn get_entries(db: SqlitePool, workspace_id: i64) -> Result<Vec<TemplateEntry>, String> {
|
|
|
|
|
- let entries = sqlx::query_as!(
|
|
|
|
|
- model::WorkspaceEntry,
|
|
|
|
|
- "SELECT id, workspace_id, parent_id, name, type FROM workspace_entries WHERE workspace_id = ?",
|
|
|
|
|
- workspace_id,
|
|
|
|
|
- )
|
|
|
|
|
- .fetch_all(&db)
|
|
|
|
|
- .await
|
|
|
|
|
- .map_err(|e| e.to_string())?;
|
|
|
|
|
-
|
|
|
|
|
- let mut request_params: HashMap<i64, RequestParams> = sqlx::query_as!(
|
|
|
|
|
- RequestParams,
|
|
|
|
|
- r#"
|
|
|
|
|
- SELECT rp.request_id as id, method as 'method!', url as 'url!', content_type, body
|
|
|
|
|
- FROM request_params rp
|
|
|
|
|
- LEFT JOIN request_bodies rb ON rp.request_id = rb.request_id
|
|
|
|
|
- WHERE workspace_id = ?
|
|
|
|
|
- "#,
|
|
|
|
|
- workspace_id
|
|
|
|
|
- )
|
|
|
|
|
- .fetch_all(&db)
|
|
|
|
|
- .await
|
|
|
|
|
- .map_err(|e| e.to_string())?
|
|
|
|
|
- .into_iter()
|
|
|
|
|
- .map(|req| (req.id, req))
|
|
|
|
|
- .collect();
|
|
|
|
|
-
|
|
|
|
|
- let mut out: Vec<TemplateEntry> = vec![];
|
|
|
|
|
- let mut children: HashMap<i64, Vec<TemplateCollection>> = HashMap::new();
|
|
|
|
|
- let mut child_requests = HashMap::<i64, Vec<TemplateRequest>>::new();
|
|
|
|
|
-
|
|
|
|
|
- for entry in entries {
|
|
|
|
|
- match entry.r#type {
|
|
|
|
|
- WorkspaceEntryType::Request => {
|
|
|
|
|
- let headers = sqlx::query_as!(
|
|
|
|
|
- model::RequestHeader,
|
|
|
|
|
- "SELECT name, value FROM request_headers WHERE request_id = ?",
|
|
|
|
|
- entry.id
|
|
|
|
|
- )
|
|
|
|
|
- .fetch_all(&db)
|
|
|
|
|
- .await
|
|
|
|
|
- .map_err(|e| e.to_string())?;
|
|
|
|
|
-
|
|
|
|
|
- let Some(params) = request_params.remove(&entry.id) else {
|
|
|
|
|
- tracing::warn!("request {} has no params!", entry.id);
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ let input = match params.r#type {
|
|
|
|
|
+ db::WorkspaceEntryType::Request => WorkspaceEntryCreate::Request {
|
|
|
|
|
+ name: String::new(),
|
|
|
|
|
+ workspace_id: ws_id,
|
|
|
|
|
+ parent_id: params.parent_id,
|
|
|
|
|
+ method: "GET".to_string(),
|
|
|
|
|
+ url: String::new(),
|
|
|
|
|
+ },
|
|
|
|
|
+ db::WorkspaceEntryType::Collection => WorkspaceEntryCreate::Collection {
|
|
|
|
|
+ name: String::new(),
|
|
|
|
|
+ workspace_id: ws_id,
|
|
|
|
|
+ parent_id: params.parent_id,
|
|
|
|
|
+ },
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- let parent_id = entry.parent_id;
|
|
|
|
|
- let req = TemplateRequest::from_params_and_headers(entry, params, headers);
|
|
|
|
|
- if let Some(parent) = parent_id {
|
|
|
|
|
- child_requests
|
|
|
|
|
- .entry(parent)
|
|
|
|
|
- .and_modify(|reqs| reqs.push(req.clone()))
|
|
|
|
|
- .or_insert(vec![req.clone()]);
|
|
|
|
|
- } else {
|
|
|
|
|
- out.push(TemplateEntry::Request(req));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return Task::perform(
|
|
|
|
|
+ db::create_workspace_entry(state.db.clone(), input),
|
|
|
|
|
+ |entry| unwrap!(entry, Message::WorkspaceEntryCreated),
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
- WorkspaceEntryType::Collection => {
|
|
|
|
|
- let col = TemplateCollection {
|
|
|
|
|
- id: entry.id,
|
|
|
|
|
- entries: HashMap::new(),
|
|
|
|
|
|
|
+ EntryMenuMessage::Dismiss(id) | EntryMenuMessage::Expand(id) => {
|
|
|
|
|
+ let Some(ws) = state.ws_current.as_ref() else {
|
|
|
|
|
+ return Task::none();
|
|
|
};
|
|
};
|
|
|
- if let Some(parent) = entry.parent_id {
|
|
|
|
|
- children
|
|
|
|
|
- .entry(parent)
|
|
|
|
|
- .and_modify(|p| p.push(col.clone()))
|
|
|
|
|
- .or_insert(vec![col]);
|
|
|
|
|
- } else {
|
|
|
|
|
- out.push(TemplateEntry::Collection(col));
|
|
|
|
|
|
|
+
|
|
|
|
|
+ match id {
|
|
|
|
|
+ Some(id) => {
|
|
|
|
|
+ if let Some(menu) = ws.borrow_mut().menus.get_mut(&id) {
|
|
|
|
|
+ tracing::debug!("Updating menu: {id} ({msg:?})");
|
|
|
|
|
+ menu.update(&msg);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ None => ws.borrow_mut().menu.update(&msg),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn extend_recursive(
|
|
|
|
|
- collection: &mut TemplateCollection,
|
|
|
|
|
- children: &mut HashMap<i64, Vec<TemplateCollection>>,
|
|
|
|
|
- requests: &mut HashMap<i64, Vec<TemplateRequest>>,
|
|
|
|
|
- ) {
|
|
|
|
|
- let id = collection.id as i64;
|
|
|
|
|
- collection.entries.extend(
|
|
|
|
|
- children
|
|
|
|
|
- .remove(&id)
|
|
|
|
|
- .unwrap_or_default()
|
|
|
|
|
- .into_iter()
|
|
|
|
|
- .map(|child| (child.id, TemplateEntry::Collection(child))),
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- collection.entries.extend(
|
|
|
|
|
- requests
|
|
|
|
|
- .remove(&id)
|
|
|
|
|
- .unwrap_or_default()
|
|
|
|
|
- .into_iter()
|
|
|
|
|
- .map(|req| (req.id, TemplateEntry::Request(req))),
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- for entry in collection.entries.values_mut() {
|
|
|
|
|
- let TemplateEntry::Collection(collection) = entry else {
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ Message::WorkspaceEntryCreated(entry) => {
|
|
|
|
|
+ let Some(ws) = state.ws_current.as_ref() else {
|
|
|
|
|
+ return Task::none();
|
|
|
};
|
|
};
|
|
|
- extend_recursive(collection, children, requests);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- for entry in out.iter_mut() {
|
|
|
|
|
- let TemplateEntry::Collection(collection) = entry else {
|
|
|
|
|
- continue;
|
|
|
|
|
- };
|
|
|
|
|
- extend_recursive(collection, &mut children, &mut child_requests);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Ok(out)
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ ws.borrow_mut().insert_entry(entry);
|
|
|
|
|
|
|
|
-async fn get_environments(
|
|
|
|
|
- db: SqlitePool,
|
|
|
|
|
- workspace_id: i64,
|
|
|
|
|
-) -> Result<HashMap<usize, TemplateWorkspaceEnvironment>, String> {
|
|
|
|
|
- let envs = get_workspace_envs(db.clone(), workspace_id).await?;
|
|
|
|
|
-
|
|
|
|
|
- let mut out = HashMap::with_capacity(envs.len());
|
|
|
|
|
-
|
|
|
|
|
- for env in envs {
|
|
|
|
|
- let variables = get_workspace_env_variables(db.clone(), env.id as i64).await?;
|
|
|
|
|
-
|
|
|
|
|
- out.insert(
|
|
|
|
|
- env.id as usize,
|
|
|
|
|
- TemplateWorkspaceEnvironment {
|
|
|
|
|
- id: env.id,
|
|
|
|
|
- name: env.name,
|
|
|
|
|
- variables: variables
|
|
|
|
|
- .into_iter()
|
|
|
|
|
- .map(|v| {
|
|
|
|
|
- (
|
|
|
|
|
- v.name.clone(),
|
|
|
|
|
- TemplateEnvironmentVariable {
|
|
|
|
|
- id: v.id,
|
|
|
|
|
- name: v.name,
|
|
|
|
|
- value: v.value,
|
|
|
|
|
- secret: v.secret,
|
|
|
|
|
- },
|
|
|
|
|
- )
|
|
|
|
|
- })
|
|
|
|
|
- .collect(),
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ tracing::info!("Workspace current: {:#?}", ws);
|
|
|
|
|
+ }
|
|
|
|
|
+ Message::Noop => {}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- Ok(out)
|
|
|
|
|
|
|
+ Task::none()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async fn get_workspace_envs(
|
|
|
|
|
- db: SqlitePool,
|
|
|
|
|
- workspace_id: i64,
|
|
|
|
|
-) -> Result<Vec<model::WorkspaceEnv>, String> {
|
|
|
|
|
- match sqlx::query_as!(
|
|
|
|
|
- model::WorkspaceEnv,
|
|
|
|
|
- "SELECT id, workspace_id, name FROM workspace_envs WHERE workspace_id = $1",
|
|
|
|
|
- workspace_id
|
|
|
|
|
- )
|
|
|
|
|
- .fetch_all(&db)
|
|
|
|
|
- .await
|
|
|
|
|
- {
|
|
|
|
|
- Ok(envs) => Ok(envs),
|
|
|
|
|
- Err(e) => Err(e.to_string()),
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
|
|
+fn view(state: &AppState) -> Element<'_, Message> {
|
|
|
|
|
+ let menus = state.ws_menu.view(&state.workspaces, &state.ws_buffer);
|
|
|
|
|
|
|
|
-async fn get_workspace_env_variables(
|
|
|
|
|
- db: SqlitePool,
|
|
|
|
|
- env_id: i64,
|
|
|
|
|
-) -> Result<Vec<model::WorkspaceEnvVariable>, String> {
|
|
|
|
|
- match sqlx::query_as!(
|
|
|
|
|
- model::WorkspaceEnvVariable,
|
|
|
|
|
- "SELECT id, env_id, name, value, secret FROM workspace_env_variables WHERE env_id = $1",
|
|
|
|
|
- env_id
|
|
|
|
|
- )
|
|
|
|
|
- .fetch_all(&db)
|
|
|
|
|
- .await
|
|
|
|
|
- {
|
|
|
|
|
- Ok(envs) => Ok(envs),
|
|
|
|
|
- Err(e) => Err(e.to_string()),
|
|
|
|
|
|
|
+ match state.ws_current.as_ref().map(|ws| ws.borrow()) {
|
|
|
|
|
+ Some(ws) => {
|
|
|
|
|
+ let ws_name = ws.name.clone();
|
|
|
|
|
+ let main = ws.view();
|
|
|
|
|
+ return iced::widget::column![
|
|
|
|
|
+ menus,
|
|
|
|
|
+ main,
|
|
|
|
|
+ row![text(format!("Workspace: {}", ws_name))]
|
|
|
|
|
+ ]
|
|
|
|
|
+ .into();
|
|
|
|
|
+ }
|
|
|
|
|
+ None => {
|
|
|
|
|
+ let main = iced::widget::container("Select a workspace to start working.")
|
|
|
|
|
+ .center(Length::Fill)
|
|
|
|
|
+ .padding(10);
|
|
|
|
|
+ iced::widget::column![
|
|
|
|
|
+ menus,
|
|
|
|
|
+ main,
|
|
|
|
|
+ row![text(format!(
|
|
|
|
|
+ "Workspace: {}",
|
|
|
|
|
+ state
|
|
|
|
|
+ .ws_current
|
|
|
|
|
+ .as_ref()
|
|
|
|
|
+ .map_or(String::new(), |w| w.borrow().name.clone())
|
|
|
|
|
+ ))]
|
|
|
|
|
+ ]
|
|
|
|
|
+ .into()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|