Skip to main content

uk_police_api/models/
stop_and_search.rs

1use serde::{Deserialize, Serialize};
2
3use super::crime::Location;
4
5/// Type of stop and search.
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub enum StopAndSearchType {
8    #[serde(rename = "Person search")]
9    Person,
10    #[serde(rename = "Vehicle search")]
11    Vehicle,
12    #[serde(rename = "Person and Vehicle search")]
13    PersonAndVehicle,
14}
15
16/// A stop and search record.
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct StopAndSearch {
19    /// Type of search performed.
20    #[serde(rename = "type")]
21    pub kind: Option<StopAndSearchType>,
22    /// Whether a person was searched.
23    pub involved_person: Option<bool>,
24    /// Date and time of the stop (ISO 8601 format).
25    pub datetime: Option<String>,
26    /// Whether this was part of a policing operation.
27    pub operation: Option<bool>,
28    /// Name of the policing operation, if applicable.
29    pub operation_name: Option<String>,
30    /// Approximate location of the stop.
31    pub location: Option<Location>,
32    /// Gender of the person stopped.
33    pub gender: Option<String>,
34    /// Age range of the person stopped.
35    pub age_range: Option<String>,
36    /// Self-defined ethnicity of the person stopped.
37    pub self_defined_ethnicity: Option<String>,
38    /// Officer-defined ethnicity of the person stopped.
39    pub officer_defined_ethnicity: Option<String>,
40    /// Legislation under which the stop was conducted.
41    pub legislation: Option<String>,
42    /// Object of the search (e.g. "Controlled drugs").
43    pub object_of_search: Option<String>,
44    /// Outcome of the stop. `None` if nothing was found.
45    /// The API may return `false` instead of `null` when nothing was found.
46    #[serde(default, deserialize_with = "deserialize_outcome")]
47    pub outcome: Option<String>,
48    /// Whether the outcome was linked to the object of search.
49    pub outcome_linked_to_object_of_search: Option<bool>,
50    /// Whether removal of more than outer clothing was required.
51    pub removal_of_more_than_outer_clothing: Option<bool>,
52    /// Outcome object with id and name (returned by some endpoints).
53    #[serde(default)]
54    pub outcome_object: Option<OutcomeObject>,
55}
56
57/// Outcome identifier returned by the stops-by-force endpoint.
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct OutcomeObject {
60    /// Outcome identifier.
61    pub id: Option<String>,
62    /// Outcome name.
63    pub name: Option<String>,
64}
65
66/// Deserializes the `outcome` field which can be a string, `false`, or `null`.
67fn deserialize_outcome<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
68where
69    D: serde::Deserializer<'de>,
70{
71    #[derive(Deserialize)]
72    #[serde(untagged)]
73    enum StringOrBool {
74        String(String),
75        #[allow(dead_code)]
76        Bool(bool),
77    }
78
79    match Option::<StringOrBool>::deserialize(deserializer)? {
80        Some(StringOrBool::String(s)) => Ok(Some(s)),
81        _ => Ok(None),
82    }
83}