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