use crate::{resource, sign, Package, PassBuilder, PassConfig};
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use std::{fs::File, io::Read};
#[pyclass]
pub struct PyPassConfig {
#[pyo3(get, set)]
pub organization_name: String,
#[pyo3(get, set)]
pub description: String,
#[pyo3(get, set)]
pub pass_type_identifier: String,
#[pyo3(get, set)]
pub team_identifier: String,
#[pyo3(get, set)]
pub serial_number: String,
}
#[pymethods]
impl PyPassConfig {
#[new]
fn new(
organization_name: String,
description: String,
pass_type_identifier: String,
team_identifier: String,
serial_number: String,
) -> Self {
Self {
organization_name,
description,
pass_type_identifier,
team_identifier,
serial_number,
}
}
}
#[pyfunction]
#[pyo3(signature = (
config,
cert_path,
key_path,
output_path,
icon_path = None,
icon2x_path = None // ← NEW
))]
fn generate_pass(
config: &PyPassConfig,
cert_path: &str,
key_path: &str,
output_path: &str,
icon_path: Option<&str>,
icon2x_path: Option<&str>,
) -> PyResult<()> {
let pass = PassBuilder::new(PassConfig {
organization_name: config.organization_name.clone(),
description: config.description.clone(),
pass_type_identifier: config.pass_type_identifier.clone(),
team_identifier: config.team_identifier.clone(),
serial_number: config.serial_number.clone(),
})
.grouping_identifier("com.example.pass.app".into())
.logo_text("Test pass".into())
.build();
let mut package = Package::new(pass);
if let Some(p) = icon_path {
let f = File::open(p)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(e.to_string()))?;
package
.add_resource(resource::Type::Icon(resource::Version::Standard), f)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?;
}
if let Some(p) = icon2x_path {
let f = File::open(p)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(e.to_string()))?;
package
.add_resource(resource::Type::Icon(resource::Version::Size2X), f) .map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?;
}
let mut cert = Vec::new();
File::open(cert_path)
.and_then(|mut f| f.read_to_end(&mut cert))
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("Cert error: {e}")))?;
let mut key = Vec::new();
File::open(key_path)
.and_then(|mut f| f.read_to_end(&mut key))
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("Key error: {e}")))?;
let scfg = sign::SignConfig::new(sign::WWDR::G4, &cert, &key)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("Sign cfg: {e}")))?;
package.add_certificates(scfg);
let outfile = File::create(output_path)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("Output error: {e}")))?;
package
.write(outfile)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("Package write: {e}")))?;
Ok(())
}
#[pymodule]
fn passes_rs_py(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyPassConfig>()?;
m.add_function(wrap_pyfunction!(generate_pass, m)?)?;
Ok(())
}