Skip to content

Commit 0ec9dd1

Browse files
committed
v14: Make state field optional and disallow null for state.open
In v0.13 state and but state.open must be present, but state.open may be null. This doesn't provide any benefit, so v14 removed these restrictions. We can't provide full backwards compatiblity, since state.open == null is not supported in v14. To still have a json which is compatible with both v0.13 and v14 state.open must be set to either true or false.
1 parent 6875e36 commit 0ec9dd1

File tree

2 files changed

+68
-6
lines changed

2 files changed

+68
-6
lines changed

src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
//! You can create a new `Status` instance by using the `StatusBuilder`.
2424
//!
2525
//! use serde_json;
26-
//! use spaceapi::{Status, StatusBuilder, Location, Contact, IssueReportChannel};
26+
//! use spaceapi::{State, Status, StatusBuilder, Location, Contact, IssueReportChannel};
2727
//!
2828
//! # fn main() {
2929
//! let status = StatusBuilder::new("coredump")
3030
//! .logo("https://www.coredump.ch/logo.png")
3131
//! .url("https://www.coredump.ch/")
32+
//! .state(State{
33+
//! open: Some(false),
34+
//! ..State::default()
35+
//! })
3236
//! .location(
3337
//! Location {
3438
//! address: None,

src/status.rs

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub struct Icon {
4646

4747
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
4848
pub struct State {
49+
#[serde(skip_serializing_if = "Option::is_none")]
4950
pub open: Option<bool>,
5051
#[serde(skip_serializing_if = "Option::is_none")]
5152
pub lastchange: Option<u64>,
@@ -57,6 +58,15 @@ pub struct State {
5758
pub icon: Option<Icon>,
5859
}
5960

61+
impl State {
62+
fn verify(&self, version: StatusBuilderVersion) -> Result<(), String> {
63+
if version != StatusBuilderVersion::V14 && self.open.is_none() {
64+
return Err("state.open must be present".into());
65+
}
66+
Ok(())
67+
}
68+
}
69+
6070
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
6171
pub struct Event {
6272
pub name: String,
@@ -229,7 +239,8 @@ pub struct Status {
229239
pub issue_report_channels: Vec<IssueReportChannel>,
230240

231241
// Mutable data
232-
pub state: State,
242+
#[serde(skip_serializing_if = "Option::is_none")]
243+
pub state: Option<State>,
233244
#[serde(skip_serializing_if = "Option::is_none")]
234245
pub sensors: Option<Sensors>,
235246

@@ -295,6 +306,7 @@ pub struct StatusBuilder {
295306
radio_show: Option<Vec<RadioShow>>,
296307
issue_report_channels: Vec<IssueReportChannel>,
297308
extensions: Extensions,
309+
state: Option<State>,
298310
}
299311

300312
impl StatusBuilder {
@@ -329,6 +341,11 @@ impl StatusBuilder {
329341
}
330342
}
331343

344+
pub fn state(mut self, state: State) -> Self {
345+
self.state = Some(state);
346+
self
347+
}
348+
332349
pub fn logo<S: Into<String>>(mut self, logo: S) -> Self {
333350
self.logo = Some(logo.into());
334351
self
@@ -410,6 +427,9 @@ impl StatusBuilder {
410427
if let Some(spacefed) = &self.spacefed {
411428
spacefed.verify(self.version)?;
412429
}
430+
if let Some(state) = &self.state {
431+
state.verify(self.version)?;
432+
}
413433

414434
if self.version == StatusBuilderVersion::V14 {
415435
if contact.jabber.is_some() {
@@ -425,8 +445,13 @@ impl StatusBuilder {
425445
if !self.issue_report_channels.is_empty() {
426446
return Err("issue_report_channels key was removed".into());
427447
}
428-
} else if self.issue_report_channels.is_empty() {
429-
return Err("issue_report_channels must not be empty".into());
448+
} else {
449+
if self.issue_report_channels.is_empty() {
450+
return Err("issue_report_channels must not be empty".into());
451+
}
452+
if self.state.is_none() {
453+
return Err("state must be present in v0.13".into());
454+
}
430455
}
431456

432457
Ok(Status {
@@ -444,6 +469,7 @@ impl StatusBuilder {
444469
events: self.events,
445470
radio_show: self.radio_show,
446471
issue_report_channels: self.issue_report_channels,
472+
state: self.state,
447473
extensions: self.extensions,
448474
..Default::default()
449475
})
@@ -567,6 +593,10 @@ mod test {
567593
let status = StatusBuilder::mixed("foo")
568594
.logo("bar")
569595
.url("foobar")
596+
.state(State {
597+
open: Some(false),
598+
..State::default()
599+
})
570600
.location(Location::default())
571601
.contact(Contact::default())
572602
.add_issue_report_channel(IssueReportChannel::Email)
@@ -580,6 +610,10 @@ mod test {
580610
space: "foo".into(),
581611
logo: "bar".into(),
582612
url: "foobar".into(),
613+
state: Some(State {
614+
open: Some(false),
615+
..State::default()
616+
}),
583617
issue_report_channels: vec![IssueReportChannel::Email],
584618
..Status::default()
585619
}
@@ -591,6 +625,10 @@ mod test {
591625
let status = StatusBuilder::new("foo")
592626
.logo("bar")
593627
.url("foobar")
628+
.state(State {
629+
open: Some(false),
630+
..State::default()
631+
})
594632
.location(Location::default())
595633
.contact(Contact::default())
596634
.spacefed(Spacefed {
@@ -650,6 +688,10 @@ mod test {
650688
let status = StatusBuilder::new("foo")
651689
.logo("bar")
652690
.url("foobar")
691+
.state(State {
692+
open: Some(false),
693+
..State::default()
694+
})
653695
.location(Location::default())
654696
.contact(Contact::default())
655697
.add_extension("aaa", Value::Array(vec![Value::Null, Value::from(42)]))
@@ -666,6 +708,10 @@ mod test {
666708
let status = StatusBuilder::new("a")
667709
.logo("b")
668710
.url("c")
711+
.state(State {
712+
open: Some(false),
713+
..State::default()
714+
})
669715
.location(Location::default())
670716
.contact(Contact::default())
671717
.add_issue_report_channel(IssueReportChannel::Email)
@@ -675,7 +721,7 @@ mod test {
675721
&to_string(&status.unwrap()).unwrap(),
676722
"{\"api\":\"0.13\",\"space\":\"a\",\"logo\":\"b\",\"url\":\"c\",\
677723
\"location\":{\"lat\":0.0,\"lon\":0.0},\"contact\":{},\"issue_report_channels\":[\"email\"],\
678-
\"state\":{\"open\":null}}"
724+
\"state\":{\"open\":false}}"
679725
);
680726
}
681727

@@ -684,6 +730,10 @@ mod test {
684730
let status = StatusBuilder::new("a")
685731
.logo("b")
686732
.url("c")
733+
.state(State {
734+
open: Some(false),
735+
..State::default()
736+
})
687737
.location(Location::default())
688738
.contact(Contact::default())
689739
.add_issue_report_channel(IssueReportChannel::Email)
@@ -695,7 +745,7 @@ mod test {
695745
&to_string(&status.unwrap()).unwrap(),
696746
"{\"api\":\"0.13\",\"space\":\"a\",\"logo\":\"b\",\"url\":\"c\",\
697747
\"location\":{\"lat\":0.0,\"lon\":0.0},\"contact\":{},\"issue_report_channels\":[\"email\"],\
698-
\"state\":{\"open\":null},\"ext_aaa\":\"xxx\",\"ext_bbb\":[null,42]}"
748+
\"state\":{\"open\":false},\"ext_aaa\":\"xxx\",\"ext_bbb\":[null,42]}"
699749
);
700750
}
701751

@@ -704,6 +754,10 @@ mod test {
704754
let mut status = StatusBuilder::new("foo")
705755
.logo("bar")
706756
.url("foobar")
757+
.state(State {
758+
open: Some(false),
759+
..State::default()
760+
})
707761
.location(Location::default())
708762
.contact(Contact::default())
709763
.add_issue_report_channel(IssueReportChannel::Email)
@@ -728,6 +782,10 @@ mod test {
728782
let status = StatusBuilder::new("a")
729783
.logo("b")
730784
.url("c")
785+
.state(State {
786+
open: Some(false),
787+
..State::default()
788+
})
731789
.location(Location::default())
732790
.contact(Contact::default())
733791
.add_issue_report_channel(IssueReportChannel::Email)

0 commit comments

Comments
 (0)