1#![deny(unsafe_code)]
18#![doc(test(attr(deny(warnings))))]
20#![deny(
24 clippy::all,
25 clippy::default_trait_access,
26 clippy::expl_impl_clone_on_copy,
27 clippy::if_not_else,
28 clippy::needless_continue,
29 clippy::single_match_else,
30 clippy::unseparated_literal_suffix,
31 clippy::used_underscore_binding
32)]
33#![allow(clippy::match_ref_pats)]
35#![allow(
37 clippy::derived_hash_with_manual_eq,
38 clippy::len_without_is_empty,
39 clippy::redundant_field_names,
40 clippy::too_many_arguments,
41 clippy::single_component_path_imports,
42 clippy::double_must_use
43)]
44#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
46#![allow(clippy::mutex_atomic)]
48
49use libmedusa_zip as lib;
50
51use pyo3::{exceptions::PyValueError, prelude::*};
52
53use std::path::PathBuf;
54
55
56#[pyclass]
57#[derive(Clone)]
58pub struct EntryName(pub String);
59
60#[pymethods]
61impl EntryName {
62 #[new]
63 fn new(name: String) -> PyResult<Self> {
64 let parsed =
66 lib::EntryName::validate(name).map_err(|e| PyValueError::new_err(format!("{}", e)))?;
67 Ok(parsed.into())
68 }
69
70 fn __repr__(&self) -> String { format!("EntryName({:?})", &self.0) }
71
72 fn __str__(&self) -> String { self.0.clone() }
73}
74
75impl TryFrom<EntryName> for lib::EntryName {
76 type Error = lib::MedusaNameFormatError;
77
78 fn try_from(x: EntryName) -> Result<Self, Self::Error> {
79 let EntryName(x) = x;
80 Self::validate(x)
81 }
82}
83
84impl From<lib::EntryName> for EntryName {
85 fn from(x: lib::EntryName) -> Self { Self(x.into_string()) }
86}
87
88
89#[pyclass]
90#[derive(Clone)]
91pub struct FileSource {
92 #[pyo3(get)]
93 pub name: EntryName,
94 #[pyo3(get)]
95 pub source: PathBuf,
96}
97
98#[pymethods]
99impl FileSource {
100 #[new]
101 fn new(name: EntryName, source: PathBuf) -> Self { Self { name, source } }
102
103 fn __repr__(&self, py: Python<'_>) -> PyResult<String> {
104 let Self { name, source } = self;
105 let name = crate::util::repr(py, name.clone())?;
106 let source = crate::util::repr(py, source.clone())?;
107 Ok(format!("FileSource(name={}, source={})", name, source))
108 }
109}
110
111impl TryFrom<FileSource> for lib::FileSource {
112 type Error = lib::MedusaNameFormatError;
113
114 fn try_from(x: FileSource) -> Result<Self, Self::Error> {
115 let FileSource { name, source } = x;
116 let name: lib::EntryName = name.try_into()?;
117 Ok(Self { name, source })
118 }
119}
120
121impl From<lib::FileSource> for FileSource {
122 fn from(x: lib::FileSource) -> Self {
123 let lib::FileSource { name, source } = x;
124 Self {
125 name: name.into(),
126 source,
127 }
128 }
129}
130
131
132#[cfg(feature = "sync")]
135pub(crate) static TOKIO_RUNTIME: once_cell::sync::Lazy<tokio::runtime::Runtime> =
136 once_cell::sync::Lazy::new(|| {
137 tokio::runtime::Runtime::new().expect("creating ffi runtime failed")
138 });
139
140
141fn add_submodule(parent: &PyModule, py: Python<'_>, child: &PyModule) -> PyResult<()> {
142 parent.add_submodule(child)?;
143 py.import("sys")?
144 .getattr("modules")?
145 .set_item(format!("{}.{}", parent.name()?, child.name()?), child)?;
146 Ok(())
147}
148
149#[pymodule]
153fn pymedusa_zip(py: Python<'_>, medusa_zip: &PyModule) -> PyResult<()> {
154 let crawl = crawl::crawl_module(py)?;
155 add_submodule(medusa_zip, py, crawl)?;
156 let merge = merge::merge_module(py)?;
157 add_submodule(medusa_zip, py, merge)?;
158 let destination = destination::destination_module(py)?;
159 add_submodule(medusa_zip, py, destination)?;
160 let zip = zip::zip_module(py)?;
161 add_submodule(medusa_zip, py, zip)?;
162
163 medusa_zip.add_class::<EntryName>()?;
164 medusa_zip.add_class::<FileSource>()?;
165
166 Ok(())
167}
168
169mod crawl;
170mod destination;
171mod merge;
172mod zip;
173
174mod util;