lt_rs/
errors.rs

1use crate::ffi::error::ffi::{self};
2
3#[derive(Debug)]
4pub enum LtrsError {
5    LibtorrentError(LibtorrentError),
6    HttpError(HttpError),
7    GzipError(GzipError),
8    I2pError(I2pError),
9    PcpError(PcpError),
10    BdecodeError(BdecodeError),
11    SocksError(SocksError),
12    UpnpError(UpnpError),
13    // If error category is not known
14    // This is enable even without safe_enums feature because we need to check
15    // libtorrent to see if we already covered all possible variants
16    Unknown(i32),
17}
18
19impl LtrsError {
20    pub fn is_ok(&self) -> bool {
21        match self {
22            LtrsError::LibtorrentError(e) => {
23                matches!(e, LibtorrentError::NoError)
24            }
25            LtrsError::HttpError(e) => {
26                matches!(e, HttpError::Ok)
27            }
28            LtrsError::GzipError(e) => {
29                matches!(e, GzipError::NoError)
30            }
31            LtrsError::I2pError(e) => {
32                matches!(e, I2pError::NoError)
33            }
34            LtrsError::PcpError(e) => {
35                matches!(e, PcpError::Success)
36            }
37            LtrsError::BdecodeError(e) => {
38                matches!(e, BdecodeError::NoError)
39            }
40            LtrsError::SocksError(e) => {
41                matches!(e, SocksError::NoError)
42            }
43            LtrsError::UpnpError(e) => {
44                matches!(e, UpnpError::NoError)
45            }
46            LtrsError::Unknown(e) => *e == 0,
47        }
48    }
49}
50
51#[derive(Debug)]
52#[cfg_attr(feature = "safe_enums", derive(num_enum::FromPrimitive))]
53#[repr(u8)]
54pub enum LibtorrentError {
55    /// Not an error
56    NoError = 0,
57    /// Two torrents has files which end up overwriting each other
58    FileCollision,
59    /// A piece did not match its piece hash
60    FailedHashCheck,
61    /// The .torrent file does not contain a bencoded dictionary at
62    /// its top level
63    TorrentIsNoDict,
64    /// The .torrent file does not have an ``info`` dictionary
65    TorrentMissingInfo,
66    /// The .torrent file's ``info`` entry is not a dictionary
67    TorrentInfoNoDict,
68    /// The .torrent file does not have a ``piece length`` entry
69    TorrentMissingPieceLength,
70    /// The .torrent file does not have a ``name`` entry
71    TorrentMissingName,
72    /// The .torrent file's name entry is invalid
73    TorrentInvalidName,
74    /// The length of a file, or of the whole .torrent file is invalid.
75    /// Either negative or not an integer
76    TorrentInvalidLength,
77    /// Failed to parse a file entry in the .torrent
78    TorrentFileParseFailed,
79    /// The ``pieces`` field is missing or invalid in the .torrent file
80    TorrentMissingPieces,
81    /// The ``pieces`` string has incorrect length
82    TorrentInvalidHashes,
83    /// The .torrent file has more pieces than is supported by libtorrent
84    TooManyPiecesInTorrent,
85    /// The metadata (.torrent file) that was received from the swarm
86    /// matched the info-hash, but failed to be parsed
87    InvalidSwarmMetadata,
88    /// The file or buffer is not correctly bencoded
89    InvalidBencoding,
90    /// The .torrent file does not contain any files
91    NoFilesInTorrent,
92    /// The string was not properly url-encoded as expected
93    InvalidEscapedString,
94    /// Operation is not permitted since the session is shutting down
95    SessionIsClosing,
96    /// There's already a torrent with that info-hash added to the
97    /// session
98    DuplicateTorrent,
99    /// The supplied torrent_handle is not referring to a valid torrent
100    InvalidTorrentHandle,
101    /// The type requested from the entry did not match its type
102    InvalidEntryType,
103    /// The specified URI does not contain a valid info-hash
104    MissingInfoHashInUri,
105    /// One of the files in the torrent was unexpectedly small. This
106    /// might be caused by files being changed by an external process
107    FileTooShort,
108    /// The URL used an unknown protocol. Currently ``http`` and
109    /// ``https`` (if built with openssl support) are recognized. For
110    /// trackers ``udp`` is recognized as well.
111    UnsupportedUrlProtocol,
112    /// The URL did not conform to URL syntax and failed to be parsed
113    UrlParseError,
114    /// The peer sent a piece message of length 0
115    PeerSentEmptyPiece,
116    /// A bencoded structure was corrupt and failed to be parsed
117    ParseFailed,
118    /// The fast resume file was missing or had an invalid file version
119    /// tag
120    InvalidFileTag,
121    /// The fast resume file was missing or had an invalid info-hash
122    MissingInfoHash,
123    /// The info-hash did not match the torrent
124    MismatchingInfoHash,
125    /// The URL contained an invalid hostname
126    InvalidHostname,
127    /// The URL had an invalid port
128    InvalidPort,
129    /// The port is blocked by the port-filter, and prevented the
130    /// connection
131    PortBlocked,
132    /// The IPv6 address was expected to end with "]"
133    ExpectedCloseBracketInAddress,
134    /// The torrent is being destructed, preventing the operation to
135    /// succeed
136    DestructingTorrent,
137    /// The connection timed out
138    TimedOut,
139    /// The peer is upload only, and we are upload only. There's no point
140    /// in keeping the connection
141    UploadUploadConnection,
142    /// The peer is upload only, and we're not interested in it. There's
143    /// no point in keeping the connection
144    UninterestingUploadPeer,
145    /// The peer sent an unknown info-hash
146    InvalidInfoHash,
147    /// The torrent is paused, preventing the operation from succeeding
148    TorrentPaused,
149    /// The peer sent an invalid have message, either wrong size or
150    /// referring to a piece that doesn't exist in the torrent
151    InvalidHave,
152    /// The bitfield message had the incorrect size
153    InvalidBitfieldSize,
154    /// The peer kept requesting pieces after it was choked, possible
155    /// abuse attempt.
156    TooManyRequestsWhenChoked,
157    /// The peer sent a piece message that does not correspond to a
158    /// piece request sent by the client
159    InvalidPiece,
160    /// memory allocation failed
161    NoMemory,
162    /// The torrent is aborted, preventing the operation to succeed
163    TorrentAborted,
164    /// The peer is a connection to ourself, no point in keeping it
165    SelfConnection,
166    /// The peer sent a piece message with invalid size, either negative
167    /// or greater than one block
168    InvalidPieceSize,
169    /// The peer has not been interesting or interested in us for too
170    /// long, no point in keeping it around
171    TimedOutNoInterest,
172    /// The peer has not said anything in a long time, possibly dead
173    TimedOutInactivity,
174    /// The peer did not send a handshake within a reasonable amount of
175    /// time, it might not be a bittorrent peer
176    TimedOutNoHandshake,
177    /// The peer has been unchoked for too long without requesting any
178    /// data. It might be lying about its interest in us
179    TimedOutNoRequest,
180    /// The peer sent an invalid choke message
181    InvalidChoke,
182    /// The peer send an invalid unchoke message
183    InvalidUnchoke,
184    /// The peer sent an invalid interested message
185    InvalidInterested,
186    /// The peer sent an invalid not-interested message
187    InvalidNotInterested,
188    /// The peer sent an invalid piece request message
189    InvalidRequest,
190    /// The peer sent an invalid hash-list message (this is part of the
191    /// merkle-torrent extension)
192    InvalidHashList,
193    /// The peer sent an invalid hash-piece message (this is part of the
194    /// merkle-torrent extension)
195    InvalidHashPiece,
196    /// The peer sent an invalid cancel message
197    InvalidCancel,
198    /// The peer sent an invalid DHT port-message
199    InvalidDhtPort,
200    /// The peer sent an invalid suggest piece-message
201    InvalidSuggest,
202    /// The peer sent an invalid have all-message
203    InvalidHaveAll,
204    /// The peer sent an invalid have none-message
205    InvalidHaveNone,
206    /// The peer sent an invalid reject message
207    InvalidReject,
208    /// The peer sent an invalid allow fast-message
209    InvalidAllowFast,
210    /// The peer sent an invalid extension message ID
211    InvalidExtended,
212    /// The peer sent an invalid message ID
213    InvalidMessage,
214    /// The synchronization hash was not found in the encrypted handshake
215    SyncHashNotFound,
216    /// The encryption constant in the handshake is invalid
217    InvalidEncryptionConstant,
218    /// The peer does not support plain text, which is the selected mode
219    NoPlaintextMode,
220    /// The peer does not support RC4, which is the selected mode
221    NoRc4Mode,
222    /// The peer does not support any of the encryption modes that the
223    /// client supports
224    UnsupportedEncryptionMode,
225    /// The peer selected an encryption mode that the client did not
226    /// advertise and does not support
227    UnsupportedEncryptionModeSelected,
228    /// The pad size used in the encryption handshake is of invalid size
229    InvalidPadSize,
230    /// The encryption handshake is invalid
231    InvalidEncryptHandshake,
232    /// The client is set to not support incoming encrypted connections
233    /// and this is an encrypted connection
234    NoIncomingEncrypted,
235    /// The client is set to not support incoming regular bittorrent
236    /// connections, and this is a regular connection
237    NoIncomingRegular,
238    /// The client is already connected to this peer-ID
239    DuplicatePeerId,
240    /// Torrent was removed
241    TorrentRemoved,
242    /// The packet size exceeded the upper sanity check-limit
243    PacketTooLarge,
244
245    Reserved,
246
247    /// The web server responded with an error
248    HttpError,
249    /// The web server response is missing a location header
250    MissingLocation,
251    /// The web seed redirected to a path that no longer matches the
252    /// .torrent directory structure
253    InvalidRedirection,
254    /// The connection was closed because it redirected to a different
255    /// URL
256    Redirecting,
257    /// The HTTP range header is invalid
258    InvalidRangeLtrsError,
259    /// The HTTP response did not have a content length
260    NoContentLength,
261    /// The IP is blocked by the IP filter
262    BannedByIpFilter,
263    /// At the connection limit
264    TooManyConnections,
265    /// The peer is marked as banned
266    PeerBanned,
267    /// The torrent is stopping, causing the operation to fail
268    StoppingTorrent,
269    /// The peer has sent too many corrupt pieces and is banned
270    TooManyCorruptPieces,
271    /// The torrent is not ready to receive peers
272    TorrentNotReady,
273    /// The peer is not completely constructed yet
274    PeerNotConstructed,
275    /// The session is closing, causing the operation to fail
276    SessionClosing,
277    /// The peer was disconnected in order to leave room for a
278    /// potentially better peer
279    OptimisticDisconnect,
280    /// The torrent is finished
281    TorrentFinished,
282    /// No UPnP router found
283    NoRouter,
284    /// The metadata message says the metadata exceeds the limit
285    MetadataTooLarge,
286    /// The peer sent an invalid metadata request message
287    InvalidMetadataRequest,
288    /// The peer advertised an invalid metadata size
289    InvalidMetadataSize,
290    /// The peer sent a message with an invalid metadata offset
291    InvalidMetadataOffset,
292    /// The peer sent an invalid metadata message
293    InvalidMetadataMessage,
294    /// The peer sent a peer exchange message that was too large
295    PexMessageTooLarge,
296    /// The peer sent an invalid peer exchange message
297    InvalidPexMessage,
298    /// The peer sent an invalid tracker exchange message
299    InvalidLtTrackerMessage,
300    /// The peer sent pex messages too often. This is a possible
301    /// attempt of and attack
302    TooFrequentPex,
303    /// The operation failed because it requires the torrent to have
304    /// the metadata (.torrent file) and it doesn't have it yet.
305    /// This happens for magnet links before they have downloaded the
306    /// metadata, and also torrents added by URL.
307    NoMetadata,
308    /// The peer sent an invalid ``dont_have`` message. The don't have
309    /// message is an extension to allow peers to advertise that the
310    /// no longer has a piece they previously had.
311    InvalidDontHave,
312    /// The peer tried to connect to an SSL torrent without connecting
313    /// over SSL.
314    RequiresSslConnection,
315    /// The peer tried to connect to a torrent with a certificate
316    /// for a different torrent.
317    InvalidSslCert,
318    /// the torrent is not an SSL torrent, and the operation requires
319    /// an SSL torrent
320    NotAnSslTorrent,
321    /// peer was banned because its listen port is within a banned port
322    /// range, as specified by the port_filter.
323    BannedByPortFilter,
324    /// The session_handle is not referring to a valid session_impl
325    InvalidSessionHandle,
326    /// the listen socket associated with this request was closed
327    InvalidListenSocket,
328    InvalidHashRequest,
329    InvalidHashes,
330    InvalidHashReject,
331
332    /// these error codes are deprecated, NAT-PMP/PCP error codes have
333    /// been moved to their own category
334
335    // /// The NAT-PMP router responded with an unsupported protocol version
336    // #[deprecated]
337    // UnsupportedProtocolVersion = 120,
338    // /// You are not authorized to map ports on this NAT-PMP router
339    // #[deprecated]
340    // NatpmpNotAuthorized,
341    // /// The NAT-PMP router failed because of a network failure
342    // #[deprecated]
343    // NetworkFailure,
344    // /// The NAT-PMP router failed because of lack of resources
345    // #[deprecated]
346    // NoResources,
347    // /// The NAT-PMP router failed because an unsupported opcode was sent
348    // #[deprecated]
349    // UnsupportedOpcode,
350
351    /// The resume data file is missing the ``file sizes`` entry
352    MissingFileSizes = 130,
353    /// The resume data file ``file sizes`` entry is empty
354    NoFilesInResumeData,
355    /// The resume data file is missing the ``pieces`` and ``slots`` entry
356    MissingPieces,
357    /// The number of files in the resume data does not match the number
358    /// of files in the torrent
359    MismatchingNumberOfFiles,
360    /// One of the files on disk has a different size than in the fast
361    /// resume file
362    MismatchingFileSize,
363    /// One of the files on disk has a different timestamp than in the
364    /// fast resume file
365    MismatchingFileTimestamp,
366    /// The resume data file is not a dictionary
367    NotADictionary,
368    /// The ``blocks per piece`` entry is invalid in the resume data file
369    InvalidBlocksPerPiece,
370    /// The resume file is missing the ``slots`` entry, which is required
371    /// for torrents with compact allocation. *DEPRECATED*
372    MissingSlots,
373    /// The resume file contains more slots than the torrent
374    TooManySlots,
375    /// The ``slot`` entry is invalid in the resume data
376    InvalidSlotList,
377    /// One index in the ``slot`` list is invalid
378    InvalidPieceIndex,
379    /// The pieces on disk needs to be re-ordered for the specified
380    /// allocation mode. This happens if you specify sparse allocation
381    /// and the files on disk are using compact storage. The pieces needs
382    /// to be moved to their right position. *DEPRECATED*
383    PiecesNeedReorder,
384    /// this error is returned when asking to save resume data and
385    /// specifying the flag to only save when there's anything new to save
386    /// (torrent_handle::only_if_modified) and there wasn't anything changed.
387    ResumeDataNotModified,
388    /// the save_path in add_torrent_params is not valid
389    InvalidSavePath,
390
391    /// The HTTP header was not correctly formatted
392    HttpParseError = 150,
393    /// The HTTP response was in the 300-399 range but lacked a location
394    /// header
395    HttpMissingLocation,
396    /// The HTTP response was encoded with gzip or deflate but
397    /// decompressing it failed
398    HttpFailedDecompress,
399
400    /// The URL specified an i2p address, but no i2p router is configured
401    NoI2pRouter = 160,
402    /// i2p acceptor is not available yet, can't announce without endpoint
403    NoI2pEndpoint,
404
405    /// The tracker URL doesn't support transforming it into a scrape
406    /// URL. i.e. it doesn't contain "announce.
407    ScrapeNotAvailable = 170,
408    /// invalid tracker response
409    InvalidTrackerResponse,
410    /// invalid peer dictionary entry. Not a dictionary
411    InvalidPeerDict,
412    /// tracker sent a failure message
413    TrackerFailure,
414    /// missing or invalid ``files`` entry
415    InvalidFilesEntry,
416    /// missing or invalid ``hash`` entry
417    InvalidHashEntry,
418    /// missing or invalid ``peers`` and ``peers6`` entry
419    InvalidPeersEntry,
420    /// UDP tracker response packet has invalid size
421    InvalidTrackerResponseLength,
422    /// invalid transaction id in UDP tracker response
423    InvalidTrackerTransactionId,
424    /// invalid action field in UDP tracker response
425    InvalidTrackerAction,
426    /// skipped announce (because it's assumed to be unreachable over the
427    /// given source network interface)
428    AnnounceSkipped,
429
430    /// expected string in bencoded string
431    // #[deprecated]
432    // ExpectedString190,
433    // /// expected colon in bencoded string
434    // #[deprecated]
435    // ExpectedColon,
436    // /// unexpected end of file in bencoded string
437    // #[deprecated]
438    // UnexpectedEof,
439    // /// expected value (list, dict, int or string) in bencoded string
440    // #[deprecated]
441    // ExpectedValue,
442    // /// bencoded recursion depth limit exceeded
443    // #[deprecated]
444    // DepthExceeded,
445    // /// bencoded item count limit exceeded
446    // #[deprecated]
447    // LimitExceeded,
448    // /// integer overflow
449    // #[deprecated]
450    // Overflow,
451
452    /// random number generation failed
453    NoEntropy = 200,
454    /// blocked by SSRF mitigation
455    SsrvMitigation,
456    /// blocked because IDNA host names are banned
457    BlockedByIdna,
458
459    /// the torrent file has an unknown meta version
460    TorrentUnknownVersion = 210,
461    /// the v2 torrent file has no file tree
462    TorrentMissingFileTree,
463    /// the torrent contains v2 keys but does not specify meta version 2
464    TorrentMissingMetaVersion,
465    /// the v1 and v2 file metadata does not match
466    TorrentInconsistentFiles,
467    /// one or more files are missing piece layer hashes
468    TorrentMissingPieceLayer,
469    /// a piece layer has the wrong size or failed hash check
470    TorrentInvalidPieceLayer,
471    /// a v2 file entry has no root hash
472    TorrentMissingPiecesRoot,
473    /// the v1 and v2 hashes do not describe the same data
474    TorrentInconsistentHashes,
475    /// a file in the v2 metadata has the pad attribute set
476    TorrentInvalidPadFile,
477    /// The value is not used by libtorrent itself, it is only used
478    /// internally to match againt C++ integers and prevent crashes
479    /// as otherwise we would need a unreachable!() in the match arms.
480    #[cfg(feature = "safe_enums")]
481    #[num_enum(default)]
482    UnknownErrorCode,
483}
484
485#[derive(Debug)]
486#[cfg_attr(feature = "safe_enums", derive(num_enum::FromPrimitive))]
487#[repr(u16)]
488pub enum HttpError {
489    Cont = 100,
490    Ok = 200,
491    Created = 201,
492    Accepted = 202,
493    NoContent = 204,
494    MultipleChoices = 300,
495    MovedPermanently = 301,
496    MovedTemporarily = 302,
497    NotModified = 304,
498    BadRequest = 400,
499    Unauthorized = 401,
500    Forbidden = 403,
501    NotFound = 404,
502    InternalServerError = 500,
503    NotImplemented = 501,
504    BadGateway = 502,
505    ServiceUnavailable = 503,
506    #[cfg(feature = "safe_enums")]
507    #[num_enum(default)]
508    UnknownErrorCode,
509}
510
511#[derive(Debug)]
512#[cfg_attr(feature = "safe_enums", derive(num_enum::FromPrimitive))]
513#[repr(u8)]
514pub enum I2pError {
515    NoError = 0,
516    ParseFailed,
517    CantReachPeer,
518    I2pError,
519    InvalidKey,
520    InvalidId,
521    Timeout,
522    KeyNotFound,
523    DuplicatedId,
524    #[cfg(feature = "safe_enums")]
525    #[num_enum(default)]
526    UnknownErrorCode,
527}
528
529#[derive(Debug)]
530#[cfg_attr(feature = "safe_enums", derive(num_enum::FromPrimitive))]
531#[repr(u8)]
532pub enum GzipError {
533    NoError = 0,
534    InvalidGzipHeader,
535    InflatedDataTooLarge,
536    DataDidNotTerminate,
537    SpaceExhausted,
538    InvalidBlockType,
539    InvalidStoredBlockLength,
540    TooManyLengthOrDistanceCodes,
541    CodeLengthsCodesIncomplete,
542    RepeatLengthsWithNoFirstLength,
543    RepeatMoreThanSpecifiedLengths,
544    InvalidLiteralLengthCodeLengths,
545    InvalidDistanceCodeLengths,
546    InvalidLiteralCodeInBlock,
547    DistanceTooFarBackInBlock,
548    UnknownGzipError,
549    #[cfg(feature = "safe_enums")]
550    #[num_enum(default)]
551    UnknownErrorCode,
552}
553
554#[derive(Debug)]
555#[cfg_attr(feature = "safe_enums", derive(num_enum::FromPrimitive))]
556#[repr(u8)]
557pub enum PcpError {
558    Success = 0,
559    UnsupportedVersion,
560    NotAuthorized,
561    MalformedRequest,
562    UnsupportedOpcode,
563    UnsupportedOption,
564    MalformedOption,
565    NetworkFailure,
566    NoResources,
567    UnsupportedProtocol,
568    UserExQuota,
569    CannotProvideExternal,
570    AddressMismatch,
571    ExcessiveRemotePeers,
572    #[cfg(feature = "safe_enums")]
573    #[num_enum(default)]
574    UnknownErrorCode,
575}
576
577#[derive(Debug)]
578#[cfg_attr(feature = "safe_enums", derive(num_enum::FromPrimitive))]
579#[repr(u8)]
580pub enum BdecodeError {
581    NoError = 0,
582    ExpectedDigit,
583    ExpectedColon,
584    UnexpectedEof,
585    ExpectedValue,
586    DepthExceeded,
587    LimitExceeded,
588    Overflow,
589    #[cfg(feature = "safe_enums")]
590    #[num_enum(default)]
591    UnknownErrorCode,
592}
593
594#[derive(Debug)]
595#[cfg_attr(feature = "safe_enums", derive(num_enum::FromPrimitive))]
596#[repr(u8)]
597pub enum SocksError {
598    NoError = 0,
599    UnsupportedVersion,
600    UnsupportedAuthenticationMethod,
601    UnsupportedAuthenticationVersion,
602    AuthenticationError,
603    UsernameRequired,
604    GeneralFailure,
605    CommandNotSupported,
606    NoIdentd,
607    IdentdError,
608    #[cfg(feature = "safe_enums")]
609    #[num_enum(default)]
610    UnknownErrorCode,
611}
612
613#[derive(Debug)]
614#[cfg_attr(feature = "safe_enums", derive(num_enum::FromPrimitive))]
615#[repr(u16)]
616pub enum UpnpError {
617    NoError = 0,
618    InvalidArgument = 402,
619    ActionFailed = 501,
620    ValueNotInArray = 714,
621    SourceIpCannotBeWildcarded = 715,
622    ExternalPortCannotBeWildcarded = 716,
623    PortMappingConflict = 718,
624    InternalPortMustMatchExternal = 724,
625    OnlyPermanentLeasesSupported = 725,
626    RemoteHostMustBeWildcarded = 726,
627    ExternalPortMustBeWildcarded = 727,
628    #[cfg(feature = "safe_enums")]
629    #[num_enum(default)]
630    UnknownErrorCode,
631}
632
633impl From<ffi::Error> for LtrsError {
634    fn from(err: ffi::Error) -> Self {
635        cfg_if::cfg_if! {
636            if #[cfg(feature = "safe_enums")] {
637                match err.category {
638                    ffi::ErrorCategory::LibtorrentError => {
639                        LtrsError::LibtorrentError(err.code.into())
640                    }
641                    ffi::ErrorCategory::HttpError => {
642                        LtrsError::HttpError(err.code.into())
643                    }
644                    ffi::ErrorCategory::GzipError => {
645                        LtrsError::GzipError(err.code.into())
646                    }
647                    ffi::ErrorCategory::PcpError => {
648                        LtrsError::PcpError(err.code.into())
649                    }
650                    ffi::ErrorCategory::BdecodeError => {
651                        LtrsError::BdecodeError(err.code.into())
652                    }
653                    ffi::ErrorCategory::SocksError => {
654                        LtrsError::SocksError(err.code.into())
655                    }
656                    ffi::ErrorCategory::UpnpError => {
657                        LtrsError::UpnpError(err.codee.into())
658                    }
659                    ffi::ErrorCategory::I2pError => {
660                        LtrsError::I2pError(err.code.into())
661                    }
662                    _ => LtrsError::Unknown(err.code),
663                }
664            } else {
665                // SAFETY: We trust that libtorrent will not return invalid error codes
666                unsafe {
667                    match err.category {
668                        ffi::ErrorCategory::LibtorrentError => {
669                            LtrsError::LibtorrentError(std::mem::transmute(err.code as u8))
670                        }
671                        ffi::ErrorCategory::HttpError => {
672                            LtrsError::HttpError(std::mem::transmute(err.code as u16))
673                        }
674                        ffi::ErrorCategory::GzipError => {
675                            LtrsError::GzipError(std::mem::transmute(err.code as u8))
676                        }
677                        ffi::ErrorCategory::PcpError => {
678                            LtrsError::PcpError(std::mem::transmute(err.code as u8))
679                        }
680                        ffi::ErrorCategory::BdecodeError => {
681                            LtrsError::BdecodeError(std::mem::transmute(err.code as u8))
682                        }
683                        ffi::ErrorCategory::SocksError => {
684                            LtrsError::SocksError(std::mem::transmute(err.code as u8))
685                        }
686                        ffi::ErrorCategory::UpnpError => {
687                            LtrsError::UpnpError(std::mem::transmute(err.code as u16))
688                        }
689                        ffi::ErrorCategory::I2pError => {
690                            LtrsError::I2pError(std::mem::transmute(err.code as u8))
691                        }
692                        _ => LtrsError::Unknown(err.code),
693                    }
694                }
695            }
696        }
697    }
698}