• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / bibdata / 3165b919-56f4-4faa-baf4-b5a08621230c

18 Sep 2025 03:08PM UTC coverage: 90.866% (+0.2%) from 90.675%
3165b919-56f4-4faa-baf4-b5a08621230c

Pull #2927

circleci

Ryan Laddusaw
Add QA DSpace config
Pull Request #2927: Index theses from dspace 7+

1411 of 1519 new or added lines in 16 files covered. (92.89%)

41 existing lines in 7 files now uncovered.

8874 of 9766 relevant lines covered (90.87%)

338.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.42
/lib/bibdata_rs/src/theses/holdings.rs
1
// This module is responsible for describing the holdings of a thesis
2

3
use crate::{solr::ElectronicAccess, theses::embargo};
4
use serde::{ser::SerializeStruct, Serialize};
5

6
use ThesisAvailability::*;
7

8
pub fn call_number(non_ark_ids: Option<&Vec<String>>) -> String {
160✔
9
    let ids = match non_ark_ids {
160✔
10
        Some(value) => value,
78✔
11
        None => &Vec::default(),
82✔
12
    };
13
    match ids.first() {
160✔
14
        Some(id) => format!("AC102 {}", id),
2✔
15
        None => "AC102".to_string(),
158✔
16
    }
17
}
160✔
18

19
pub fn online_holding_string(non_ark_ids: Option<&Vec<String>>) -> Option<String> {
40✔
20
    serde_json::to_string(&ThesisHoldingHash {
40✔
21
        thesis: OnlineHolding {
40✔
22
            call_number: call_number(non_ark_ids),
40✔
23
        },
40✔
24
    })
40✔
25
    .ok()
40✔
26
}
40✔
27

28
pub fn physical_holding_string(non_ark_ids: Option<&Vec<String>>) -> Option<String> {
12✔
29
    serde_json::to_string(&ThesisHoldingHash {
12✔
30
        thesis: PhysicalHolding {
12✔
31
            call_number: call_number(non_ark_ids),
12✔
32
        },
12✔
33
    })
12✔
34
    .ok()
12✔
35
}
12✔
36

37
fn physical_class_year(class_years: &[String]) -> bool {
380✔
38
    // For theses, there is no physical copy since 2013
39
    // anything 2012 and prior have a physical copy
40
    // See docs/theses.md
41
    match class_years.first() {
380✔
42
        Some(year) => year.as_str() < "2013",
154✔
43
        None => false,
226✔
44
    }
45
}
380✔
46

47
// Returns a string containing a JSON key/value.  The key is the Ark URL,
48
// the value is an array of details about what that URL provides.
49
pub fn dataspace_url_with_metadata(
58✔
50
    identifier_uri: Option<&Vec<String>>,
58✔
51
    location: bool,
58✔
52
    access_rights: bool,
58✔
53
    mudd_walkin: bool,
58✔
54
    class_year: Vec<String>,
58✔
55
    embargo: embargo::Embargo,
58✔
56
) -> Option<ElectronicAccess> {
58✔
57
    let first_ark = identifier_uri?.first()?;
58✔
58
    Some(ElectronicAccess {
59
        url: first_ark.to_owned(),
8✔
60
        link_text: "DataSpace".to_owned(),
8✔
61
        link_description: match on_site_only(
8✔
62
            location,
8✔
63
            access_rights,
8✔
64
            mudd_walkin,
8✔
65
            class_year,
8✔
66
            embargo,
8✔
67
        ) {
8✔
NEW
68
            OnSiteOnly => Some("Citation only".to_owned()),
×
69
            AvailableOffSite => Some("Full text".to_owned()),
8✔
70
        },
71
        iiif_manifest_paths: None,
8✔
72
        digital_content: None,
8✔
73
    })
74
}
58✔
75

76
#[derive(Debug, PartialEq)]
77
pub enum ThesisAvailability {
78
    AvailableOffSite,
79
    OnSiteOnly,
80
}
81

82
pub fn on_site_only(
397✔
83
    location: bool,
397✔
84
    access_rights: bool,
397✔
85
    mudd_walkin: bool,
397✔
86
    class_year: Vec<String>,
397✔
87
    embargo: embargo::Embargo,
397✔
88
) -> ThesisAvailability {
397✔
89
    if matches!(embargo, embargo::Embargo::Current(_)) {
397✔
90
        return OnSiteOnly;
19✔
91
    };
378✔
92
    if !physical_class_year(&class_year) {
378✔
93
        return AvailableOffSite;
305✔
94
    }
73✔
95
    if location || access_rights || mudd_walkin {
73✔
96
        return OnSiteOnly;
73✔
97
    }
×
NEW
98
    AvailableOffSite
×
99
}
397✔
100

101
#[derive(Debug, Serialize)]
102
pub struct ThesisHoldingHash<T>
103
where
104
    T: Serialize,
105
{
106
    thesis: T,
107
}
108

109
#[derive(Debug)]
110
pub struct OnlineHolding {
111
    call_number: String,
112
}
113

114
impl Serialize for OnlineHolding {
115
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
41✔
116
    where
41✔
117
        S: serde::Serializer,
41✔
118
    {
119
        let mut serializer = serializer.serialize_struct("OnlineHolding", 3)?;
41✔
120
        serializer.serialize_field("call_number", &self.call_number)?;
41✔
121
        serializer.serialize_field("call_number_browse", &self.call_number)?;
41✔
122
        serializer.serialize_field("dspace", &true)?;
41✔
123
        serializer.end()
41✔
124
    }
41✔
125
}
126

127
#[derive(Debug)]
128
pub struct PhysicalHolding {
129
    call_number: String,
130
}
131

132
impl Serialize for PhysicalHolding {
133
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
13✔
134
    where
13✔
135
        S: serde::Serializer,
13✔
136
    {
137
        let mut serializer = serializer.serialize_struct("PhysicalHolding", 6)?;
13✔
138
        serializer.serialize_field("location", "Mudd Manuscript Library")?;
13✔
139
        serializer.serialize_field("library", "Mudd Manuscript Library")?;
13✔
140
        serializer.serialize_field("location_code", "mudd$stacks")?;
13✔
141
        serializer.serialize_field("call_number", &self.call_number)?;
13✔
142
        serializer.serialize_field("call_number_browse", &self.call_number)?;
13✔
143
        serializer.serialize_field("dspace", &true)?;
13✔
144
        serializer.end()
13✔
145
    }
13✔
146
}
147

148
#[cfg(test)]
149
mod tests {
150
    use std::vec;
151

152
    use super::*;
153

154
    #[test]
155
    fn it_can_create_call_number() {
1✔
156
        assert_eq!(
1✔
157
            call_number(Some(&vec![
1✔
158
                "123".to_owned(),
1✔
159
                "456".to_owned(),
1✔
160
                "789".to_owned()
1✔
161
            ])),
1✔
162
            "AC102 123"
163
        );
164
        assert_eq!(call_number(Some(&vec!["2377".to_owned()])), "AC102 2377");
1✔
165
        assert_eq!(call_number(Some(&vec![])), "AC102");
1✔
166
        assert_eq!(call_number(None), "AC102");
1✔
167
    }
1✔
168

169
    #[test]
170
    fn it_can_serialize_online_holding() {
1✔
171
        let hash = ThesisHoldingHash {
1✔
172
            thesis: OnlineHolding {
1✔
173
                call_number: "AC102".to_owned(),
1✔
174
            },
1✔
175
        };
1✔
176
        assert_eq!(
1✔
177
            serde_json::to_string(&hash).unwrap(),
1✔
178
            r#"{"thesis":{"call_number":"AC102","call_number_browse":"AC102","dspace":true}}"#
179
        );
180
    }
1✔
181

182
    #[test]
183
    fn it_can_serialize_physical_holding() {
1✔
184
        let hash = ThesisHoldingHash {
1✔
185
            thesis: PhysicalHolding {
1✔
186
                call_number: "AC102".to_owned(),
1✔
187
            },
1✔
188
        };
1✔
189
        assert_eq!(
1✔
190
            serde_json::to_string(&hash).unwrap(),
1✔
191
            r#"{"thesis":{"location":"Mudd Manuscript Library","library":"Mudd Manuscript Library","location_code":"mudd$stacks","call_number":"AC102","call_number_browse":"AC102","dspace":true}}"#
192
        );
193
    }
1✔
194

195
    #[test]
196
    fn it_is_onsite_only_when_current_embargo() {
1✔
197
        let location = false;
1✔
198
        let access_rights = false;
1✔
199
        let mudd_walkin = false;
1✔
200
        let class_year = vec![];
1✔
201
        let embargo =
1✔
202
            embargo::Embargo::Current("This content is embargoed until July 13, 2100".to_owned());
1✔
203
        assert_eq!(
1✔
204
            on_site_only(location, access_rights, mudd_walkin, class_year, embargo),
1✔
205
            OnSiteOnly
206
        );
207
    }
1✔
208

209
    #[test]
210
    fn it_is_not_onsite_only_when_embargo_is_expired() {
1✔
211
        let location = false;
1✔
212
        let access_rights = false;
1✔
213
        let mudd_walkin = false;
1✔
214
        let class_year = vec![];
1✔
215
        let embargo = embargo::Embargo::Expired;
1✔
216
        assert_eq!(
1✔
217
            on_site_only(location, access_rights, mudd_walkin, class_year, embargo),
1✔
218
            AvailableOffSite
219
        );
220
    }
1✔
221

222
    #[test]
223
    fn it_is_not_onsite_only_when_embargo_is_expired_and_walkin() {
1✔
224
        let location = false;
1✔
225
        let access_rights = false;
1✔
226
        let mudd_walkin = true;
1✔
227
        let class_year = vec![];
1✔
228
        let embargo = embargo::Embargo::Expired;
1✔
229
        assert_eq!(
1✔
230
            on_site_only(location, access_rights, mudd_walkin, class_year, embargo),
1✔
231
            AvailableOffSite
232
        );
233
    }
1✔
234

235
    #[test]
236
    fn it_is_onsite_only_when_prior_to_2013() {
1✔
237
        let location = false;
1✔
238
        let access_rights = false;
1✔
239
        let mudd_walkin = true;
1✔
240
        let class_year = vec!["2012-01-01T00:00:00Z".to_owned()];
1✔
241
        let embargo = embargo::Embargo::Expired;
1✔
242
        assert_eq!(
1✔
243
            on_site_only(location, access_rights, mudd_walkin, class_year, embargo),
1✔
244
            OnSiteOnly
245
        );
246
    }
1✔
247

248
    #[test]
249
    fn it_is_not_onsite_only_when_from_2013() {
1✔
250
        let location = false;
1✔
251
        let access_rights = false;
1✔
252
        let mudd_walkin = true;
1✔
253
        let class_year = vec!["2013-01-01T00:00:00Z".to_owned()];
1✔
254
        let embargo = embargo::Embargo::Expired;
1✔
255
        assert_eq!(
1✔
256
            on_site_only(location, access_rights, mudd_walkin, class_year, embargo),
1✔
257
            AvailableOffSite
258
        );
259
    }
1✔
260

261
    #[test]
262
    fn it_is_not_onsite_only_when_physical_location_is_true() {
1✔
263
        let location = true;
1✔
264
        let access_rights = false;
1✔
265
        let mudd_walkin = false;
1✔
266
        let class_year = vec![];
1✔
267
        let embargo = embargo::Embargo::None;
1✔
268
        assert_eq!(
1✔
269
            on_site_only(location, access_rights, mudd_walkin, class_year, embargo),
1✔
270
            AvailableOffSite
271
        );
272
    }
1✔
273

274
    #[test]
275
    fn it_is_not_onsite_only_when_no_restrictions_field() {
1✔
276
        let location = false;
1✔
277
        let access_rights = false;
1✔
278
        let mudd_walkin = false;
1✔
279
        let class_year = vec![];
1✔
280
        let embargo = embargo::Embargo::None;
1✔
281
        assert_eq!(
1✔
282
            on_site_only(location, access_rights, mudd_walkin, class_year, embargo),
1✔
283
            AvailableOffSite
284
        );
285
    }
1✔
286

287
    #[test]
288
    fn it_can_determine_if_class_year_would_have_physical_holdings() {
1✔
289
        assert!(physical_class_year(&["2010".to_string()]));
1✔
290
    }
1✔
291

292
    #[test]
293
    fn it_can_determine_if_class_year_is_too_new_for_physical_holdings() {
1✔
294
        assert!(!physical_class_year(&["2013".to_string()]));
1✔
295
    }
1✔
296
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc