Skip to content

Commit 747c405

Browse files
committed
pcsc: Allow a transaction to own the underlying card.
- Since Rust's support for self-referencial data structures is weak, there are cases where it is useful for a transaction to own the card and not just for it to hold a mutable reference. In these cases, the caller needs to explicitly end the transaction to recover the underlying card, but that is acceptable. - Generalize `Transaction` to be generic over a `BorrowMut<Card>`. Add new functions `Card::begin_transaction_owned` and `Card::begin_transaction2_owned` to create this type of transaction. Change `Transaction::end` to return the underlying card. And, add a variant, `Transaction::end2`, to unconditionally return the underlying card independent of whether ending the transaction succeeded. - Fixes #28.
1 parent f210772 commit 747c405

File tree

1 file changed

+106
-16
lines changed

1 file changed

+106
-16
lines changed

pcsc/src/lib.rs

+106-16
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
extern crate bitflags;
105105
extern crate pcsc_sys as ffi;
106106

107+
use std::borrow::BorrowMut;
107108
use std::ffi::{CStr, CString};
108109
use std::mem::{forget, transmute};
109110
use std::ops::Deref;
@@ -632,8 +633,10 @@ pub struct Card {
632633
// - There can only be one active transaction at a time.
633634
// - All operations on the card must be performed through the transaction
634635
// for the duration of the transaction's lifetime.
635-
pub struct Transaction<'tx> {
636-
card: &'tx mut Card,
636+
pub struct Transaction<C> where C: BorrowMut<Card> {
637+
// This is an option, because we need to tombstone it to
638+
// effectively disable the Drop implementation.
639+
card: Option<C>,
637640
}
638641

639642
/// An iterator over card reader names.
@@ -1161,14 +1164,45 @@ impl Card {
11611164
/// [2]: https://msdn.microsoft.com/en-us/library/aa379469.aspx
11621165
pub fn transaction(
11631166
&mut self,
1164-
) -> Result<Transaction, Error> {
1167+
) -> Result<Transaction<&mut Card>, Error> {
11651168
unsafe {
11661169
try_pcsc!(ffi::SCardBeginTransaction(
11671170
self.handle,
11681171
));
11691172

11701173
Ok(Transaction {
1171-
card: self,
1174+
card: Some(self),
1175+
})
1176+
}
1177+
}
1178+
1179+
/// Start a new exclusive transaction with the card.
1180+
///
1181+
/// This function is like [`Card::transaction`], but it takes
1182+
/// ownership of the underlying [`Card`]. You can get the `Card`
1183+
/// back by explicitly ending the transaction using
1184+
/// [`Transaction::end`] or [`Transaction::end2`].
1185+
///
1186+
/// This function is useful when you want to save a
1187+
/// [`Transaction`], but can't because Rust's support for
1188+
/// self-referential data structures is still awkward at best.
1189+
///
1190+
/// Note: you shouldn't keep a transaction open for longer than
1191+
/// necessary. Microsoft's PCSC implementation, for instance,
1192+
/// will [automatically end a transaction][1] after 5 seconds of
1193+
/// inactivity.
1194+
///
1195+
/// [1]: https://msdn.microsoft.com/en-us/library/aa379469.aspx
1196+
pub fn transaction_owned(
1197+
self,
1198+
) -> Result<Transaction<Card>, Error> {
1199+
unsafe {
1200+
try_pcsc!(ffi::SCardBeginTransaction(
1201+
self.handle,
1202+
));
1203+
1204+
Ok(Transaction {
1205+
card: Some(self),
11721206
})
11731207
}
11741208
}
@@ -1193,7 +1227,41 @@ impl Card {
11931227
/// [2]: https://msdn.microsoft.com/en-us/library/aa379469.aspx
11941228
pub fn transaction2(
11951229
&mut self,
1196-
) -> Result<Transaction, (&mut Self, Error)> {
1230+
) -> Result<Transaction<&mut Card>, (&mut Self, Error)> {
1231+
unsafe {
1232+
let err = ffi::SCardBeginTransaction(
1233+
self.handle,
1234+
);
1235+
if err != ffi::SCARD_S_SUCCESS {
1236+
return Err((self, Error::from_raw(err)));
1237+
}
1238+
1239+
return Ok(Transaction {
1240+
card: Some(self),
1241+
})
1242+
}
1243+
}
1244+
1245+
/// Start a new exclusive transaction with the card.
1246+
///
1247+
/// This function is like [`Card::transaction2`], but it takes
1248+
/// ownership of the underlying [`Card`]. You can get the `Card`
1249+
/// back by explicitly ending the transaction using
1250+
/// [`Transaction::end`] or [`Transaction::end2`].
1251+
///
1252+
/// This function is useful when you want to save a
1253+
/// [`Transaction`], but can't because Rust's support for
1254+
/// self-referential data structures is still awkward at best.
1255+
///
1256+
/// Note: you shouldn't keep a transaction open for longer than
1257+
/// necessary. Microsoft's PCSC implementation, for instance,
1258+
/// will [automatically end a transaction][1] after 5 seconds of
1259+
/// inactivity.
1260+
///
1261+
/// [1]: https://msdn.microsoft.com/en-us/library/aa379469.aspx
1262+
pub fn transaction2_owned(
1263+
self,
1264+
) -> Result<Transaction<Card>, (Self, Error)> {
11971265
unsafe {
11981266
let err = ffi::SCardBeginTransaction(
11991267
self.handle,
@@ -1203,7 +1271,7 @@ impl Card {
12031271
}
12041272

12051273
return Ok(Transaction {
1206-
card: self,
1274+
card: Some(self),
12071275
})
12081276
}
12091277
}
@@ -1639,7 +1707,9 @@ impl Drop for Card {
16391707
unsafe impl Send for Card {}
16401708
unsafe impl Sync for Card {}
16411709

1642-
impl<'tx> Transaction<'tx> {
1710+
impl<C> Transaction<C>
1711+
where C: BorrowMut<Card>
1712+
{
16431713
/// End the transaction.
16441714
///
16451715
/// In case of error, ownership of the transaction is returned to the
@@ -1658,46 +1728,66 @@ impl<'tx> Transaction<'tx> {
16581728
/// this function if you want to handle errors or use a different
16591729
/// disposition method.
16601730
pub fn end(
1661-
self,
1731+
mut self,
16621732
disposition: Disposition,
1663-
) -> Result<(), (Transaction<'tx>, Error)> {
1733+
) -> Result<C, (Self, Error)> {
16641734
unsafe {
16651735
let err = ffi::SCardEndTransaction(
1666-
self.card.handle,
1736+
self.card.as_mut().unwrap().borrow().handle,
16671737
disposition.into_raw(),
16681738
);
16691739
if err != 0 {
16701740
return Err((self, Error::from_raw(err)));
16711741
}
16721742

16731743
// Skip the drop, we did it "manually".
1674-
forget(self);
1744+
Ok(self.card.take().unwrap())
1745+
}
1746+
}
16751747

1676-
Ok(())
1748+
/// End the transaction.
1749+
///
1750+
/// This function is like [`Transaction::end`], but you always get
1751+
/// back the underlying card.
1752+
pub fn end2(
1753+
self,
1754+
disposition: Disposition,
1755+
) -> C {
1756+
match self.end(disposition) {
1757+
Ok(c) => c,
1758+
Err((mut tx, _err)) => tx.card.take().unwrap(),
16771759
}
16781760
}
16791761
}
16801762

1681-
impl<'tx> Drop for Transaction<'tx> {
1763+
impl<C> Drop for Transaction<C>
1764+
where C: BorrowMut<Card>
1765+
{
16821766
fn drop(&mut self) {
1767+
if self.card.is_none() {
1768+
// Already dropped.
1769+
return;
1770+
}
1771+
16831772
unsafe {
16841773
// Error is ignored here; to do proper error handling,
16851774
// end() should be called manually.
16861775
//
16871776
// Disposition is hard-coded to LeaveCard here; to use
16881777
// another method, end() should be called manually.
16891778
let _err = ffi::SCardEndTransaction(
1690-
self.card.handle,
1779+
self.handle,
16911780
Disposition::LeaveCard.into_raw(),
16921781
);
16931782
}
16941783
}
16951784
}
16961785

1697-
impl<'tx> Deref for Transaction<'tx> {
1786+
impl<C> Deref for Transaction<C> where C: BorrowMut<Card>
1787+
{
16981788
type Target = Card;
16991789

17001790
fn deref(&self) -> &Card {
1701-
self.card
1791+
self.card.as_ref().unwrap().borrow()
17021792
}
17031793
}

0 commit comments

Comments
 (0)