SymacSys sym import working
This commit is contained in:
877
Cargo.lock
generated
877
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chrono = "0.4.44"
|
||||||
notify = "8.2.0"
|
notify = "8.2.0"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
toml = "1.0.6"
|
toml = "1.0.6"
|
||||||
|
zip = "8.2.0"
|
||||||
|
|||||||
206
src/main.rs
206
src/main.rs
@@ -1,16 +1,20 @@
|
|||||||
use std::{
|
use std::{
|
||||||
env, fs,
|
env,
|
||||||
|
fs::{self, File},
|
||||||
|
ops::Not,
|
||||||
path::{self, Path, PathBuf},
|
path::{self, Path, PathBuf},
|
||||||
sync::mpsc,
|
sync::mpsc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use chrono::Local;
|
||||||
use notify::{Event, EventKind, RecursiveMode, Watcher, event::CreateKind};
|
use notify::{Event, EventKind, RecursiveMode, Watcher, event::CreateKind};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use zip::ZipArchive;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
struct Config {
|
struct Config {
|
||||||
libraries: Option<LibConfig>,
|
libraries: LibConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -25,7 +29,11 @@ impl Config {
|
|||||||
} else {
|
} else {
|
||||||
fs::create_dir_all(config_path.parent().unwrap())
|
fs::create_dir_all(config_path.parent().unwrap())
|
||||||
.map_err(|_| ConfigError::ConfigErr)?;
|
.map_err(|_| ConfigError::ConfigErr)?;
|
||||||
let config = Config { libraries: None };
|
let config = Config {
|
||||||
|
libraries: LibConfig {
|
||||||
|
path: PathBuf::from("~/KiCad/libraries/"),
|
||||||
|
},
|
||||||
|
};
|
||||||
let contents = toml::to_string_pretty(&config).map_err(|_| ConfigError::ConfigErr)?;
|
let contents = toml::to_string_pretty(&config).map_err(|_| ConfigError::ConfigErr)?;
|
||||||
fs::write(&config_path, contents).map_err(|_| ConfigError::ConfigErr)?;
|
fs::write(&config_path, contents).map_err(|_| ConfigError::ConfigErr)?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
@@ -35,8 +43,7 @@ impl Config {
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
struct LibConfig {
|
struct LibConfig {
|
||||||
symacsys_symbols: Option<PathBuf>,
|
path: PathBuf,
|
||||||
symacsys_footprints: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SourceType {
|
enum SourceType {
|
||||||
@@ -58,13 +65,96 @@ enum SourceError {
|
|||||||
FilenameError,
|
FilenameError,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> notify::Result<()> {
|
#[derive(Error, Debug)]
|
||||||
let config = Config::load_or_create().unwrap();
|
enum ImportError {
|
||||||
if config.libraries.is_some() {
|
#[error("Unknown Path")]
|
||||||
if config.libraries.clone().unwrap().symacsys_symbols.is_some() {
|
InvalidPath,
|
||||||
println!("{:?}", config.libraries.unwrap().symacsys_symbols.unwrap());
|
#[error("Corrputed Zip")]
|
||||||
|
InvalidZip,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SymbolLibrary {
|
||||||
|
version: String,
|
||||||
|
symbols: Vec<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Symbol {
|
||||||
|
name: String,
|
||||||
|
raw: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolLibrary {
|
||||||
|
fn new() -> Self {
|
||||||
|
SymbolLibrary {
|
||||||
|
version: "20211014".to_string(),
|
||||||
|
symbols: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn write(&self) -> String {
|
||||||
|
let mut out = format!(
|
||||||
|
"(kicad_symbol_lib\n\t(version {})\n\t(generator \"uniloader\")\n",
|
||||||
|
self.version,
|
||||||
|
);
|
||||||
|
|
||||||
|
for sym in &self.symbols {
|
||||||
|
out.push_str(&sym.raw);
|
||||||
|
out.push_str("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push_str(")");
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_str(input: &str) -> Self {
|
||||||
|
let input = input.replace("\r\n", "\n");
|
||||||
|
let mut version = String::new();
|
||||||
|
let mut symbols = Vec::new();
|
||||||
|
|
||||||
|
if let Some(v_start) = input.find("(version ") {
|
||||||
|
let rest = &input[v_start + 9..];
|
||||||
|
if let Some(v_end) = rest.find(')') {
|
||||||
|
version = rest[..v_end].trim().to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
let chars: Vec<char> = input.chars().collect();
|
||||||
|
|
||||||
|
while i < chars.len() {
|
||||||
|
if input[i..].starts_with("(symbol ") {
|
||||||
|
let start = i;
|
||||||
|
let mut depth = 0;
|
||||||
|
|
||||||
|
while i < chars.len() {
|
||||||
|
if chars[i] == '(' {
|
||||||
|
depth += 1;
|
||||||
|
} else if chars[i] == ')' {
|
||||||
|
depth -= 1;
|
||||||
|
|
||||||
|
if depth == 0 {
|
||||||
|
let end = i + 1;
|
||||||
|
let raw = input[start..end].to_string();
|
||||||
|
|
||||||
|
let name = raw.split('"').nth(1).unwrap_or("unknown").to_string();
|
||||||
|
|
||||||
|
symbols.push(Symbol { name, raw });
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { version, symbols }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> notify::Result<()> {
|
||||||
|
let config = Config::load_or_create().unwrap();
|
||||||
|
println!("{:?}", config.libraries.path);
|
||||||
let (tx, rx) = mpsc::channel::<notify::Result<Event>>();
|
let (tx, rx) = mpsc::channel::<notify::Result<Event>>();
|
||||||
let mut watcher = notify::recommended_watcher(tx)?;
|
let mut watcher = notify::recommended_watcher(tx)?;
|
||||||
let downloads = PathBuf::from(env::var("HOME").unwrap()).join("Downloads");
|
let downloads = PathBuf::from(env::var("HOME").unwrap()).join("Downloads");
|
||||||
@@ -85,7 +175,12 @@ fn main() -> notify::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match source_type {
|
match source_type {
|
||||||
SourceType::SymacSys => println!("SymacSys detected"),
|
SourceType::SymacSys => {
|
||||||
|
println!("SymacSys detected, Installing...");
|
||||||
|
let Ok(_) = import_symacsys(&event.paths[0], &config) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
}
|
||||||
SourceType::UltraLibrarian => println!("UltraLibrarian detected"),
|
SourceType::UltraLibrarian => println!("UltraLibrarian detected"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,3 +215,92 @@ fn find_source_type(path: &Path) -> Result<SourceType, SourceError> {
|
|||||||
Err(SourceError::SourceUnknown)
|
Err(SourceError::SourceUnknown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_zip(path: &Path, config: &Config) -> Result<PathBuf, ImportError> {
|
||||||
|
let file = File::open(path).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
let mut archive = ZipArchive::new(file).map_err(|_| ImportError::InvalidZip)?;
|
||||||
|
|
||||||
|
let Some(file_name) = path.file_stem() else {
|
||||||
|
return Err(ImportError::InvalidPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
let tmp_path = config.libraries.path.join("tmp").join(file_name);
|
||||||
|
|
||||||
|
fs::create_dir_all(&tmp_path).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
|
||||||
|
archive
|
||||||
|
.extract(&tmp_path)
|
||||||
|
.map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
|
||||||
|
fs::remove_file(path).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
Ok(tmp_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_symacsys(path: &Path, config: &Config) -> Result<(), ImportError> {
|
||||||
|
let tmp_path = extract_zip(path, config)?;
|
||||||
|
|
||||||
|
let dirs = fs::read_dir(&tmp_path).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
|
||||||
|
let Some(data_dir) = dirs
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.find(|entry| entry.file_type().map(|t| t.is_dir()).unwrap_or(false))
|
||||||
|
.map(|entry| entry.path())
|
||||||
|
else {
|
||||||
|
return Err(ImportError::InvalidPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
let kicad_dir = data_dir.join("KiCad");
|
||||||
|
|
||||||
|
let results = fs::read_dir(&kicad_dir).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
|
||||||
|
let Some(sym) = results
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.find(|entry| {
|
||||||
|
entry
|
||||||
|
.path()
|
||||||
|
.extension()
|
||||||
|
.map(|ext| ext == "kicad_sym")
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
.map(|entry| entry.path())
|
||||||
|
else {
|
||||||
|
return Err(ImportError::InvalidPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
let sym_str = fs::read_to_string(&sym).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
|
||||||
|
let new_lib = SymbolLibrary::from_str(&sym_str);
|
||||||
|
|
||||||
|
let symacsys_path = config.libraries.path.join("SymacSys");
|
||||||
|
let symacsys_sym = symacsys_path.join("SymacSys_Components.kicad_sym");
|
||||||
|
|
||||||
|
fs::create_dir_all(symacsys_path).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
|
||||||
|
let mut current_lib: SymbolLibrary;
|
||||||
|
|
||||||
|
if symacsys_sym.exists() {
|
||||||
|
let symacsys_sym_str =
|
||||||
|
fs::read_to_string(&symacsys_sym).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
current_lib = SymbolLibrary::from_str(&symacsys_sym_str);
|
||||||
|
} else {
|
||||||
|
current_lib = SymbolLibrary::new();
|
||||||
|
let symacsys_sym_str = current_lib.write();
|
||||||
|
fs::write(&symacsys_sym, symacsys_sym_str).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for symbol in new_lib.symbols {
|
||||||
|
current_lib.symbols.push(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_lib = current_lib.write();
|
||||||
|
fs::write(&symacsys_sym, out_lib).map_err(|_| ImportError::InvalidPath)?;
|
||||||
|
|
||||||
|
println!("{:?}", tmp_path);
|
||||||
|
|
||||||
|
fs::remove_dir_all(tmp_path).map_err(|_| {
|
||||||
|
println!("Error rmdir");
|
||||||
|
ImportError::InvalidPath
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user