mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-12-07 12:42:36 +00:00
configurator: add new configurator
add new configurator Tracked-On: #6691 Signed-off-by: Weiyi Feng <weiyix.feng@intel.com> (cherry picked from commit 1232a7229af7ed60706cc725acf955ccbd8ca035)
This commit is contained in:
316
misc/config_tools/configurator/src-tauri/src/configurator.rs
Normal file
316
misc/config_tools/configurator/src-tauri/src/configurator.rs
Normal file
@@ -0,0 +1,316 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::fs;
|
||||
use std::ops::Add;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use glob::{glob_with, MatchOptions};
|
||||
use itertools::Itertools;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
|
||||
#[repr(u16)]
|
||||
#[non_exhaustive]
|
||||
pub enum HistoryType {
|
||||
WorkingFolder = 1,
|
||||
Board,
|
||||
Scenario,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct History {
|
||||
pub working_folder: Vec<String>,
|
||||
pub board_file: Vec<String>,
|
||||
pub scenario_file: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigData {
|
||||
pub history: History,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Configurator {
|
||||
pub config_write_enable: bool,
|
||||
pub config_path: PathBuf,
|
||||
|
||||
pub config_data: ConfigData,
|
||||
pub working_folder: String,
|
||||
|
||||
}
|
||||
|
||||
|
||||
pub fn write_file(path: PathBuf, content: String) -> Result<(), String> {
|
||||
fs::write(path, content).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
impl ConfigData {
|
||||
fn new() -> ConfigData {
|
||||
let history = History { working_folder: vec![], board_file: vec![], scenario_file: vec![] };
|
||||
ConfigData { history }
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> String {
|
||||
serde_json::to_string(&self).unwrap_or_else(|_| {
|
||||
let default = ConfigData::new();
|
||||
ConfigData::serialize(&default)
|
||||
})
|
||||
}
|
||||
|
||||
/// deserialize data
|
||||
fn deserialize(config_json: String) -> Result<ConfigData, String> {
|
||||
match serde_json::from_str(&config_json.to_string()) {
|
||||
Ok(config_data) => Ok(config_data),
|
||||
Err(e) => Err(e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Configurator {
|
||||
pub fn new() -> Self {
|
||||
match Self::ensure_config_file() {
|
||||
Ok(config_file_path) => {
|
||||
// read config.json
|
||||
Self::init(config_file_path)
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("get config file path error! error: {}", e.to_string());
|
||||
log::warn!("Use blank config and disable config write to start configurator.");
|
||||
Self {
|
||||
config_write_enable: false,
|
||||
config_path: Path::new(".").to_path_buf(),
|
||||
config_data: ConfigData::new(),
|
||||
working_folder: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_config_file() -> Result<PathBuf, String> {
|
||||
// get config_dir or home_dir path
|
||||
// Todo: 讨论fallback逻辑是否可行
|
||||
let config_base = match dirs::config_dir() {
|
||||
None => {
|
||||
log::info!("get config_dir error! fallback to get home_dir.");
|
||||
match dirs::home_dir() {
|
||||
None => {
|
||||
return Err("get config_dir and home_dir error!".to_string());
|
||||
}
|
||||
Some(path) => path
|
||||
}
|
||||
}
|
||||
Some(path) => path
|
||||
};
|
||||
|
||||
|
||||
// get acrn-configurator dir path and check it exist
|
||||
let config_dir = config_base.join(".acrn-configurator");
|
||||
log::info!("current config_dir is {}.", config_dir.to_str().unwrap());
|
||||
if !config_dir.is_dir() {
|
||||
match fs::create_dir(&config_dir) {
|
||||
Err(e) => {
|
||||
//Todo: 明确无法创建 .acrn-configurator 文件夹时的处理逻辑
|
||||
log::warn!("Create configurator config dir failed, {}", e.to_string());
|
||||
return Err(e.to_string());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get config.json file path and check it exist
|
||||
let default_config_path = config_dir.join("config.json");
|
||||
if !default_config_path.is_file() {
|
||||
let empty_config = ConfigData::new();
|
||||
match fs::write(&default_config_path, empty_config.serialize()) {
|
||||
Ok(_) => {}
|
||||
Err(e) => return Err(e.to_string())
|
||||
};
|
||||
}
|
||||
Ok(default_config_path)
|
||||
}
|
||||
|
||||
pub fn init(config_file_path: PathBuf) -> Configurator {
|
||||
let config_json = match fs::read_to_string(&config_file_path) {
|
||||
Ok(data) => { data }
|
||||
Err(e) => {
|
||||
log::warn!("read config error! error: {}", e.to_string());
|
||||
log::warn!("Use default blank config to start due to read config failed.");
|
||||
return Configurator {
|
||||
config_write_enable: false,
|
||||
config_path: config_file_path,
|
||||
config_data: ConfigData::new(),
|
||||
working_folder: "".to_string(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let config_data = match ConfigData::deserialize(config_json) {
|
||||
Ok(config_data) => {
|
||||
log::info!("success load config: {}", config_data.serialize());
|
||||
config_data
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Deserialize json data error! error: {}", e);
|
||||
log::warn!("Use default blank config to start due to deserialize config failed.");
|
||||
ConfigData::new()
|
||||
}
|
||||
};
|
||||
log::info!("Using config: {}", config_data.serialize());
|
||||
Configurator {
|
||||
config_write_enable: true,
|
||||
config_path: config_file_path,
|
||||
config_data,
|
||||
working_folder: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn save_config(&self) {
|
||||
if !self.config_write_enable {
|
||||
return;
|
||||
}
|
||||
match fs::write(&self.config_path, self.config_data.serialize()) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("Write config error! error:{}",e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_history(&mut self, history_type: HistoryType, path: &Path) {
|
||||
let path_string: String = path.to_string_lossy().parse().unwrap();
|
||||
match history_type {
|
||||
HistoryType::WorkingFolder => {
|
||||
self.config_data.history.working_folder.insert(0, path_string);
|
||||
self.config_data.history.working_folder = self.config_data.history.working_folder.clone().into_iter().unique().collect()
|
||||
}
|
||||
HistoryType::Board => {
|
||||
self.config_data.history.board_file.insert(0, path_string);
|
||||
self.config_data.history.board_file = self.config_data.history.board_file.clone().into_iter().unique().collect()
|
||||
}
|
||||
HistoryType::Scenario => {
|
||||
self.config_data.history.scenario_file.insert(0, path_string);
|
||||
self.config_data.history.scenario_file = self.config_data.history.scenario_file.clone().into_iter().unique().collect()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_history(&self, history_type: HistoryType) -> &[String] {
|
||||
match history_type {
|
||||
HistoryType::WorkingFolder => {
|
||||
self.config_data.history.working_folder.borrow()
|
||||
}
|
||||
HistoryType::Board => {
|
||||
self.config_data.history.board_file.borrow()
|
||||
}
|
||||
HistoryType::Scenario => {
|
||||
self.config_data.history.scenario_file.borrow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn force_reset(&mut self) {
|
||||
self.config_data = ConfigData::new();
|
||||
self.save_config()
|
||||
}
|
||||
|
||||
pub fn set_working_folder(&mut self, working_folder: String) {
|
||||
self.working_folder = working_folder
|
||||
}
|
||||
|
||||
pub fn write_board(&self, board_name: String, board_xml_string: String) -> Result<(), String> {
|
||||
let options = MatchOptions {
|
||||
case_sensitive: false,
|
||||
..Default::default()
|
||||
};
|
||||
let pattern = self.working_folder.clone().add("/.*\\.board\\.xml");
|
||||
let files = match glob_with(&pattern, options).map_err(|e| e.to_string()) {
|
||||
Ok(files) => { files }
|
||||
Err(e) => return Err(e.to_string())
|
||||
};
|
||||
for entry in files {
|
||||
match entry {
|
||||
Ok(filepath) => {
|
||||
match fs::remove_file(&filepath) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
let err_msg = format!(
|
||||
"Can not delete file:{} error: {}",
|
||||
filepath.to_str().unwrap_or_else(|| "").to_string(), e.to_string()
|
||||
);
|
||||
log::warn!("{}",err_msg);
|
||||
return Err(err_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("find old board error! error:{}", e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let board_basename = board_name.add(".board.xml");
|
||||
let board_xml_path = Path::new(&self.working_folder).join(board_basename);
|
||||
write_file(board_xml_path, board_xml_string)
|
||||
}
|
||||
}
|
||||
|
||||
static mut WORKING_FOLDER: String = String::new();
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_history(history_type: HistoryType) -> Result<String, ()> {
|
||||
let configurator = Configurator::new();
|
||||
let history = serde_json::to_string(configurator.get_history(history_type)).unwrap_or_else(|_| String::from("[]"));
|
||||
Ok(history)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn add_history(history_type: HistoryType, path: String) -> Result<(), &'static str> {
|
||||
let path_buf = Path::new(&path);
|
||||
if !(path_buf.is_dir() || path_buf.is_file()) {
|
||||
return Err("Not a validate dir or file path.");
|
||||
}
|
||||
let mut configurator = Configurator::new();
|
||||
configurator.add_history(history_type, path_buf);
|
||||
configurator.save_config();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn set_working_folder(working_folder: String) -> Result<(), ()> {
|
||||
unsafe {
|
||||
WORKING_FOLDER = working_folder;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub fn write_board(board_name: String, contents: String) -> Result<(), String> {
|
||||
let mut configurator = Configurator::new();
|
||||
unsafe {
|
||||
configurator.set_working_folder(WORKING_FOLDER.clone());
|
||||
}
|
||||
configurator.write_board(board_name, contents)
|
||||
}
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub fn force_reset() -> Result<(), ()> {
|
||||
let mut configurator = Configurator::new();
|
||||
configurator.force_reset();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_home() -> Result<String, ()> {
|
||||
match dirs::home_dir() {
|
||||
None => {
|
||||
Ok(String::new())
|
||||
}
|
||||
Some(path) => {
|
||||
Ok(path.to_str().unwrap().to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
102
misc/config_tools/configurator/src-tauri/src/filesystem.rs
Normal file
102
misc/config_tools/configurator/src-tauri/src/filesystem.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use serde::Serialize;
|
||||
use std::fs::{self, File};
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_read_text_file(path: &str) -> Result<String, String> {
|
||||
let mut file = File::open(path).map_err(|e| e.to_string())?;
|
||||
let mut contents = String::new();
|
||||
file
|
||||
.read_to_string(&mut contents)
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_write_text_file(path: &str, contents: &str) -> Result<(), String> {
|
||||
let mut file = File::create(path).map_err(|e| e.to_string())?;
|
||||
file
|
||||
.write_all(contents.as_bytes())
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_read_binary_file(path: &str) -> Result<Vec<u8>, String> {
|
||||
let mut file = File::open(path).map_err(|e| e.to_string())?;
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents).map_err(|e| e.to_string())?;
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_write_binary_file(path: &str, contents: &[u8]) -> Result<(), String> {
|
||||
let mut file = File::create(path).map_err(|e| e.to_string())?;
|
||||
file.write_all(contents).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DirEntry {
|
||||
path: String,
|
||||
children: Option<Vec<DirEntry>>,
|
||||
}
|
||||
|
||||
fn read_dir<P: AsRef<Path>>(
|
||||
path: P,
|
||||
recursive: bool,
|
||||
) -> io::Result<Vec<DirEntry>> {
|
||||
let path = path.as_ref();
|
||||
let mut entries = Vec::new();
|
||||
for entry in fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path().to_str().unwrap().to_string();
|
||||
let children = if recursive && entry.file_type()?.is_dir() {
|
||||
Some(read_dir(&path, true)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
entries.push(DirEntry { path, children });
|
||||
}
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_read_dir(
|
||||
path: &str,
|
||||
recursive: bool,
|
||||
) -> Result<Vec<DirEntry>, String> {
|
||||
read_dir(path, recursive).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_rename(from: &str, to: &str) -> Result<(), String> {
|
||||
fs::rename(from, to).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_delete_file(path: &str) -> Result<(), String> {
|
||||
fs::remove_file(path).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_delete_dir(path: &str) -> Result<(), String> {
|
||||
fs::remove_dir_all(path).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_create_dir(path: &str) -> Result<(), String> {
|
||||
fs::create_dir(path).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_is_file(path: &str) -> bool {
|
||||
fs::metadata(path).map(|m| m.is_file()).unwrap_or(false)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fs_is_dir(path: &str) -> bool {
|
||||
fs::metadata(path).map(|m| m.is_dir()).unwrap_or(false)
|
||||
}
|
||||
80
misc/config_tools/configurator/src-tauri/src/main.rs
Normal file
80
misc/config_tools/configurator/src-tauri/src/main.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
mod filesystem;
|
||||
mod configurator;
|
||||
|
||||
use log::*;
|
||||
|
||||
use tauri::{api::cli::get_matches, utils::config::CliConfig, PackageInfo, RunEvent};
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
// Init context
|
||||
let context = tauri::generate_context!();
|
||||
|
||||
// Build app instance and run
|
||||
let app = tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
filesystem::fs_rename,
|
||||
filesystem::fs_read_dir,
|
||||
filesystem::fs_read_text_file,
|
||||
filesystem::fs_read_binary_file,
|
||||
filesystem::fs_write_text_file,
|
||||
filesystem::fs_write_binary_file,
|
||||
filesystem::fs_read_dir,
|
||||
filesystem::fs_delete_file,
|
||||
filesystem::fs_delete_dir,
|
||||
filesystem::fs_create_dir,
|
||||
filesystem::fs_is_file,
|
||||
filesystem::fs_is_dir,
|
||||
configurator::get_history,
|
||||
configurator::add_history,
|
||||
configurator::set_working_folder,
|
||||
configurator::write_board,
|
||||
configurator::force_reset,
|
||||
configurator::get_home
|
||||
])
|
||||
.setup(|app| {
|
||||
// Handle cli cmdline
|
||||
let app_config = app.config();
|
||||
let cli_config = app_config.tauri.cli.as_ref().unwrap();
|
||||
let package_info = app.package_info();
|
||||
handle_cli_arg_data(&cli_config, &package_info);
|
||||
Ok(())
|
||||
})
|
||||
.build(context)
|
||||
.expect("error while running tauri application");
|
||||
|
||||
app.run(|_app, event| {
|
||||
if let RunEvent::Exit = event {
|
||||
info!("Received Exit event");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
fn handle_cli_arg_data(cli_config: &CliConfig, pkg_info: &PackageInfo) {
|
||||
match get_matches(cli_config, pkg_info) {
|
||||
Ok(matches) => {
|
||||
let mut exit_flag = false;
|
||||
if let Some(arg_data) = matches.args.get("help") {
|
||||
println!("{}", arg_data.value.as_str().unwrap_or("No help available"));
|
||||
exit_flag = true;
|
||||
}
|
||||
if let Some(arg_data) = matches.args.get("version") {
|
||||
println!("{}", arg_data.value.as_str().unwrap_or("No version data available"));
|
||||
exit_flag = true
|
||||
}
|
||||
if exit_flag {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{}", e.to_string());
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user