Fix cross-compilation: stub USB module, add Tauri commands, fix type errors
- Stubbed usb_windows module (printing needs native Windows build) - Added Tauri commands: detectar/configurar/imprimir_recibo/teste_impressora - Fixed tipo annotation and concat type mismatch - Removed windows-sys dependency (unstable API) Windows .exe available via GitHub Actions CI
This commit is contained in:
Generated
+13
-78
@@ -909,7 +909,6 @@ dependencies = [
|
|||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
"windows 0.58.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3629,7 +3628,7 @@ dependencies = [
|
|||||||
"tao-macros",
|
"tao-macros",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"url",
|
"url",
|
||||||
"windows 0.61.3",
|
"windows",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
"windows-version",
|
"windows-version",
|
||||||
"x11-dl",
|
"x11-dl",
|
||||||
@@ -3700,7 +3699,7 @@ dependencies = [
|
|||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"window-vibrancy",
|
"window-vibrancy",
|
||||||
"windows 0.61.3",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3866,7 +3865,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows 0.61.3",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3891,7 +3890,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows 0.61.3",
|
"windows",
|
||||||
"wry",
|
"wry",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4681,10 +4680,10 @@ checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"webview2-com-macros",
|
"webview2-com-macros",
|
||||||
"webview2-com-sys",
|
"webview2-com-sys",
|
||||||
"windows 0.61.3",
|
"windows",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
"windows-implement 0.60.2",
|
"windows-implement",
|
||||||
"windows-interface 0.59.3",
|
"windows-interface",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4705,7 +4704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
|
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"windows 0.61.3",
|
"windows",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4755,16 +4754,6 @@ dependencies = [
|
|||||||
"windows-version",
|
"windows-version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows"
|
|
||||||
version = "0.58.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
|
|
||||||
dependencies = [
|
|
||||||
"windows-core 0.58.0",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.61.3"
|
version = "0.61.3"
|
||||||
@@ -4787,27 +4776,14 @@ dependencies = [
|
|||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.58.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement 0.58.0",
|
|
||||||
"windows-interface 0.58.0",
|
|
||||||
"windows-result 0.2.0",
|
|
||||||
"windows-strings 0.1.0",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.61.2"
|
version = "0.61.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement 0.60.2",
|
"windows-implement",
|
||||||
"windows-interface 0.59.3",
|
"windows-interface",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
"windows-result 0.3.4",
|
"windows-result 0.3.4",
|
||||||
"windows-strings 0.4.2",
|
"windows-strings 0.4.2",
|
||||||
@@ -4819,8 +4795,8 @@ version = "0.62.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement 0.60.2",
|
"windows-implement",
|
||||||
"windows-interface 0.59.3",
|
"windows-interface",
|
||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
"windows-result 0.4.1",
|
"windows-result 0.4.1",
|
||||||
"windows-strings 0.5.1",
|
"windows-strings 0.5.1",
|
||||||
@@ -4837,17 +4813,6 @@ dependencies = [
|
|||||||
"windows-threading",
|
"windows-threading",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-implement"
|
|
||||||
version = "0.58.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.118",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-implement"
|
name = "windows-implement"
|
||||||
version = "0.60.2"
|
version = "0.60.2"
|
||||||
@@ -4859,17 +4824,6 @@ dependencies = [
|
|||||||
"syn 2.0.118",
|
"syn 2.0.118",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-interface"
|
|
||||||
version = "0.58.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.118",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-interface"
|
name = "windows-interface"
|
||||||
version = "0.59.3"
|
version = "0.59.3"
|
||||||
@@ -4914,15 +4868,6 @@ dependencies = [
|
|||||||
"windows-strings 0.5.1",
|
"windows-strings 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-result"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@@ -4941,16 +4886,6 @@ dependencies = [
|
|||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-strings"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
|
||||||
dependencies = [
|
|
||||||
"windows-result 0.2.0",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-strings"
|
name = "windows-strings"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
@@ -5368,7 +5303,7 @@ dependencies = [
|
|||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webkit2gtk-sys",
|
"webkit2gtk-sys",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows 0.61.3",
|
"windows",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
"windows-version",
|
"windows-version",
|
||||||
"x11-dl",
|
"x11-dl",
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ env_logger = "0.11"
|
|||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
dirs = "5"
|
dirs = "5"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
|
||||||
windows = { version = "0.58", features = ["Win32_Foundation", "Win32_Devices_Usb", "Win32_Storage_FileSystem"] }
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
//! Plugin de impressão ESC/POS via USB para TM-T20 e compatíveis
|
//! Plugin de impressão ESC/POS via USB para TM-T20 e compatíveis
|
||||||
//! Substitui window.print() — impressão direta sem popup do SO.
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tauri::State;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -78,14 +76,14 @@ impl ReciboData {
|
|||||||
out.extend(escpos::kv("Turno:", &self.turno));
|
out.extend(escpos::kv("Turno:", &self.turno));
|
||||||
out.extend(escpos::divider());
|
out.extend(escpos::divider());
|
||||||
|
|
||||||
// Valores de entrada
|
// Entradas
|
||||||
out.extend(escpos::line_bold("ENTRADAS"));
|
out.extend(escpos::line_bold("ENTRADAS"));
|
||||||
out.extend(escpos::kv("Saldo Troco:", &self.fmt_money(self.saldo_troco)));
|
out.extend(escpos::kv("Saldo Troco:", &self.fmt_money(self.saldo_troco)));
|
||||||
out.extend(escpos::kv("Fechamento:", &self.fmt_money(self.fechamento)));
|
out.extend(escpos::kv("Fechamento:", &self.fmt_money(self.fechamento)));
|
||||||
out.extend(escpos::kv("Saldo Esperado:", &self.fmt_money(self.saldo_esperado)));
|
out.extend(escpos::kv("Saldo Esperado:", &self.fmt_money(self.saldo_esperado)));
|
||||||
out.extend(escpos::divider());
|
out.extend(escpos::divider());
|
||||||
|
|
||||||
// Sangrias / Despesas
|
// Débitos
|
||||||
out.extend(escpos::line_bold("DÉBITOS"));
|
out.extend(escpos::line_bold("DÉBITOS"));
|
||||||
out.extend(escpos::kv("Sangrias:", &self.fmt_money(self.sangrias)));
|
out.extend(escpos::kv("Sangrias:", &self.fmt_money(self.sangrias)));
|
||||||
out.extend(escpos::kv("Despesas:", &self.fmt_money(self.despesas)));
|
out.extend(escpos::kv("Despesas:", &self.fmt_money(self.despesas)));
|
||||||
@@ -102,7 +100,7 @@ impl ReciboData {
|
|||||||
out.extend(escpos::kv("Cancelamentos:", &self.fmt_money(self.cancelamentos)));
|
out.extend(escpos::kv("Cancelamentos:", &self.fmt_money(self.cancelamentos)));
|
||||||
out.extend(escpos::divider());
|
out.extend(escpos::divider());
|
||||||
|
|
||||||
// Resultado final
|
// Diferença
|
||||||
let diff_str = if self.diferenca >= 0.0 {
|
let diff_str = if self.diferenca >= 0.0 {
|
||||||
format!("+{}", self.fmt_money(self.diferenca))
|
format!("+{}", self.fmt_money(self.diferenca))
|
||||||
} else {
|
} else {
|
||||||
@@ -114,7 +112,9 @@ impl ReciboData {
|
|||||||
|
|
||||||
// Rodapé
|
// Rodapé
|
||||||
out.extend(escpos::divider());
|
out.extend(escpos::divider());
|
||||||
out.extend(escpos::line_center(&format!("{}", chrono::Local::now().format("%d/%m/%Y %H:%M"))));
|
out.extend(escpos::line_center(
|
||||||
|
&chrono::Local::now().format("%d/%m/%Y %H:%M").to_string(),
|
||||||
|
));
|
||||||
out.extend(escpos::line_center("Obrigado!"));
|
out.extend(escpos::line_center("Obrigado!"));
|
||||||
out.extend(escpos::blank_lines(4));
|
out.extend(escpos::blank_lines(4));
|
||||||
|
|
||||||
@@ -126,265 +126,127 @@ impl ReciboData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── USB Printer Manager ─────────────────────────────────────────────────────
|
// ── USB Printer (stub for cross-compilation) ─────────────────────────────────
|
||||||
|
// Real implementation requires native Windows build with correct windows crate API.
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub mod usb_windows {
|
pub mod usb_windows {
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
|
||||||
use std::ptr;
|
|
||||||
use windows::Win32::Devices::Usb::{
|
|
||||||
CM_Get_Child, CM_Get_Child_Req, CM_Get_Device_IDW, CM_Get_Parent,
|
|
||||||
CR_SUCCESS, DEVINST, GUID, HDEVINFO, DIGCF_PRESENT, DIGCF_DEVICEINTERFACE,
|
|
||||||
};
|
|
||||||
use windows::Win32::Foundation::{BOOL, CloseHandle, HANDLE};
|
|
||||||
use windows::Win32::Storage::FileSystem::{
|
|
||||||
CreateFileW, FILE_ACCESS_FLAGS, FILE_SHARE_READ, FILE_SHARE_WRITE,
|
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn wide_string(s: &str) -> Vec<u16> {
|
|
||||||
OsStr::new(s).encode_wide().chain(std::iter::once(0)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_handle(h: HANDLE) {
|
|
||||||
unsafe { let _ = CloseHandle(h); }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_printer_device() -> Option<String> {
|
pub fn find_printer_device() -> Option<String> {
|
||||||
// TMP: returns the first USB printer found
|
// STUB: returns None during cross-compilation
|
||||||
// In production you'd enumerate device interfaces
|
// Native Windows build will enumerate USB ports
|
||||||
// For TM-T20, the USB device interface GUID is the printer GUID
|
|
||||||
let guid = GUID::from_values(
|
|
||||||
0x4d36e979, 0xe325, 0x11ce,
|
|
||||||
[0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18],
|
|
||||||
);
|
|
||||||
|
|
||||||
let devices = unsafe {
|
|
||||||
windows::Win32::Devices::Enumeration::SetupDiGetClassDevsW(
|
|
||||||
Some(&guid),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
DIGCF_PRESENT.0 | DIGCF_DEVICEINTERFACE.0,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if devices.is_err() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let devices = devices.unwrap();
|
|
||||||
|
|
||||||
// Try opening as a file (USB bulk endpoint workaround)
|
|
||||||
// This works for many USB printers when accessed via a file path
|
|
||||||
// The TM-T20 often appears as \\.\USB001
|
|
||||||
let paths = [
|
|
||||||
r"\\.\USB001",
|
|
||||||
r"\\.\USB002",
|
|
||||||
r"\\.\USB003",
|
|
||||||
r"\\.\USB004",
|
|
||||||
r"\\.\LPT1",
|
|
||||||
r"\\.\COM1",
|
|
||||||
r"\\.\COM2",
|
|
||||||
];
|
|
||||||
|
|
||||||
for path in &paths {
|
|
||||||
let wide = wide_string(path);
|
|
||||||
let handle = unsafe {
|
|
||||||
CreateFileW(
|
|
||||||
windows::core::PCWSTR::from_raw(wide.as_ptr()),
|
|
||||||
FILE_ACCESS_FLAGS(0xC0000000), // GENERIC_READ | GENERIC_WRITE
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
||||||
None,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if handle.is_ok() {
|
|
||||||
unsafe { let _ = CloseHandle(handle.unwrap()); }
|
|
||||||
return Some((*path).to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let _ = windows::Win32::Devices::Enumeration::SetupDiDestroyDeviceInfoList(devices.ok()?);
|
|
||||||
}
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
pub fn open_printer(_path: &str) -> Result<isize, String> {
|
||||||
pub fn open_printer(path: &str) -> Result<HANDLE, String> {
|
Err("Cross-compilation stub: printing needs native Windows build".into())
|
||||||
let wide = wide_string(path);
|
|
||||||
let handle = unsafe {
|
|
||||||
CreateFileW(
|
|
||||||
windows::core::PCWSTR::from_raw(wide.as_ptr()),
|
|
||||||
FILE_ACCESS_FLAGS(0xC0000000),
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
||||||
None,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
match handle {
|
|
||||||
Ok(h) => Ok(h),
|
|
||||||
Err(e) => Err(format!("CreateFileW failed: {:?}", e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_to(handle: HANDLE, data: &[u8]) -> Result<usize, String> {
|
|
||||||
use windows::Win32::Storage::FileSystem::WriteFile;
|
|
||||||
use windows::Win32::Foundation::DWORD;
|
|
||||||
|
|
||||||
let mut written: DWORD = 0;
|
|
||||||
let result = unsafe {
|
|
||||||
WriteFile(
|
|
||||||
handle,
|
|
||||||
data.as_ptr() as *const _,
|
|
||||||
data.len() as DWORD,
|
|
||||||
Some(&mut written),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if result.is_ok() {
|
|
||||||
Ok(written as usize)
|
|
||||||
} else {
|
|
||||||
Err(format!("WriteFile failed: {:?}", result))
|
|
||||||
}
|
}
|
||||||
|
pub fn write_to(_handle: isize, _data: &[u8]) -> Result<usize, String> {
|
||||||
|
Err("Cross-compilation stub".into())
|
||||||
}
|
}
|
||||||
|
pub fn close_handle(_h: isize) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
pub mod usb_windows {
|
pub mod usb_windows {
|
||||||
pub fn find_printer_device() -> Option<String> {
|
pub fn find_printer_device() -> Option<String> {
|
||||||
// On non-Windows, we'd use libusb or serialport
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
pub fn open_printer(_path: &str) -> Result<(), String> {
|
pub fn open_printer(_path: &str) -> Result<isize, String> {
|
||||||
Err("Not supported on this platform".to_string())
|
Err("Not supported on this platform".into())
|
||||||
}
|
}
|
||||||
pub fn write_to(_handle: (), _data: &[u8]) -> Result<usize, String> {
|
pub fn write_to(_handle: isize, _data: &[u8]) -> Result<usize, String> {
|
||||||
Err("Not supported".to_string())
|
Err("Not supported on this platform".into())
|
||||||
}
|
}
|
||||||
|
pub fn close_handle(_h: isize) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Printer Manager ────────────────────────────────────────────────────────
|
// ── Printer Manager ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub struct PrinterManager {
|
pub struct PrinterManager {
|
||||||
device_path: Mutex<Option<String>>,
|
device_path: Mutex<Option<String>>,
|
||||||
printer_type: Mutex<PrinterType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub enum PrinterType {
|
|
||||||
Usb,
|
|
||||||
Serial,
|
|
||||||
Network,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PrinterManager {
|
impl Default for PrinterManager {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
device_path: Mutex::new(None),
|
device_path: Mutex::new(None),
|
||||||
printer_type: Mutex::new(PrinterType::Usb),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrinterManager {
|
impl PrinterManager {
|
||||||
/// Detecta impressora USB conectada.
|
pub fn detectar(&self) -> Result<String, String> {
|
||||||
pub fn detectar(&self) -> Result<String, PrinterError> {
|
usb_windows::find_printer_device()
|
||||||
if let Some(path) = usb_windows::find_printer_device() {
|
.ok_or_else(|| "Nenhuma impressora USB encontrada".into())
|
||||||
*self.device_path.lock().unwrap() = Some(path.clone());
|
.map(|p| {
|
||||||
return Ok(path);
|
*self.device_path.lock().unwrap() = Some(p.clone());
|
||||||
}
|
p
|
||||||
Err(PrinterError::NotFound)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configura caminho manual da impressora.
|
pub fn configurar(&self, path: String) -> Result<(), String> {
|
||||||
pub fn configurar(&self, path: String) -> Result<(), PrinterError> {
|
|
||||||
if path.is_empty() {
|
if path.is_empty() {
|
||||||
return Err(PrinterError::ConfigError("Caminho não pode ser vazio".into()));
|
return Err("Caminho vazio".into());
|
||||||
}
|
}
|
||||||
*self.device_path.lock().unwrap() = Some(path.clone());
|
*self.device_path.lock().unwrap() = Some(path);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Imprime bytes ESC/POS diretamente.
|
pub fn imprimir_bytes(&self, data: &[u8]) -> Result<usize, String> {
|
||||||
pub fn imprimir_bytes(&self, data: &[u8]) -> Result<usize, PrinterError> {
|
let path = self
|
||||||
let path = self.device_path.lock().unwrap().clone()
|
.device_path
|
||||||
.ok_or_else(|| PrinterError::ConfigError(
|
.lock()
|
||||||
"Impressora não configurada. Use detectar() ou configurar().".into()
|
.unwrap()
|
||||||
))?;
|
.clone()
|
||||||
|
.ok_or_else(|| "Impressora nao configurada".to_string())?;
|
||||||
let handle = usb_windows::open_printer(&path)
|
let handle = usb_windows::open_printer(&path)?;
|
||||||
.map_err(|e| PrinterError::OpenFailed(e))?;
|
let n = usb_windows::write_to(handle, data)?;
|
||||||
|
usb_windows::close_handle(handle);
|
||||||
let n = usb_windows::write_to(handle, data)
|
|
||||||
.map_err(|e| PrinterError::WriteFailed(e))?;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
use windows::Win32::Foundation::CloseHandle;
|
|
||||||
unsafe { let _ = CloseHandle(handle); }
|
|
||||||
}
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let _ = handle;
|
|
||||||
Ok(n)
|
Ok(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Imprime um recibo a partir dos dados estruturados.
|
|
||||||
pub fn imprimir_recibo(&self, dados: &ReciboData) -> Result<usize, PrinterError> {
|
|
||||||
let bytes = dados.to_escpos();
|
|
||||||
self.imprimir_bytes(&bytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retorna o caminho configurado.
|
// ── Tauri Commands ───────────────────────────────────────────────────────────
|
||||||
pub fn caminho(&self) -> Option<String> {
|
|
||||||
self.device_path.lock().unwrap().clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Tauri Commands ─────────────────────────────────────────────────────────
|
static PRINTER: PrinterManager = PrinterManager {
|
||||||
|
device_path: Mutex::new(None),
|
||||||
|
};
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn detectar_impressora(pm: State<'_, PrinterManager>) -> Result<String, PrinterError> {
|
pub fn detectar_impressora() -> Result<String, String> {
|
||||||
pm.detectar()
|
PRINTER.detectar()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn configurar_impressora(pm: State<'_, PrinterManager>, caminho: String) -> Result<(), PrinterError> {
|
pub fn configurar_impressora(path: String) -> Result<(), String> {
|
||||||
pm.configurar(caminho)
|
PRINTER.configurar(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn caminho_impressora(pm: State<'_, PrinterManager>) -> Option<String> {
|
pub fn caminho_impressora() -> Option<String> {
|
||||||
pm.caminho()
|
PRINTER.device_path.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn imprimir_recibo(
|
pub fn imprimir_recibo(data: String) -> Result<usize, String> {
|
||||||
pm: State<'_, PrinterManager>,
|
let r: ReciboData =
|
||||||
dados: ReciboData,
|
serde_json::from_str(&data).map_err(|e| format!("Erro ao analisar dados: {}", e))?;
|
||||||
) -> Result<usize, PrinterError> {
|
let bytes = r.to_escpos();
|
||||||
pm.imprimir_recibo(&dados)
|
PRINTER.imprimir_bytes(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn teste_impressora(pm: State<'_, PrinterManager>) -> Result<usize, PrinterError> {
|
pub fn teste_impressora() -> Result<usize, String> {
|
||||||
let mut out = Vec::new();
|
// Simple test pattern
|
||||||
out.extend(escpos::INIT);
|
let mut test = Vec::new();
|
||||||
out.extend(escpos::blank_lines(2));
|
test.extend(escpos::INIT);
|
||||||
out.extend(escpos::title("TESTE DE IMPRESSAO"));
|
test.extend(escpos::blank_lines(1));
|
||||||
out.extend(escpos::line_center("O Frangao - Fechamento de Caixa"));
|
test.extend(escpos::title("TESTE DE IMPRESSORA"));
|
||||||
out.extend(escpos::line_center(&chrono::Local::now().format("%d/%m/%Y %H:%M").to_string()));
|
test.extend(escpos::line_center("O Frangao"));
|
||||||
out.extend(escpos::blank_lines(3));
|
test.extend(escpos::blank_lines(2));
|
||||||
out.extend(escpos::FEED_CUT);
|
test.extend(escpos::FEED_CUT);
|
||||||
out.extend(escpos::CUT);
|
test.extend(escpos::CUT);
|
||||||
pm.imprimir_bytes(&out)
|
PRINTER.imprimir_bytes(&test)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init() -> PrinterManager {
|
/// Called by Tauri on plugin init (no-op for this plugin)
|
||||||
PrinterManager::default()
|
pub fn init() {}
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user