7z: распаковка в буфер

This commit is contained in:
Anton Titovets
2026-06-19 10:06:22 +03:00
parent 06d36c15f2
commit dbd87492a5
8 changed files with 215 additions and 13 deletions
+6 -6
View File
@@ -121,14 +121,14 @@ dependencies = [
[[package]]
name = "common-backend"
version = "2.2.0"
version = "2.3.0"
dependencies = [
"tokio",
]
[[package]]
name = "common-core"
version = "2.2.0"
version = "2.3.0"
dependencies = [
"addin1c",
"common-janx",
@@ -136,14 +136,14 @@ dependencies = [
[[package]]
name = "common-janx"
version = "2.2.0"
version = "2.3.0"
dependencies = [
"serde_json",
]
[[package]]
name = "common-logs"
version = "2.2.0"
version = "2.3.0"
dependencies = [
"chrono",
"common-janx",
@@ -152,7 +152,7 @@ dependencies = [
[[package]]
name = "common-utils"
version = "2.2.0"
version = "2.3.0"
dependencies = [
"common-janx",
]
@@ -350,7 +350,7 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "opi_sevenz"
version = "2.2.0"
version = "2.3.0"
dependencies = [
"common-backend",
"common-core",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "opi_sevenz"
version = "2.2.0"
version = "2.3.0"
license = "MIT"
edition = "2021"
+9
View File
@@ -66,6 +66,15 @@ impl AddIn {
.pack_from_description_to_file(description, archive_path)
}
pub fn unpack_to_description(
&mut self,
archive_data: &[u8],
password: &str,
) -> Result<JanxValue, String> {
self.lock_backend()
.unpack_to_description(archive_data, password)
}
pub fn set_logger(&mut self, logger_config: &JanxValue) -> JanxValue {
match Logger::from_janx(logger_config) {
Ok(logger) => match self.lock_backend().set_logger(Arc::new(logger)) {
+103 -1
View File
@@ -1,6 +1,7 @@
use std::collections::BTreeMap;
use std::path::Path;
use common_core::{FromJanx, JanxValue};
use common_core::{FromJanx, JanxValue, janx};
#[derive(Debug, Clone)]
pub struct ArchiveDescription {
@@ -43,6 +44,29 @@ impl ArchiveDescription {
Ok(Self { password, entries })
}
pub fn from_flat_entries(
password: String,
entries: &[(String, bool, Option<Vec<u8>>)],
) -> Self {
let mut tree = TreeDir::default();
for (path, is_directory, data) in entries {
tree.insert(path, *is_directory, data.clone());
}
Self {
password,
entries: tree.into_nodes(),
}
}
pub fn to_janx(&self) -> JanxValue {
janx!({
"password": self.password.clone(),
"entries": nodes_to_janx(&self.entries),
})
}
}
fn parse_entries(value: &JanxValue) -> Result<Vec<ArchiveNode>, String> {
@@ -119,3 +143,81 @@ pub fn join_archive_path(prefix: &str, name: &str) -> String {
format!("{}/{}", prefix.trim_end_matches('/'), name)
}
}
fn nodes_to_janx(nodes: &[ArchiveNode]) -> JanxValue {
JanxValue::Array(nodes.iter().map(node_to_janx).collect())
}
fn node_to_janx(node: &ArchiveNode) -> JanxValue {
match node {
ArchiveNode::Directory { name, entries } => janx!({
"name": name.clone(),
"directory": true,
"entries": nodes_to_janx(entries),
}),
ArchiveNode::FileFromPath { name, .. } => janx!({
"name": name.clone(),
"directory": false,
"from_path": true,
"path": String::new(),
}),
ArchiveNode::FileFromData { name, data } => janx!({
"name": name.clone(),
"directory": false,
"from_path": false,
"data": JanxValue::binary(data.clone()),
}),
}
}
#[derive(Default)]
struct TreeDir {
subdirs: BTreeMap<String, TreeDir>,
files: BTreeMap<String, Vec<u8>>,
}
impl TreeDir {
fn insert(&mut self, path: &str, is_directory: bool, data: Option<Vec<u8>>) {
let path = path.trim_matches('/').replace('\\', "/");
if path.is_empty() {
return;
}
let parts: Vec<&str> = path.split('/').collect();
self.insert_parts(&parts, is_directory, data);
}
fn insert_parts(&mut self, parts: &[&str], is_directory: bool, data: Option<Vec<u8>>) {
if parts.len() == 1 {
if is_directory {
self.subdirs.entry(parts[0].to_string()).or_default();
} else {
self.files
.insert(parts[0].to_string(), data.unwrap_or_default());
}
return;
}
self.subdirs
.entry(parts[0].to_string())
.or_default()
.insert_parts(&parts[1..], is_directory, data);
}
fn into_nodes(self) -> Vec<ArchiveNode> {
let mut nodes = Vec::new();
for (name, subdir) in self.subdirs {
nodes.push(ArchiveNode::Directory {
name,
entries: subdir.into_nodes(),
});
}
for (name, data) in self.files {
nodes.push(ArchiveNode::FileFromData { name, data });
}
nodes
}
}
+35 -1
View File
@@ -2,9 +2,10 @@ use std::fs::{self, File};
use std::io::{Cursor, Seek, Write};
use std::path::Path;
use common_core::JanxValue;
use sevenz_rust2::{
compress, compress_encrypted, decompress, decompress_with_password, encoder_options::AesEncoderOptions,
ArchiveEntry, ArchiveWriter, EncoderMethod, Password,
ArchiveEntry, ArchiveReader, ArchiveWriter, EncoderMethod, Password,
};
use crate::archive_description::{join_archive_path, ArchiveDescription, ArchiveNode};
@@ -82,6 +83,39 @@ pub fn pack_description_to_file(
.map_err(|error| format!("Failed to write archive file: {}", error))
}
pub fn unpack_buffer_to_description(
archive_data: &[u8],
password: &str,
) -> Result<JanxValue, String> {
if archive_data.is_empty() {
return Err("Archive data is empty".to_string());
}
let mut reader = ArchiveReader::new(Cursor::new(archive_data.to_vec()), password.into())
.map_err(|error| error.to_string())?;
let file_list: Vec<(String, bool)> = reader
.archive()
.files
.iter()
.map(|entry| (entry.name().to_string(), entry.is_directory()))
.collect();
let mut collected = Vec::with_capacity(file_list.len());
for (name, is_directory) in file_list {
if is_directory {
collected.push((name, true, None));
} else {
let data = reader
.read_file(&name)
.map_err(|error| error.to_string())?;
collected.push((name, false, Some(data)));
}
}
Ok(ArchiveDescription::from_flat_entries(password.to_string(), &collected).to_janx())
}
fn push_nodes<W: Write + Seek>(
nodes: &[ArchiveNode],
prefix: &str,
+21
View File
@@ -102,6 +102,20 @@ impl SevenZBackend {
})
}
pub fn unpack_to_description(
&mut self,
archive_data: &[u8],
password: &str,
) -> Result<JanxValue, String> {
let archive_data = archive_data.to_vec();
let password = password.to_string();
self.call_janx(|response| WorkerCommand::UnpackToDescription {
archive_data,
password,
response,
})
}
pub fn set_logger(&mut self, logger: Arc<Logger>) -> Result<(), String> {
if self.logger.is_some() {
return Ok(());
@@ -142,6 +156,13 @@ impl SevenZBackend {
self.call_thread(build).and_then(|result| result)
}
fn call_janx<F>(&mut self, build: F) -> Result<JanxValue, String>
where
F: FnOnce(Sender<Result<JanxValue, String>>) -> WorkerCommand,
{
self.call_thread(build).and_then(|result| result)
}
fn call_result<F>(&mut self, build: F) -> Result<(), String>
where
F: FnOnce(Sender<Result<(), String>>) -> WorkerCommand,
+18 -4
View File
@@ -18,6 +18,7 @@ pub const METHODS: &[&[u16]] = &[
name!("UnpackFromBuffer"),
name!("PackFromDescription"),
name!("PackFromDescriptionToFile"),
name!("UnpackToDescription"),
name!("SetLogger"),
name!("GetLogs"),
name!("Version"),
@@ -33,9 +34,10 @@ pub fn get_params_amount(num: usize) -> usize {
3 => 3,
4 => 1,
5 => 2,
6 => 1,
6 => 2,
7 => 1,
8 => 0,
8 => 1,
9 => 0,
_ => 0,
}
}
@@ -47,6 +49,13 @@ fn box_blob_result(result: Result<Vec<u8>, String>) -> Box<dyn getset::ValueType
}
}
fn box_janx_result(result: Result<JanxValue, String>) -> Box<dyn getset::ValueType> {
match result {
Ok(value) => Box::new(value),
Err(error) => Box::new(error),
}
}
pub fn cal_func(obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box<dyn getset::ValueType> {
match num {
0 => {
@@ -82,14 +91,19 @@ pub fn cal_func(obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box<dyn
Box::new(obj.pack_from_description_to_file(&description, &archive_path))
}
6 => {
let archive_data = params[0].get_blob().unwrap_or_default().to_vec();
let password = params[1].get_string().unwrap_or_default();
box_janx_result(obj.unpack_to_description(&archive_data, &password))
}
7 => {
let logger_config = JanxValue::from_variant(&params[0]);
Box::new(obj.set_logger(&logger_config))
}
7 => {
8 => {
let count = params[0].get_i32().unwrap_or(0) as usize;
Box::new(obj.get_logs(count))
}
8 => Box::new(version()),
9 => Box::new(version()),
_ => Box::new(false),
}
}
+22
View File
@@ -46,6 +46,11 @@ pub enum WorkerCommand {
archive_path: String,
response: Sender<JanxValue>,
},
UnpackToDescription {
archive_data: Vec<u8>,
password: String,
response: Sender<Result<JanxValue, String>>,
},
SetLogger {
logger: Arc<Logger>,
response: Sender<Result<(), String>>,
@@ -144,6 +149,15 @@ impl Session {
Err(error) => janx_error(error),
}
}
fn unpack_to_description(
&self,
archive_data: &[u8],
password: &str,
) -> Result<JanxValue, String> {
self.log("UnpackToDescription");
archive_ops::unpack_buffer_to_description(archive_data, password)
}
}
pub fn spawn_thread(logger: Option<Arc<Logger>>) -> Result<SyncBackendThread<WorkerCommand>, String> {
@@ -200,6 +214,14 @@ pub fn spawn_thread(logger: Option<Arc<Logger>>) -> Result<SyncBackendThread<Wor
let result = session.pack_from_description_to_file(&description, &archive_path);
let _ = response.send(result);
}
WorkerCommand::UnpackToDescription {
archive_data,
password,
response,
} => {
let result = session.unpack_to_description(&archive_data, &password);
let _ = response.send(result);
}
WorkerCommand::SetLogger { logger, response } => {
session.logger = Some(logger);
session.log("Logger initialized");