1use std::{
10 convert::{TryFrom, TryInto},
11 error::Error,
12 fmt::Display,
13 str::FromStr,
14};
15
16use serde::{ser::SerializeStruct, Deserialize, Serialize};
17use thiserror::Error;
18
19#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
20pub struct StatusCode(u32);
23
24const SUBCODE_MASK: u32 = 0xffff_0000;
25const INFO_BITS_MASK: u32 = 0b0011_1111_1111;
26
27impl StatusCode {
28 pub fn is_good(&self) -> bool {
30 matches!(self.severity(), StatusCodeSeverity::Good)
31 }
32
33 pub fn is_bad(&self) -> bool {
35 matches!(self.severity(), StatusCodeSeverity::Bad)
36 }
37
38 pub fn is_uncertain(&self) -> bool {
40 matches!(self.severity(), StatusCodeSeverity::Uncertain)
41 }
42
43 pub fn bits(&self) -> u32 {
45 self.0
46 }
47
48 pub fn from_category(category: SubStatusCode) -> Self {
50 Self(category as u32)
51 }
52
53 fn get_bool(&self, offset: u8) -> bool {
54 (self.0 & (1 << offset)) != 0
55 }
56
57 #[must_use = "Status code is copied, not modified in place."]
58 fn set_bool(mut self, value: bool, offset: u8) -> Self {
59 self.0 = self.0 & !(1 << offset) | ((value as u32) << offset);
60 self
61 }
62
63 pub fn severity(&self) -> StatusCodeSeverity {
65 StatusCodeSeverity::from_value((self.0 >> 30) & 0b11).unwrap_or(StatusCodeSeverity::Bad)
67 }
68
69 #[must_use = "Status code is copied, not modified in place."]
73 pub fn set_severity(mut self, value: StatusCodeSeverity) -> Self {
74 self.0 = self.0 & !SUBCODE_MASK | ((value as u32) << 30);
77 self
78 }
79
80 pub fn structure_changed(&self) -> bool {
82 self.get_bool(15)
83 }
84
85 #[must_use = "Status code is copied, not modified in place."]
87 pub fn set_structure_changed(self, value: bool) -> Self {
88 self.set_bool(value, 15)
89 }
90
91 pub fn semantics_changed(&self) -> bool {
93 self.get_bool(14)
94 }
95
96 #[must_use = "Status code is copied, not modified in place."]
98 pub fn set_semantics_changed(self, value: bool) -> Self {
99 self.set_bool(value, 14)
100 }
101
102 pub fn sub_code(&self) -> SubStatusCode {
104 SubStatusCode::from_value(self.0 & SUBCODE_MASK).unwrap_or(SubStatusCode::Invalid)
105 }
106
107 #[must_use = "Status code is copied, not modified in place."]
109 pub fn set_sub_code(mut self, value: SubStatusCode) -> Self {
110 self.0 = self.0 & !SUBCODE_MASK | ((value as u32) & SUBCODE_MASK);
111 self
112 }
113
114 pub fn info_type(&self) -> StatusCodeInfoType {
116 StatusCodeInfoType::from_value((self.0 >> 10) & 1).unwrap_or(StatusCodeInfoType::NotUsed)
117 }
118
119 #[must_use = "Status code is copied, not modified in place."]
121 pub fn set_info_type(mut self, value: StatusCodeInfoType) -> Self {
122 self.0 = self.0 & !(1 << 10) | (((value as u32) & 1) << 10);
123 if matches!(value, StatusCodeInfoType::NotUsed) {
125 self.0 &= !INFO_BITS_MASK;
126 }
127 self
128 }
129
130 pub fn limit(&self) -> StatusCodeLimit {
132 StatusCodeLimit::from_value((self.0 >> 8) & 0b11).unwrap_or_default()
134 }
135
136 #[must_use = "Status code is copied, not modified in place."]
138 pub fn set_limit(mut self, limit: StatusCodeLimit) -> Self {
139 self.0 = self.0 & !(0b11 << 8) | ((limit as u32) << 8);
140 self
141 }
142
143 pub fn overflow(&self) -> bool {
145 self.get_bool(7)
146 }
147
148 #[must_use = "Status code is copied, not modified in place."]
150 pub fn set_overflow(self, value: bool) -> Self {
151 self.set_bool(value, 7)
152 }
153
154 pub fn multi_value(&self) -> bool {
156 self.get_bool(4)
157 }
158
159 #[must_use = "Status code is copied, not modified in place."]
161 pub fn set_multi_value(self, value: bool) -> Self {
162 self.set_bool(value, 4)
163 }
164
165 pub fn extra_data(&self) -> bool {
167 self.get_bool(3)
168 }
169
170 #[must_use = "Status code is copied, not modified in place."]
172 pub fn set_extra_data(self, value: bool) -> Self {
173 self.set_bool(value, 3)
174 }
175
176 pub fn partial(&self) -> bool {
178 self.get_bool(2)
179 }
180
181 #[must_use = "Status code is copied, not modified in place."]
183 pub fn set_partial(self, value: bool) -> Self {
184 self.set_bool(value, 2)
185 }
186
187 pub fn value_type(&self) -> StatusCodeValueType {
189 StatusCodeValueType::from_value(self.0 & 0b11).unwrap_or(StatusCodeValueType::Undefined)
190 }
191
192 #[must_use = "Status code is copied, not modified in place."]
194 pub fn set_value_type(mut self, value: StatusCodeValueType) -> Self {
195 self.0 = self.0 & !0b11 | ((value as u32) & 0b11);
196 self
197 }
198
199 pub fn validate(&self) -> Result<(), StatusCodeValidationError> {
204 if self.0 >> 30 == 0b11 {
205 return Err(StatusCodeValidationError::InvalidSeverity);
206 }
207
208 if self.0 & (0b11 << 28) != 0 || self.0 & (0b111 << 11) != 0 || self.0 & (0b11 << 5) != 0 {
209 return Err(StatusCodeValidationError::UsedReservedBit);
210 }
211
212 if self.sub_code() == SubStatusCode::Invalid {
213 return Err(StatusCodeValidationError::UnknownSubCode);
214 }
215
216 if matches!(self.info_type(), StatusCodeInfoType::NotUsed) && self.0 & INFO_BITS_MASK != 0 {
217 return Err(StatusCodeValidationError::InvalidInfoBits);
218 }
219
220 if self.value_type() == StatusCodeValueType::Undefined {
221 return Err(StatusCodeValidationError::UndefinedValueType);
222 }
223
224 Ok(())
225 }
226
227 pub fn try_parse(symbol: &str) -> Result<StatusCode, ParseStatusCodeError> {
236 let mut parts = symbol.split(&[' ', ',']);
237 let Some(root) = parts.next() else {
238 return Err(ParseStatusCodeError::InvalidFormat);
239 };
240 let root = root
241 .replace("Good_", "Good")
242 .replace("Bad_", "Bad")
243 .replace("Uncertain_", "Uncertain");
244 let sub_status = SubStatusCode::from_str(&root)
245 .map_err(|_| ParseStatusCodeError::UnknownSubStatusCode(root))?;
246
247 let mut code = sub_status as u32;
248 let mut limit_args = 0;
249
250 for flag in parts {
251 if flag.trim().is_empty() {
252 continue;
253 }
254 match flag {
255 "Low" => {
256 limit_args += 1;
257 code |= 0x100;
258 }
259 "High" => {
260 limit_args += 1;
261 code |= 0x200;
262 }
263 "Constant" => {
264 limit_args += 1;
265 code |= 0x300;
266 }
267 "StructureChanged" => code |= 1 << 15,
268 "SemanticsChanged" => code |= 1 << 14,
269 "Overflow" => code |= 1 << 7,
270 "MultipleValues" => code |= 0b1_0000,
271 "ExtraData" => code |= 0b1000,
272 "Partial" => code |= 0b100,
273 "Interpolated" => code |= 0b10,
274 "Calculated" => code |= 1,
275 _ => return Err(ParseStatusCodeError::UnsupportedFlag(flag.to_owned())),
276 }
277 }
278
279 if limit_args > 1 {
280 return Err(ParseStatusCodeError::TooManyLimits);
281 }
282
283 if code & INFO_BITS_MASK != 0 {
284 code |= 1 << 10;
285 }
286
287 let code: StatusCode = code.into();
288 code.validate()?;
289
290 Ok(code)
291 }
292}
293
294impl Serialize for StatusCode {
295 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
296 where
297 S: serde::Serializer,
298 {
299 let mut s = serializer.serialize_struct("StatusCode", 1)?;
300 s.serialize_field("code", &self.bits())?;
301 s.end()
302 }
303}
304
305#[derive(Deserialize)]
306struct JsonStatusCode {
307 code: Option<i64>,
308 symbol: Option<String>,
309}
310
311impl<'de> Deserialize<'de> for StatusCode {
312 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
313 where
314 D: serde::Deserializer<'de>,
315 {
316 let v = JsonStatusCode::deserialize(deserializer)?;
317 if let Some(c) = v.code {
318 Ok(StatusCode::try_from(c).unwrap_or(StatusCode::Invalid))
319 } else if let Some(s) = v.symbol {
320 Ok(StatusCode::try_parse(&s).unwrap_or(StatusCode::Invalid))
321 } else {
322 Ok(StatusCode::Good)
323 }
324 }
325}
326
327#[derive(Debug, Error)]
328pub enum ParseStatusCodeError {
330 #[error("Invalid format, must be on the form 'Category, Flag, Flag, ...'")]
332 InvalidFormat,
333 #[error("Unknown sub status code {0}")]
335 UnknownSubStatusCode(String),
336 #[error("Unsupported flag {0}")]
338 UnsupportedFlag(String),
339 #[error("{0}")]
341 Invalid(#[from] StatusCodeValidationError),
342 #[error("Only one of Low, High, and Constant may be set")]
344 TooManyLimits,
345}
346
347impl Display for StatusCode {
348 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349 if self.0 == 0 {
351 return write!(f, "Good");
352 }
353
354 write!(f, "{}", self.sub_code())?;
355 if self.structure_changed() {
356 write!(f, ", StructureChanged")?;
357 }
358 if self.semantics_changed() {
359 write!(f, ", SemanticsChanged")?;
360 }
361 if self.limit() != StatusCodeLimit::None {
362 write!(f, ", {}", self.limit())?;
363 }
364 if self.overflow() {
365 write!(f, ", Overflow")?;
366 }
367 if self.multi_value() {
368 write!(f, ", MultiValue")?;
369 }
370 if self.extra_data() {
371 write!(f, ", ExtraData")?;
372 }
373 if self.partial() {
374 write!(f, ", Partial")?;
375 }
376 if self.value_type() != StatusCodeValueType::Raw {
377 write!(f, ", {}", self.value_type())?;
378 }
379 Ok(())
380 }
381}
382
383impl std::fmt::Debug for StatusCode {
384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
385 write!(f, "{} ({})", self, self.0)
386 }
387}
388
389impl From<u32> for StatusCode {
390 fn from(value: u32) -> Self {
391 StatusCode(value)
392 }
393}
394
395impl TryFrom<i64> for StatusCode {
396 type Error = <u32 as TryFrom<i64>>::Error;
397
398 fn try_from(value: i64) -> Result<Self, Self::Error> {
399 let uv: u32 = value.try_into()?;
400 Ok(uv.into())
401 }
402}
403
404impl Error for StatusCode {}
405
406#[derive(Debug, Clone, Error)]
407pub enum StatusCodeValidationError {
409 #[error("Severity is the reserved value 0b11")]
411 InvalidSeverity,
412 #[error("Used one of the reserved bits 5, 6, 11, 12, 13, 28, or 29")]
414 UsedReservedBit,
415 #[error("Sub code is not recognized")]
417 UnknownSubCode,
418 #[error("Info type is 0, but info bits are non-zero")]
420 InvalidInfoBits,
421 #[error("Historian value type is undefined (0b11)")]
423 UndefinedValueType,
424}
425
426macro_rules! value_enum_impl {
431 (#[doc = $edoc:literal] $type:ident, $(#[doc = $doc:literal] $code:ident = $val:literal),* $(,)?) => {
432 value_enum_impl!(#[doc = $edoc] $type, _enum $($doc $code = $val),*);
433 value_enum_impl!($type, _name $($code),*);
434 value_enum_impl!($type, _from_val $($code = $val),*);
435 value_enum_impl!($type, _description $($doc $code),*);
436 };
437
438 (#[doc = $edoc:literal] $type:ident, _enum $($comment:literal $code:ident = $val:literal),*) => {
439 #[allow(non_camel_case_types)]
440 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
441 #[doc = $edoc]
442 #[repr(u32)]
443 pub enum $type {
444 $(#[doc = $comment] $code = $val),*
445 }
446
447 impl std::fmt::Debug for $type {
448 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449 write!(f, "{} ({})", self.name(), (*self) as u32)
450 }
451 }
452 };
453
454 ($type:ident, _enum $($comment:literal $code:ident = $val:literal),*) => {
455 #[allow(non_camel_case_types)]
456 #[derive(Debug, Copy, Clone)]
457 pub enum $type {
458 $($(#[doc = $comment])? $code = $val),*
459 }
460 };
461
462 ($type:ident, _name $($code:ident),*) => {
463 impl $type {
464 pub fn name(&self) -> &'static str {
466 match self {
467 $(Self::$code => stringify!($code)),*
468 }
469 }
470 }
471
472 impl std::str::FromStr for $type {
473 type Err = ();
474
475 fn from_str(s: &str) -> Result<Self, Self::Err> {
476 match s {
477 $(stringify!($code) => Ok(Self::$code)),*,
478 _ => Err(())
479 }
480 }
481 }
482
483 impl std::fmt::Display for $type {
484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485 write!(f, "{}", self.name())
486 }
487 }
488 };
489
490 ($type:ident, _description $($comment:literal $code:ident),*) => {
491 impl $type {
492 pub fn description(&self) -> &'static str {
494 match self {
495 $(Self::$code => $comment),*
496 }
497 }
498 }
499 };
500
501 ($type:ident, _from_val $($code:ident = $val:literal),*) => {
502 impl $type {
503 pub fn from_value(val: u32) -> Option<Self> {
505 match val {
506 $($val => Some(Self::$code)),*,
507 _ => None
508 }
509 }
510 }
511 }
512}
513
514value_enum_impl!(
520 StatusCodeLimit,
522 Constant = 0b11,
524 High = 0b10,
526 Low = 0b01,
528 None = 0b00,
530);
531
532#[allow(clippy::derivable_impls)]
533impl Default for StatusCodeLimit {
534 fn default() -> Self {
535 Self::None
536 }
537}
538
539value_enum_impl!(
540 StatusCodeSeverity,
542 Good = 0b00,
544 Uncertain = 0b01,
546 Bad = 0b10,
548);
549
550#[allow(clippy::derivable_impls)]
551impl Default for StatusCodeSeverity {
552 fn default() -> Self {
553 Self::Good
554 }
555}
556
557value_enum_impl!(
558 StatusCodeValueType,
560 Raw = 0b00,
562 Calculated = 0b01,
564 Interpolated = 0b10,
566 Undefined = 0b11
568);
569
570value_enum_impl!(
571 StatusCodeInfoType,
573 NotUsed = 0,
575 DataValue = 1,
577);
578
579macro_rules! sub_code_impl {
584 ($($code:ident,$val:literal,$doc:literal)*) => {
585 value_enum_impl!(
586 SubStatusCode,
588 $(#[doc = $doc] $code = $val),*
589 );
590 sub_code_impl!(_code_consts $($doc $code = $val),*);
591 };
592
593 (_code_consts $($comment:literal $code:ident = $val:literal),*) => {
594 #[allow(non_upper_case_globals)]
595 impl StatusCode {
596 $(#[doc = $comment] pub const $code: StatusCode = StatusCode($val);)*
597 }
598 };
599}
600
601sub_code_impl! {
602 Good,0x00000000,"The operation succeeded."
603 Uncertain,0x40000000,"The operation was uncertain."
604 Bad,0x80000000,"The operation failed."
605 BadUnexpectedError,0x80010000,"An unexpected error occurred."
606 BadInternalError,0x80020000,"An internal error occurred as a result of a programming or configuration error."
607 BadOutOfMemory,0x80030000,"Not enough memory to complete the operation."
608 BadResourceUnavailable,0x80040000,"An operating system resource is not available."
609 BadCommunicationError,0x80050000,"A low level communication error occurred."
610 BadEncodingError,0x80060000,"Encoding halted because of invalid data in the objects being serialized."
611 BadDecodingError,0x80070000,"Decoding halted because of invalid data in the stream."
612 BadEncodingLimitsExceeded,0x80080000,"The message encoding/decoding limits imposed by the stack have been exceeded."
613 BadRequestTooLarge,0x80B80000,"The request message size exceeds limits set by the server."
614 BadResponseTooLarge,0x80B90000,"The response message size exceeds limits set by the client or server."
615 BadUnknownResponse,0x80090000,"An unrecognized response was received from the server."
616 BadTimeout,0x800A0000,"The operation timed out."
617 BadServiceUnsupported,0x800B0000,"The server does not support the requested service."
618 BadShutdown,0x800C0000,"The operation was cancelled because the application is shutting down."
619 BadServerNotConnected,0x800D0000,"The operation could not complete because the client is not connected to the server."
620 BadServerHalted,0x800E0000,"The server has stopped and cannot process any requests."
621 BadNothingToDo,0x800F0000,"No processing could be done because there was nothing to do."
622 BadTooManyOperations,0x80100000,"The request could not be processed because it specified too many operations."
623 BadTooManyMonitoredItems,0x80DB0000,"The request could not be processed because there are too many monitored items in the subscription."
624 BadDataTypeIdUnknown,0x80110000,"The extension object cannot be (de)serialized because the data type id is not recognized."
625 BadCertificateInvalid,0x80120000,"The certificate provided as a parameter is not valid."
626 BadSecurityChecksFailed,0x80130000,"An error occurred verifying security."
627 BadCertificatePolicyCheckFailed,0x81140000,"The certificate does not meet the requirements of the security policy."
628 BadCertificateTimeInvalid,0x80140000,"The certificate has expired or is not yet valid."
629 BadCertificateIssuerTimeInvalid,0x80150000,"An issuer certificate has expired or is not yet valid."
630 BadCertificateHostNameInvalid,0x80160000,"The HostName used to connect to a server does not match a HostName in the certificate."
631 BadCertificateUriInvalid,0x80170000,"The URI specified in the ApplicationDescription does not match the URI in the certificate."
632 BadCertificateUseNotAllowed,0x80180000,"The certificate may not be used for the requested operation."
633 BadCertificateIssuerUseNotAllowed,0x80190000,"The issuer certificate may not be used for the requested operation."
634 BadCertificateUntrusted,0x801A0000,"The certificate is not trusted."
635 BadCertificateRevocationUnknown,0x801B0000,"It was not possible to determine if the certificate has been revoked."
636 BadCertificateIssuerRevocationUnknown,0x801C0000,"It was not possible to determine if the issuer certificate has been revoked."
637 BadCertificateRevoked,0x801D0000,"The certificate has been revoked."
638 BadCertificateIssuerRevoked,0x801E0000,"The issuer certificate has been revoked."
639 BadCertificateChainIncomplete,0x810D0000,"The certificate chain is incomplete."
640 BadUserAccessDenied,0x801F0000,"User does not have permission to perform the requested operation."
641 BadIdentityTokenInvalid,0x80200000,"The user identity token is not valid."
642 BadIdentityTokenRejected,0x80210000,"The user identity token is valid but the server has rejected it."
643 BadSecureChannelIdInvalid,0x80220000,"The specified secure channel is no longer valid."
644 BadInvalidTimestamp,0x80230000,"The timestamp is outside the range allowed by the server."
645 BadNonceInvalid,0x80240000,"The nonce does appear to be not a random value or it is not the correct length."
646 BadSessionIdInvalid,0x80250000,"The session id is not valid."
647 BadSessionClosed,0x80260000,"The session was closed by the client."
648 BadSessionNotActivated,0x80270000,"The session cannot be used because ActivateSession has not been called."
649 BadSubscriptionIdInvalid,0x80280000,"The subscription id is not valid."
650 BadRequestHeaderInvalid,0x802A0000,"The header for the request is missing or invalid."
651 BadTimestampsToReturnInvalid,0x802B0000,"The timestamps to return parameter is invalid."
652 BadRequestCancelledByClient,0x802C0000,"The request was cancelled by the client."
653 BadTooManyArguments,0x80E50000,"Too many arguments were provided."
654 BadLicenseExpired,0x810E0000,"The server requires a license to operate in general or to perform a service or operation, but existing license is expired."
655 BadLicenseLimitsExceeded,0x810F0000,"The server has limits on number of allowed operations / objects, based on installed licenses, and these limits where exceeded."
656 BadLicenseNotAvailable,0x81100000,"The server does not have a license which is required to operate in general or to perform a service or operation."
657 BadServerTooBusy,0x80EE0000,"The Server does not have the resources to process the request at this time."
658 GoodPasswordChangeRequired,0x00EF0000,"The log-on for the user succeeded but the user is required to change the password."
659 GoodSubscriptionTransferred,0x002D0000,"The subscription was transferred to another session."
660 GoodCompletesAsynchronously,0x002E0000,"The processing will complete asynchronously."
661 GoodOverload,0x002F0000,"Sampling has slowed down due to resource limitations."
662 GoodClamped,0x00300000,"The value written was accepted but was clamped."
663 BadNoCommunication,0x80310000,"Communication with the data source is defined, but not established, and there is no last known value available."
664 BadWaitingForInitialData,0x80320000,"Waiting for the server to obtain values from the underlying data source."
665 BadNodeIdInvalid,0x80330000,"The syntax the node id is not valid or refers to a node that is not valid for the operation."
666 BadNodeIdUnknown,0x80340000,"The node id refers to a node that does not exist in the server address space."
667 BadAttributeIdInvalid,0x80350000,"The attribute is not supported for the specified Node."
668 BadIndexRangeInvalid,0x80360000,"The syntax of the index range parameter is invalid."
669 BadIndexRangeNoData,0x80370000,"No data exists within the range of indexes specified."
670 BadIndexRangeDataMismatch,0x80EA0000,"The written data does not match the IndexRange specified."
671 BadDataEncodingInvalid,0x80380000,"The data encoding is invalid."
672 BadDataEncodingUnsupported,0x80390000,"The server does not support the requested data encoding for the node."
673 BadNotReadable,0x803A0000,"The access level does not allow reading or subscribing to the Node."
674 BadNotWritable,0x803B0000,"The access level does not allow writing to the Node."
675 BadOutOfRange,0x803C0000,"The value was out of range."
676 BadNotSupported,0x803D0000,"The requested operation is not supported."
677 BadNotFound,0x803E0000,"A requested item was not found or a search operation ended without success."
678 BadObjectDeleted,0x803F0000,"The object cannot be used because it has been deleted."
679 BadNotImplemented,0x80400000,"Requested operation is not implemented."
680 BadMonitoringModeInvalid,0x80410000,"The monitoring mode is invalid."
681 BadMonitoredItemIdInvalid,0x80420000,"The monitoring item id does not refer to a valid monitored item."
682 BadMonitoredItemFilterInvalid,0x80430000,"The monitored item filter parameter is not valid."
683 BadMonitoredItemFilterUnsupported,0x80440000,"The server does not support the requested monitored item filter."
684 BadFilterNotAllowed,0x80450000,"A monitoring filter cannot be used in combination with the attribute specified."
685 BadStructureMissing,0x80460000,"A mandatory structured parameter was missing or null."
686 BadEventFilterInvalid,0x80470000,"The event filter is not valid."
687 BadContentFilterInvalid,0x80480000,"The content filter is not valid."
688 BadFilterOperatorInvalid,0x80C10000,"An unrecognized operator was provided in a filter."
689 BadFilterOperatorUnsupported,0x80C20000,"A valid operator was provided, but the server does not provide support for this filter operator."
690 BadFilterOperandCountMismatch,0x80C30000,"The number of operands provided for the filter operator was less then expected for the operand provided."
691 BadFilterOperandInvalid,0x80490000,"The operand used in a content filter is not valid."
692 BadFilterElementInvalid,0x80C40000,"The referenced element is not a valid element in the content filter."
693 BadFilterLiteralInvalid,0x80C50000,"The referenced literal is not a valid value."
694 BadContinuationPointInvalid,0x804A0000,"The continuation point provide is longer valid."
695 BadNoContinuationPoints,0x804B0000,"The operation could not be processed because all continuation points have been allocated."
696 BadReferenceTypeIdInvalid,0x804C0000,"The reference type id does not refer to a valid reference type node."
697 BadBrowseDirectionInvalid,0x804D0000,"The browse direction is not valid."
698 BadNodeNotInView,0x804E0000,"The node is not part of the view."
699 BadNumericOverflow,0x81120000,"The number was not accepted because of a numeric overflow."
700 BadLocaleNotSupported,0x80ED0000,"The locale in the requested write operation is not supported."
701 BadNoValue,0x80F00000,"The variable has no default value and no initial value."
702 BadServerUriInvalid,0x804F0000,"The ServerUri is not a valid URI."
703 BadServerNameMissing,0x80500000,"No ServerName was specified."
704 BadDiscoveryUrlMissing,0x80510000,"No DiscoveryUrl was specified."
705 BadSempahoreFileMissing,0x80520000,"The semaphore file specified by the client is not valid."
706 BadRequestTypeInvalid,0x80530000,"The security token request type is not valid."
707 BadSecurityModeRejected,0x80540000,"The security mode does not meet the requirements set by the server."
708 BadSecurityPolicyRejected,0x80550000,"The security policy does not meet the requirements set by the server."
709 BadTooManySessions,0x80560000,"The server has reached its maximum number of sessions."
710 BadUserSignatureInvalid,0x80570000,"The user token signature is missing or invalid."
711 BadApplicationSignatureInvalid,0x80580000,"The signature generated with the client certificate is missing or invalid."
712 BadNoValidCertificates,0x80590000,"The client did not provide at least one software certificate that is valid and meets the profile requirements for the server."
713 BadIdentityChangeNotSupported,0x80C60000,"The server does not support changing the user identity assigned to the session."
714 BadRequestCancelledByRequest,0x805A0000,"The request was cancelled by the client with the Cancel service."
715 BadParentNodeIdInvalid,0x805B0000,"The parent node id does not to refer to a valid node."
716 BadReferenceNotAllowed,0x805C0000,"The reference could not be created because it violates constraints imposed by the data model."
717 BadNodeIdRejected,0x805D0000,"The requested node id was reject because it was either invalid or server does not allow node ids to be specified by the client."
718 BadNodeIdExists,0x805E0000,"The requested node id is already used by another node."
719 BadNodeClassInvalid,0x805F0000,"The node class is not valid."
720 BadBrowseNameInvalid,0x80600000,"The browse name is invalid."
721 BadBrowseNameDuplicated,0x80610000,"The browse name is not unique among nodes that share the same relationship with the parent."
722 BadNodeAttributesInvalid,0x80620000,"The node attributes are not valid for the node class."
723 BadTypeDefinitionInvalid,0x80630000,"The type definition node id does not reference an appropriate type node."
724 BadSourceNodeIdInvalid,0x80640000,"The source node id does not reference a valid node."
725 BadTargetNodeIdInvalid,0x80650000,"The target node id does not reference a valid node."
726 BadDuplicateReferenceNotAllowed,0x80660000,"The reference type between the nodes is already defined."
727 BadInvalidSelfReference,0x80670000,"The server does not allow this type of self reference on this node."
728 BadReferenceLocalOnly,0x80680000,"The reference type is not valid for a reference to a remote server."
729 BadNoDeleteRights,0x80690000,"The server will not allow the node to be deleted."
730 UncertainReferenceNotDeleted,0x40BC0000,"The server was not able to delete all target references."
731 BadServerIndexInvalid,0x806A0000,"The server index is not valid."
732 BadViewIdUnknown,0x806B0000,"The view id does not refer to a valid view node."
733 BadViewTimestampInvalid,0x80C90000,"The view timestamp is not available or not supported."
734 BadViewParameterMismatch,0x80CA0000,"The view parameters are not consistent with each other."
735 BadViewVersionInvalid,0x80CB0000,"The view version is not available or not supported."
736 UncertainNotAllNodesAvailable,0x40C00000,"The list of references may not be complete because the underlying system is not available."
737 GoodResultsMayBeIncomplete,0x00BA0000,"The server should have followed a reference to a node in a remote server but did not. The result set may be incomplete."
738 BadNotTypeDefinition,0x80C80000,"The provided Nodeid was not a type definition nodeid."
739 UncertainReferenceOutOfServer,0x406C0000,"One of the references to follow in the relative path references to a node in the address space in another server."
740 BadTooManyMatches,0x806D0000,"The requested operation has too many matches to return."
741 BadQueryTooComplex,0x806E0000,"The requested operation requires too many resources in the server."
742 BadNoMatch,0x806F0000,"The requested operation has no match to return."
743 BadMaxAgeInvalid,0x80700000,"The max age parameter is invalid."
744 BadSecurityModeInsufficient,0x80E60000,"The operation is not permitted over the current secure channel."
745 BadHistoryOperationInvalid,0x80710000,"The history details parameter is not valid."
746 BadHistoryOperationUnsupported,0x80720000,"The server does not support the requested operation."
747 BadInvalidTimestampArgument,0x80BD0000,"The defined timestamp to return was invalid."
748 BadWriteNotSupported,0x80730000,"The server does not support writing the combination of value, status and timestamps provided."
749 BadTypeMismatch,0x80740000,"The value supplied for the attribute is not of the same type as the attribute's value."
750 BadMethodInvalid,0x80750000,"The method id does not refer to a method for the specified object."
751 BadArgumentsMissing,0x80760000,"The client did not specify all of the input arguments for the method."
752 BadNotExecutable,0x81110000,"The executable attribute does not allow the execution of the method."
753 BadTooManySubscriptions,0x80770000,"The server has reached its maximum number of subscriptions."
754 BadTooManyPublishRequests,0x80780000,"The server has reached the maximum number of queued publish requests."
755 BadNoSubscription,0x80790000,"There is no subscription available for this session."
756 BadSequenceNumberUnknown,0x807A0000,"The sequence number is unknown to the server."
757 GoodRetransmissionQueueNotSupported,0x00DF0000,"The Server does not support retransmission queue and acknowledgement of sequence numbers is not available."
758 BadMessageNotAvailable,0x807B0000,"The requested notification message is no longer available."
759 BadInsufficientClientProfile,0x807C0000,"The client of the current session does not support one or more Profiles that are necessary for the subscription."
760 BadStateNotActive,0x80BF0000,"The sub-state machine is not currently active."
761 BadAlreadyExists,0x81150000,"An equivalent rule already exists."
762 BadTcpServerTooBusy,0x807D0000,"The server cannot process the request because it is too busy."
763 BadTcpMessageTypeInvalid,0x807E0000,"The type of the message specified in the header invalid."
764 BadTcpSecureChannelUnknown,0x807F0000,"The SecureChannelId and/or TokenId are not currently in use."
765 BadTcpMessageTooLarge,0x80800000,"The size of the message chunk specified in the header is too large."
766 BadTcpNotEnoughResources,0x80810000,"There are not enough resources to process the request."
767 BadTcpInternalError,0x80820000,"An internal error occurred."
768 BadTcpEndpointUrlInvalid,0x80830000,"The server does not recognize the QueryString specified."
769 BadRequestInterrupted,0x80840000,"The request could not be sent because of a network interruption."
770 BadRequestTimeout,0x80850000,"Timeout occurred while processing the request."
771 BadSecureChannelClosed,0x80860000,"The secure channel has been closed."
772 BadSecureChannelTokenUnknown,0x80870000,"The token has expired or is not recognized."
773 BadSequenceNumberInvalid,0x80880000,"The sequence number is not valid."
774 BadProtocolVersionUnsupported,0x80BE0000,"The applications do not have compatible protocol versions."
775 BadConfigurationError,0x80890000,"There is a problem with the configuration that affects the usefulness of the value."
776 BadNotConnected,0x808A0000,"The variable should receive its value from another variable, but has never been configured to do so."
777 BadDeviceFailure,0x808B0000,"There has been a failure in the device/data source that generates the value that has affected the value."
778 BadSensorFailure,0x808C0000,"There has been a failure in the sensor from which the value is derived by the device/data source."
779 BadOutOfService,0x808D0000,"The source of the data is not operational."
780 BadDeadbandFilterInvalid,0x808E0000,"The deadband filter is not valid."
781 UncertainNoCommunicationLastUsableValue,0x408F0000,"Communication to the data source has failed. The variable value is the last value that had a good quality."
782 UncertainLastUsableValue,0x40900000,"Whatever was updating this value has stopped doing so."
783 UncertainSubstituteValue,0x40910000,"The value is an operational value that was manually overwritten."
784 UncertainInitialValue,0x40920000,"The value is an initial value for a variable that normally receives its value from another variable."
785 UncertainSensorNotAccurate,0x40930000,"The value is at one of the sensor limits."
786 UncertainEngineeringUnitsExceeded,0x40940000,"The value is outside of the range of values defined for this parameter."
787 UncertainSubNormal,0x40950000,"The data value is derived from multiple sources and has less than the required number of Good sources."
788 GoodLocalOverride,0x00960000,"The value has been overridden."
789 GoodSubNormal,0x00EB0000,"The value is derived from multiple sources and has the required number of Good sources, but less than the full number of Good sources."
790 BadRefreshInProgress,0x80970000,"This Condition refresh failed, a Condition refresh operation is already in progress."
791 BadConditionAlreadyDisabled,0x80980000,"This condition has already been disabled."
792 BadConditionAlreadyEnabled,0x80CC0000,"This condition has already been enabled."
793 BadConditionDisabled,0x80990000,"Property not available, this condition is disabled."
794 BadEventIdUnknown,0x809A0000,"The specified event id is not recognized."
795 BadEventNotAcknowledgeable,0x80BB0000,"The event cannot be acknowledged."
796 BadDialogNotActive,0x80CD0000,"The dialog condition is not active."
797 BadDialogResponseInvalid,0x80CE0000,"The response is not valid for the dialog."
798 BadConditionBranchAlreadyAcked,0x80CF0000,"The condition branch has already been acknowledged."
799 BadConditionBranchAlreadyConfirmed,0x80D00000,"The condition branch has already been confirmed."
800 BadConditionAlreadyShelved,0x80D10000,"The condition has already been shelved."
801 BadConditionNotShelved,0x80D20000,"The condition is not currently shelved."
802 BadShelvingTimeOutOfRange,0x80D30000,"The shelving time not within an acceptable range."
803 BadNoData,0x809B0000,"No data exists for the requested time range or event filter."
804 BadBoundNotFound,0x80D70000,"No data found to provide upper or lower bound value."
805 BadBoundNotSupported,0x80D80000,"The server cannot retrieve a bound for the variable."
806 BadDataLost,0x809D0000,"Data is missing due to collection started/stopped/lost."
807 BadDataUnavailable,0x809E0000,"Expected data is unavailable for the requested time range due to an un-mounted volume, an off-line archive or tape, or similar reason for temporary unavailability."
808 BadEntryExists,0x809F0000,"The data or event was not successfully inserted because a matching entry exists."
809 BadNoEntryExists,0x80A00000,"The data or event was not successfully updated because no matching entry exists."
810 BadTimestampNotSupported,0x80A10000,"The Client requested history using a TimestampsToReturn the Server does not support."
811 GoodEntryInserted,0x00A20000,"The data or event was successfully inserted into the historical database."
812 GoodEntryReplaced,0x00A30000,"The data or event field was successfully replaced in the historical database."
813 UncertainDataSubNormal,0x40A40000,"The aggregate value is derived from multiple values and has less than the required number of Good values."
814 GoodNoData,0x00A50000,"No data exists for the requested time range or event filter."
815 GoodMoreData,0x00A60000,"More data is available in the time range beyond the number of values requested."
816 BadAggregateListMismatch,0x80D40000,"The requested number of Aggregates does not match the requested number of NodeIds."
817 BadAggregateNotSupported,0x80D50000,"The requested Aggregate is not support by the server."
818 BadAggregateInvalidInputs,0x80D60000,"The aggregate value could not be derived due to invalid data inputs."
819 BadAggregateConfigurationRejected,0x80DA0000,"The aggregate configuration is not valid for specified node."
820 GoodDataIgnored,0x00D90000,"The request specifies fields which are not valid for the EventType or cannot be saved by the historian."
821 BadRequestNotAllowed,0x80E40000,"The request was rejected by the server because it did not meet the criteria set by the server."
822 BadRequestNotComplete,0x81130000,"The request has not been processed by the server yet."
823 BadTransactionPending,0x80E80000,"The operation is not allowed because a transaction is in progress."
824 BadTicketRequired,0x811F0000,"The device identity needs a ticket before it can be accepted."
825 BadTicketInvalid,0x81200000,"The device identity needs a ticket before it can be accepted."
826 BadLocked,0x80E90000,"The requested operation is not allowed, because the Node is locked by a different application."
827 BadRequiresLock,0x80EC0000,"The requested operation is not allowed, because the Node is not locked by the application."
828 GoodEdited,0x00DC0000,"The value does not come from the real source and has been edited by the server."
829 GoodPostActionFailed,0x00DD0000,"There was an error in execution of these post-actions."
830 UncertainDominantValueChanged,0x40DE0000,"The related EngineeringUnit has been changed but the Variable Value is still provided based on the previous unit."
831 GoodDependentValueChanged,0x00E00000,"A dependent value has been changed but the change has not been applied to the device."
832 BadDominantValueChanged,0x80E10000,"The related EngineeringUnit has been changed but this change has not been applied to the device. The Variable Value is still dependent on the previous unit but its status is currently Bad."
833 UncertainDependentValueChanged,0x40E20000,"A dependent value has been changed but the change has not been applied to the device. The quality of the dominant variable is uncertain."
834 BadDependentValueChanged,0x80E30000,"A dependent value has been changed but the change has not been applied to the device. The quality of the dominant variable is Bad."
835 GoodEdited_DependentValueChanged,0x01160000,"It is delivered with a dominant Variable value when a dependent Variable has changed but the change has not been applied."
836 GoodEdited_DominantValueChanged,0x01170000,"It is delivered with a dependent Variable value when a dominant Variable has changed but the change has not been applied."
837 GoodEdited_DominantValueChanged_DependentValueChanged,0x01180000,"It is delivered with a dependent Variable value when a dominant or dependent Variable has changed but change has not been applied."
838 BadEdited_OutOfRange,0x81190000,"It is delivered with a Variable value when Variable has changed but the value is not legal."
839 BadInitialValue_OutOfRange,0x811A0000,"It is delivered with a Variable value when a source Variable has changed but the value is not legal."
840 BadOutOfRange_DominantValueChanged,0x811B0000,"It is delivered with a dependent Variable value when a dominant Variable has changed and the value is not legal."
841 BadEdited_OutOfRange_DominantValueChanged,0x811C0000,"It is delivered with a dependent Variable value when a dominant Variable has changed, the value is not legal and the change has not been applied."
842 BadOutOfRange_DominantValueChanged_DependentValueChanged,0x811D0000,"It is delivered with a dependent Variable value when a dominant or dependent Variable has changed and the value is not legal."
843 BadEdited_OutOfRange_DominantValueChanged_DependentValueChanged,0x811E0000,"It is delivered with a dependent Variable value when a dominant or dependent Variable has changed, the value is not legal and the change has not been applied."
844 GoodCommunicationEvent,0x00A70000,"The communication layer has raised an event."
845 GoodShutdownEvent,0x00A80000,"The system is shutting down."
846 GoodCallAgain,0x00A90000,"The operation is not finished and needs to be called again."
847 GoodNonCriticalTimeout,0x00AA0000,"A non-critical timeout occurred."
848 BadInvalidArgument,0x80AB0000,"One or more arguments are invalid."
849 BadConnectionRejected,0x80AC0000,"Could not establish a network connection to remote server."
850 BadDisconnect,0x80AD0000,"The server has disconnected from the client."
851 BadConnectionClosed,0x80AE0000,"The network connection has been closed."
852 BadInvalidState,0x80AF0000,"The operation cannot be completed because the object is closed, uninitialized or in some other invalid state."
853 BadEndOfStream,0x80B00000,"Cannot move beyond end of the stream."
854 BadNoDataAvailable,0x80B10000,"No data is currently available for reading from a non-blocking stream."
855 BadWaitingForResponse,0x80B20000,"The asynchronous operation is waiting for a response."
856 BadOperationAbandoned,0x80B30000,"The asynchronous operation was abandoned by the caller."
857 BadExpectedStreamToBlock,0x80B40000,"The stream did not return all data requested (possibly because it is a non-blocking stream)."
858 BadWouldBlock,0x80B50000,"Non blocking behaviour is required and the operation would block."
859 BadSyntaxError,0x80B60000,"A value had an invalid syntax."
860 BadMaxConnectionsReached,0x80B70000,"The operation could not be finished because all available connections are in use."
861 UncertainTransducerInManual,0x42080000,"The value may not be accurate because the transducer is in manual mode."
862 UncertainSimulatedValue,0x42090000,"The value is simulated."
863 UncertainSensorCalibration,0x420A0000,"The value may not be accurate due to a sensor calibration fault."
864 UncertainConfigurationError,0x420F0000,"The value may not be accurate due to a configuration issue."
865 GoodCascadeInitializationAcknowledged,0x04010000,"The value source supports cascade handshaking and the value has been Initialized based on an initialization request from a cascade secondary."
866 GoodCascadeInitializationRequest,0x04020000,"The value source supports cascade handshaking and is requesting initialization of a cascade primary."
867 GoodCascadeNotInvited,0x04030000,"The value source supports cascade handshaking, however, the source’s current state does not allow for cascade."
868 GoodCascadeNotSelected,0x04040000,"The value source supports cascade handshaking, however, the source has not selected the corresponding cascade primary for use."
869 GoodFaultStateActive,0x04070000,"There is a fault state condition active in the value source."
870 GoodInitiateFaultState,0x04080000,"A fault state condition is being requested of the destination."
871 GoodCascade,0x04090000,"The value is accurate, and the signal source supports cascade handshaking."
872 BadDataSetIdInvalid,0x80E70000,"The DataSet specified for the DataSetWriter creation is invalid."
873
874 Invalid,0xFFFFFFFF,"Invalid status code"
875}
876#[cfg(test)]
879mod tests {
880 use crate::time_series::ParseStatusCodeError;
881
882 use super::{
883 StatusCode, StatusCodeInfoType, StatusCodeLimit, StatusCodeSeverity,
884 StatusCodeValidationError, StatusCodeValueType, SubStatusCode,
885 };
886
887 #[test]
888 fn test_from_sub_code() {
889 assert_eq!("Good", StatusCode::Good.to_string());
890 assert_eq!(
891 "BadBrowseDirectionInvalid",
892 StatusCode::BadBrowseDirectionInvalid.to_string()
893 );
894 assert_eq!(
895 "UncertainDependentValueChanged",
896 StatusCode::UncertainDependentValueChanged.to_string()
897 );
898 }
899
900 #[test]
901 fn test_modify() {
902 let code = StatusCode::from(0);
903 assert_eq!(code, StatusCode::Good);
904 let code = code.set_severity(StatusCodeSeverity::Uncertain);
905 assert_eq!(code.severity(), StatusCodeSeverity::Uncertain);
906 let code = code.set_severity(StatusCodeSeverity::Bad);
907 assert_eq!(code.severity(), StatusCodeSeverity::Bad);
908
909 code.validate().unwrap();
910
911 assert!(!code.structure_changed());
912 let code = code.set_structure_changed(true);
913 code.validate().unwrap();
914 assert!(code.structure_changed());
915 let code = code.set_structure_changed(false);
916 assert!(!code.structure_changed());
917 let code = code.set_structure_changed(true);
918
919 assert!(!code.semantics_changed());
920 let code = code.set_semantics_changed(true);
921 code.validate().unwrap();
922 assert!(code.semantics_changed());
923 let code = code.set_semantics_changed(false);
924 assert!(!code.semantics_changed());
925 let code = code.set_semantics_changed(true);
926
927 assert_eq!(code.sub_code(), SubStatusCode::Bad);
928 let code = code.set_sub_code(SubStatusCode::BadAggregateConfigurationRejected);
929 assert_eq!(
930 code.sub_code(),
931 SubStatusCode::BadAggregateConfigurationRejected
932 );
933 let code = code.set_sub_code(SubStatusCode::UncertainNotAllNodesAvailable);
934 assert_eq!(
935 code.sub_code(),
936 SubStatusCode::UncertainNotAllNodesAvailable
937 );
938
939 assert_eq!(code.info_type(), StatusCodeInfoType::NotUsed);
940 let code = code.set_info_type(StatusCodeInfoType::DataValue);
941 assert_eq!(code.info_type(), StatusCodeInfoType::DataValue);
942 code.validate().unwrap();
943 let code = code.set_info_type(StatusCodeInfoType::NotUsed);
944 assert_eq!(code.info_type(), StatusCodeInfoType::NotUsed);
945
946 assert_eq!(code.limit(), StatusCodeLimit::None);
947 let code = code.set_limit(StatusCodeLimit::High);
948 assert_eq!(code.limit(), StatusCodeLimit::High);
949 let code = code.set_limit(StatusCodeLimit::Constant);
950 assert_eq!(code.limit(), StatusCodeLimit::Constant);
951
952 assert!(matches!(
953 code.validate(),
954 Err(StatusCodeValidationError::InvalidInfoBits)
955 ));
956 let code = code.set_info_type(StatusCodeInfoType::DataValue);
957 code.validate().unwrap();
958
959 assert!(!code.overflow());
960 let code = code.set_overflow(true);
961 code.validate().unwrap();
962 assert!(code.overflow());
963 let code = code.set_overflow(false);
964 assert!(!code.overflow());
965 let code = code.set_overflow(true);
966
967 assert!(!code.multi_value());
968 let code = code.set_multi_value(true);
969 code.validate().unwrap();
970 assert!(code.multi_value());
971 let code = code.set_multi_value(false);
972 assert!(!code.multi_value());
973 let code = code.set_multi_value(true);
974
975 assert!(!code.extra_data());
976 let code = code.set_extra_data(true);
977 code.validate().unwrap();
978 assert!(code.extra_data());
979 let code = code.set_extra_data(false);
980 assert!(!code.extra_data());
981 let code = code.set_extra_data(true);
982
983 assert!(!code.partial());
984 let code = code.set_partial(true);
985 code.validate().unwrap();
986 assert!(code.partial());
987 let code = code.set_partial(false);
988 assert!(!code.partial());
989 let code = code.set_partial(true);
990
991 assert_eq!(code.value_type(), StatusCodeValueType::Raw);
992 let code = code.set_value_type(StatusCodeValueType::Calculated);
993 assert_eq!(code.value_type(), StatusCodeValueType::Calculated);
994 let code = code.set_value_type(StatusCodeValueType::Interpolated);
995 assert_eq!(code.value_type(), StatusCodeValueType::Interpolated);
996
997 assert_eq!(StatusCodeSeverity::Uncertain, code.severity());
998 assert!(code.structure_changed());
999 assert!(code.semantics_changed());
1000 assert_eq!(code.info_type(), StatusCodeInfoType::DataValue);
1001 assert_eq!(
1002 code.sub_code(),
1003 SubStatusCode::UncertainNotAllNodesAvailable
1004 );
1005 assert!(code.overflow());
1006 assert!(code.multi_value());
1007 assert!(code.extra_data());
1008 assert!(code.partial());
1009 assert_eq!(code.value_type(), StatusCodeValueType::Interpolated);
1010
1011 code.validate().unwrap();
1012 }
1013
1014 #[test]
1015 fn test_parse() {
1016 assert_eq!(
1017 "Good, StructureChanged, Calculated",
1018 StatusCode::try_parse("Good, StructureChanged, Calculated")
1019 .unwrap()
1020 .to_string()
1021 );
1022 assert_eq!(
1023 "UncertainSensorCalibration, Overflow, ExtraData",
1024 StatusCode::try_parse("Uncertain_SensorCalibration, Overflow, ExtraData")
1025 .unwrap()
1026 .to_string()
1027 );
1028 assert_eq!(
1029 "Good, Low, Calculated",
1030 StatusCode::try_parse("Good, Low, Calculated")
1031 .unwrap()
1032 .to_string()
1033 );
1034
1035 assert!(matches!(
1036 StatusCode::try_parse("Bad_SomethingWrong").unwrap_err(),
1037 ParseStatusCodeError::UnknownSubStatusCode(_)
1038 ));
1039
1040 assert!(matches!(
1041 StatusCode::try_parse("Bad, Low, High").unwrap_err(),
1042 ParseStatusCodeError::TooManyLimits
1043 ));
1044
1045 assert!(matches!(
1046 StatusCode::try_parse("Good, Calculated, Interpolated").unwrap_err(),
1047 ParseStatusCodeError::Invalid(StatusCodeValidationError::UndefinedValueType)
1048 ));
1049
1050 assert!(matches!(
1051 StatusCode::try_parse("Good, Something, Foo").unwrap_err(),
1052 ParseStatusCodeError::UnsupportedFlag(_)
1053 ));
1054 }
1055}