1use crate::{
2 error::{Error, Result},
3 resources::attachments::AttachmentMetadata,
4 resources::entity_def::Name as EntityName,
5 resources::label_def::Name as LabelName,
6 resources::label_group::Name as LabelGroupName,
7 resources::label_group::DEFAULT_LABEL_GROUP_NAME,
8 ReducibleResponse, SourceId, SplittableRequest,
9};
10use chrono::{DateTime, Utc};
11use ordered_float::NotNan;
12use serde::{
13 de::{Deserializer, Error as SerdeError, MapAccess, Visitor},
14 ser::{SerializeMap, Serializer},
15 Deserialize, Serialize,
16};
17use serde_json::Value as JsonValue;
18use std::{
19 collections::HashMap,
20 fmt::{Formatter, Result as FmtResult},
21 ops::{Deref, DerefMut},
22 path::PathBuf,
23 result::Result as StdResult,
24 str::FromStr,
25};
26
27#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
28pub struct Id(pub String);
29
30impl FromStr for Id {
31 type Err = Error;
32
33 fn from_str(string: &str) -> Result<Self> {
34 Ok(Self(string.to_owned()))
35 }
36}
37
38#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
39pub struct Uid(pub String);
40
41impl FromStr for Uid {
42 type Err = Error;
43
44 fn from_str(string: &str) -> Result<Self> {
45 Ok(Self(string.to_owned()))
46 }
47}
48
49#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
50pub struct ThreadId(pub String);
51
52#[derive(Debug, Clone, Serialize, Deserialize, Default)]
53pub struct CommentTimestampFilter {
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub minimum: Option<DateTime<Utc>>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub maximum: Option<DateTime<Utc>>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(rename_all = "snake_case")]
63pub enum ReviewedFilterEnum {
64 OnlyReviewed,
65 OnlyUnreviewed,
66}
67
68type UserPropertyName = String;
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct UserPropertiesFilter(pub HashMap<UserPropertyName, PropertyFilter>);
72
73#[derive(Default, Debug, Clone, Serialize, Deserialize)]
74pub struct PropertyFilter {
75 #[serde(skip_serializing_if = "<[_]>::is_empty", default)]
76 pub one_of: Vec<PropertyValue>,
77 #[serde(skip_serializing_if = "<[_]>::is_empty", default)]
78 pub not_one_of: Vec<PropertyValue>,
79 #[serde(skip_serializing_if = "<[_]>::is_empty", default)]
80 pub domain_not_one_of: Vec<PropertyValue>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub minimum: Option<NotNan<f64>>,
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub maximum: Option<NotNan<f64>>,
85}
86
87impl PropertyFilter {
88 pub fn new(
89 one_of: Vec<PropertyValue>,
90 not_one_of: Vec<PropertyValue>,
91 domain_not_one_of: Vec<PropertyValue>,
92 ) -> Self {
93 Self {
94 one_of,
95 not_one_of,
96 domain_not_one_of,
97 ..Default::default()
98 }
99 }
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, Default)]
103pub struct CommentFilter {
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub reviewed: Option<ReviewedFilterEnum>,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub timestamp: Option<CommentTimestampFilter>,
109
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub user_properties: Option<UserPropertiesFilter>,
112
113 #[serde(skip_serializing_if = "Vec::is_empty")]
114 #[serde(default)]
115 pub sources: Vec<SourceId>,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub messages: Option<MessagesFilter>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize, Default)]
122pub struct MessagesFilter {
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub from: Option<PropertyFilter>,
125
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub to: Option<PropertyFilter>,
128}
129
130#[derive(Debug, Clone, Serialize)]
131#[serde(rename_all(serialize = "lowercase"))]
132pub enum CommentPredictionsThreshold {
133 Auto,
134}
135
136#[derive(Debug, Clone, Serialize)]
137pub struct TriggerLabelThreshold {
138 pub name: Vec<String>,
139 pub threshold: NotNan<f64>,
140}
141
142#[derive(Debug, Clone, Serialize)]
143pub struct GetCommentPredictionsRequest {
144 pub uids: Vec<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub threshold: Option<CommentPredictionsThreshold>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub labels: Option<Vec<TriggerLabelThreshold>>,
149}
150
151#[derive(Debug, Clone, Serialize)]
152pub(crate) struct GetRecentRequest<'a> {
153 pub limit: usize,
154 pub filter: &'a CommentFilter,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub continuation: Option<&'a Continuation>,
157}
158
159#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
160pub struct Continuation(pub String);
161
162#[derive(Debug, Clone, Deserialize)]
163pub struct RecentCommentsPage {
164 pub results: Vec<AnnotatedComment>,
165 pub continuation: Option<Continuation>,
166}
167
168#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
169pub struct GetLabellingsAfter(pub String);
170
171#[derive(Debug, Clone, Deserialize)]
172pub struct GetAnnotationsResponse {
173 pub results: Vec<AnnotatedComment>,
174 #[serde(default)]
175 pub after: Option<GetLabellingsAfter>,
176}
177
178#[derive(Debug, Clone, Deserialize)]
179pub struct GetPredictionsResponse {
180 pub predictions: Vec<Prediction>,
181}
182
183#[derive(Debug, Clone, Serialize)]
184pub struct UpdateAnnotationsRequest<'a> {
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub labelling: Option<&'a [NewLabelling]>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub entities: Option<&'a NewEntities>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub moon_forms: Option<&'a [NewMoonForm]>,
191}
192
193#[derive(Debug, Clone, Deserialize)]
194pub struct CommentsIterPage {
195 pub comments: Vec<Comment>,
196 pub continuation: Option<Continuation>,
197}
198
199#[derive(Debug, Clone, Serialize)]
200pub(crate) struct PutCommentsRequest {
201 pub comments: Vec<NewComment>,
202}
203
204impl SplittableRequest for PutCommentsRequest {
205 fn split(self) -> impl Iterator<Item = Self> {
206 self.comments.into_iter().map(|comment| Self {
207 comments: vec![comment],
208 })
209 }
210
211 fn count(&self) -> usize {
212 self.comments.len()
213 }
214}
215
216#[derive(Default, Debug, Copy, Clone, Deserialize)]
217pub struct PutCommentsResponse;
218
219impl ReducibleResponse for PutCommentsResponse {}
220
221#[derive(Debug, Clone, Serialize)]
222pub(crate) struct SyncCommentsRequest {
223 pub comments: Vec<NewComment>,
224}
225
226impl SplittableRequest for SyncCommentsRequest {
227 fn split(self) -> impl Iterator<Item = Self>
228 where
229 Self: Sized,
230 {
231 self.comments.into_iter().map(|comment| Self {
232 comments: vec![comment],
233 })
234 }
235
236 fn count(&self) -> usize {
237 self.comments.len()
238 }
239}
240
241#[derive(Debug, Clone, Deserialize, Default)]
242pub struct SyncCommentsResponse {
243 pub new: usize,
244 pub updated: usize,
245 pub unchanged: usize,
246}
247
248impl ReducibleResponse for SyncCommentsResponse {
249 fn empty() -> Self {
250 Self {
251 new: 0,
252 updated: 0,
253 unchanged: 0,
254 }
255 }
256 fn merge(self, b: Self) -> Self {
257 Self {
258 new: self.new + b.new,
259 updated: self.updated + b.updated,
260 unchanged: self.unchanged + b.unchanged,
261 }
262 }
263}
264
265#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
266pub struct GetCommentResponse {
267 pub comment: Comment,
268}
269
270#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
271pub struct Comment {
272 pub id: Id,
273 pub uid: Uid,
274 #[serde(skip_serializing_if = "Option::is_none")]
275 pub thread_id: Option<ThreadId>,
276 pub timestamp: DateTime<Utc>,
277 pub messages: Vec<Message>,
278 #[serde(skip_serializing_if = "PropertyMap::is_empty", default)]
279 pub user_properties: PropertyMap,
280 #[serde(skip_serializing_if = "Vec::is_empty", default)]
281 pub attachments: Vec<AttachmentMetadata>,
282 pub created_at: DateTime<Utc>,
283
284 #[serde(default)]
285 pub has_annotations: bool,
286}
287
288#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
289pub struct NewComment {
290 pub id: Id,
291 #[serde(skip_serializing_if = "Option::is_none")]
292 pub thread_id: Option<ThreadId>,
293 pub timestamp: DateTime<Utc>,
294 pub messages: Vec<Message>,
295 #[serde(skip_serializing_if = "PropertyMap::is_empty", default)]
296 pub user_properties: PropertyMap,
297 #[serde(skip_serializing_if = "Vec::is_empty", default)]
298 pub attachments: Vec<AttachmentMetadata>,
299}
300
301#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
302pub struct Message {
303 pub body: MessageBody,
304
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub language: Option<String>,
307
308 #[serde(skip_serializing_if = "Option::is_none")]
309 pub subject: Option<MessageSubject>,
310
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub signature: Option<MessageSignature>,
313
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub from: Option<String>,
316
317 #[serde(skip_serializing_if = "Option::is_none")]
318 pub to: Option<Vec<String>>,
319
320 #[serde(skip_serializing_if = "Option::is_none")]
321 pub cc: Option<Vec<String>>,
322
323 #[serde(skip_serializing_if = "Option::is_none")]
324 pub bcc: Option<Vec<String>>,
325
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub sent_at: Option<DateTime<Utc>>,
328}
329
330#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
331pub struct MessageBody {
332 pub text: String,
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub translated_from: Option<String>,
335 #[serde(skip_serializing_if = "Option::is_none")]
336 pub text_markup: Option<JsonValue>,
337 #[serde(skip_serializing_if = "Option::is_none")]
338 pub translated_from_markup: Option<JsonValue>,
339}
340
341#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
342pub struct MessageSubject {
343 pub text: String,
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub translated_from: Option<String>,
346}
347
348#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
349pub struct MessageSignature {
350 pub text: String,
351 #[serde(skip_serializing_if = "Option::is_none")]
352 pub translated_from: Option<String>,
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub text_markup: Option<JsonValue>,
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub translated_from_markup: Option<JsonValue>,
357}
358
359#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
360pub enum Sentiment {
361 #[serde(rename = "positive")]
362 Positive,
363
364 #[serde(rename = "negative")]
365 Negative,
366}
367
368#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
369pub struct AttachmentReference(pub String);
370
371#[derive(Debug, Clone, PartialEq, Default, Eq)]
372pub struct PropertyMap(HashMap<String, PropertyValue>);
373
374#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
375#[serde(untagged)]
376pub enum PropertyValue {
377 String(String),
378 Number(NotNan<f64>),
379}
380
381impl Deref for PropertyMap {
382 type Target = HashMap<String, PropertyValue>;
383
384 fn deref(&self) -> &Self::Target {
385 &self.0
386 }
387}
388
389impl DerefMut for PropertyMap {
390 fn deref_mut(&mut self) -> &mut Self::Target {
391 &mut self.0
392 }
393}
394
395impl PropertyMap {
396 #[inline]
397 pub fn new() -> Self {
398 Default::default()
399 }
400
401 #[inline]
402 pub fn with_capacity(capacity: usize) -> Self {
403 PropertyMap(HashMap::with_capacity(capacity))
404 }
405
406 #[inline]
407 pub fn insert_number(&mut self, key: String, value: NotNan<f64>) {
408 self.0.insert(key, PropertyValue::Number(value));
409 }
410
411 #[inline]
412 pub fn insert_string(&mut self, key: String, value: String) {
413 self.0.insert(key, PropertyValue::String(value));
414 }
415
416 #[inline]
418 pub fn is_empty(&self) -> bool {
419 self.0.is_empty()
420 }
421}
422
423const STRING_PROPERTY_PREFIX: &str = "string:";
424const NUMBER_PROPERTY_PREFIX: &str = "number:";
425
426impl Serialize for PropertyMap {
427 fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
428 let mut state = serializer.serialize_map(Some(self.len()))?;
429 if self.0.is_empty() {
430 return state.end();
431 }
432
433 let mut full_name = String::with_capacity(32);
434 for (key, value) in &self.0 {
435 full_name.clear();
436 match value {
437 PropertyValue::String(value) => {
438 if !value.trim().is_empty() {
439 full_name.push_str(STRING_PROPERTY_PREFIX);
440 full_name.push_str(key);
441 state.serialize_entry(&full_name, &value)?;
442 }
443 }
444 PropertyValue::Number(value) => {
445 full_name.push_str(NUMBER_PROPERTY_PREFIX);
446 full_name.push_str(key);
447 state.serialize_entry(&full_name, &value)?;
448 }
449 }
450 }
451 state.end()
452 }
453}
454
455impl<'de> Deserialize<'de> for PropertyMap {
456 #[inline]
457 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
458 deserializer.deserialize_any(PropertyMapVisitor)
459 }
460}
461
462struct PropertyMapVisitor;
463impl<'de> Visitor<'de> for PropertyMapVisitor {
464 type Value = PropertyMap;
465
466 fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult {
467 write!(formatter, "a user property map")
468 }
469
470 #[inline]
471 fn visit_unit<E>(self) -> StdResult<PropertyMap, E> {
472 Ok(PropertyMap::new())
473 }
474
475 fn visit_map<M>(self, mut access: M) -> StdResult<PropertyMap, M::Error>
476 where
477 M: MapAccess<'de>,
478 {
479 let mut values = PropertyMap::with_capacity(access.size_hint().unwrap_or(0));
480
481 while let Some(mut key) = access.next_key()? {
482 if strip_prefix(&mut key, STRING_PROPERTY_PREFIX) {
483 values.insert(key, PropertyValue::String(access.next_value()?));
484 } else if strip_prefix(&mut key, NUMBER_PROPERTY_PREFIX) {
485 values.insert(key, PropertyValue::Number(access.next_value()?));
486 } else {
487 return Err(M::Error::custom(format!(
488 "user property full name `{key}` has invalid \
489 type prefix"
490 )));
491 }
492 }
493
494 Ok(values)
495 }
496}
497
498#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
499pub struct AnnotatedComment {
500 pub comment: Comment,
501 #[serde(skip_serializing_if = "should_skip_serializing_labelling")]
502 pub labelling: Option<Vec<Labelling>>,
503 #[serde(skip_serializing_if = "should_skip_serializing_entities")]
504 pub entities: Option<Entities>,
505 #[serde(skip_serializing_if = "Option::is_none")]
506 pub thread_properties: Option<ThreadProperties>,
507 #[serde(skip_serializing_if = "should_skip_serializing_optional_vec", default)]
508 pub moon_forms: Option<Vec<MoonForm>>,
509 #[serde(skip_serializing_if = "should_skip_serializing_optional_vec", default)]
510 pub label_properties: Option<Vec<LabelProperty>>,
511}
512
513#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
514pub struct Prediction {
515 pub uid: Uid,
516 #[serde(skip_serializing_if = "should_skip_serializing_optional_vec")]
517 pub labels: Option<Vec<PredictedLabel>>,
518 #[serde(skip_serializing_if = "should_skip_serializing_optional_vec")]
519 pub entities: Option<Vec<Entity>>,
520}
521
522pub fn get_default_labelling_group(labelling: &Option<Vec<Labelling>>) -> Option<&Labelling> {
523 labelling
524 .iter()
525 .flatten()
526 .find(|&labelling_group| labelling_group.is_default_group())
527}
528
529impl Labelling {
530 pub fn is_default_group(&self) -> bool {
531 self.group == *DEFAULT_LABEL_GROUP_NAME
532 }
533}
534
535impl NewLabelling {
536 pub fn is_default_group(&self) -> bool {
537 self.group == *DEFAULT_LABEL_GROUP_NAME
538 }
539}
540
541impl HasAnnotations for AnnotatedComment {
542 fn has_annotations(&self) -> bool {
543 let has_labels = self.labelling.iter().flatten().any(|labelling_group| {
544 !labelling_group.assigned.is_empty() || !labelling_group.dismissed.is_empty()
545 });
546 let has_entities = self
547 .entities
548 .as_ref()
549 .map(|entities| !entities.assigned.is_empty() || !entities.dismissed.is_empty())
550 .unwrap_or(false);
551 has_labels || has_entities || self.moon_forms.has_annotations()
552 }
553}
554
555impl AnnotatedComment {
556 pub fn without_predictions(mut self) -> Self {
557 self.labelling = self.labelling.and_then(|mut labelling| {
558 if labelling.iter().all(|labelling_group| {
559 labelling_group.assigned.is_empty() && labelling_group.dismissed.is_empty()
560 }) {
561 None
562 } else {
563 for comment_labelling in &mut labelling {
564 comment_labelling.predicted = None;
565 }
566 Some(labelling)
567 }
568 });
569 self.entities = self.entities.and_then(|mut entities| {
570 if entities.assigned.is_empty() && entities.dismissed.is_empty() {
571 None
572 } else {
573 entities.predicted = None;
574 Some(entities)
575 }
576 });
577 self
578 }
579}
580
581#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
582pub struct ThreadProperties {
583 duration: Option<NotNan<f64>>,
584 response_time: Option<NotNan<f64>>,
585 num_messages: u64,
586 thread_position: Option<u64>,
587 first_sender: Option<String>,
588}
589
590#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
591#[serde(untagged)]
592pub enum EitherLabelling {
593 Labelling(Vec<NewLabelling>),
594 LegacyLabelling(NewLegacyLabelling),
595}
596
597impl EitherLabelling {
598 fn into_new_labellings(self) -> Vec<NewLabelling> {
599 match self {
600 EitherLabelling::Labelling(new_labelling_vec) => new_labelling_vec,
601 EitherLabelling::LegacyLabelling(new_legacy_labelling) => {
602 vec![NewLabelling {
603 group: DEFAULT_LABEL_GROUP_NAME.clone(),
604 assigned: new_legacy_labelling.assigned,
605 dismissed: new_legacy_labelling.dismissed,
606 }]
607 }
608 }
609 }
610}
611
612impl From<EitherLabelling> for Vec<NewLabelling> {
613 fn from(either_labelling: EitherLabelling) -> Vec<NewLabelling> {
614 either_labelling.into_new_labellings()
615 }
616}
617
618impl HasAnnotations for EitherLabelling {
619 fn has_annotations(&self) -> bool {
620 match self {
621 EitherLabelling::Labelling(new_labelling) => {
622 new_labelling.iter().any(|labelling_group| {
623 labelling_group.assigned.is_some() || labelling_group.dismissed.is_some()
624 })
625 }
626 EitherLabelling::LegacyLabelling(new_legacy_labelling) => {
627 new_legacy_labelling.assigned.is_some() || new_legacy_labelling.dismissed.is_some()
628 }
629 }
630 }
631}
632
633#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
634pub struct NewAnnotatedComment {
635 pub comment: NewComment,
636 #[serde(skip_serializing_if = "Option::is_none")]
637 pub labelling: Option<EitherLabelling>,
638 #[serde(skip_serializing_if = "Option::is_none")]
639 pub entities: Option<NewEntities>,
640 #[serde(skip_serializing_if = "Option::is_none")]
641 pub audio_path: Option<PathBuf>,
642 #[serde(skip_serializing_if = "should_skip_serializing_optional_vec", default)]
643 pub moon_forms: Option<Vec<NewMoonForm>>,
644}
645
646impl<T> HasAnnotations for Option<T>
647where
648 T: HasAnnotations,
649{
650 fn has_annotations(&self) -> bool {
651 self.as_ref().is_some_and(HasAnnotations::has_annotations)
652 }
653}
654
655impl HasAnnotations for Vec<MoonForm> {
656 fn has_annotations(&self) -> bool {
657 self.iter().any(|form| !form.assigned.is_empty())
658 }
659}
660
661impl HasAnnotations for Vec<NewMoonForm> {
662 fn has_annotations(&self) -> bool {
663 self.iter().any(|form| !form.assigned.is_empty())
664 }
665}
666
667impl NewAnnotatedComment {
668 pub fn has_annotations(&self) -> bool {
669 self.labelling.has_annotations()
670 || self.entities.has_annotations()
671 || self.moon_forms.has_annotations()
672 }
673}
674
675#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
676pub struct Labelling {
677 pub group: LabelGroupName,
678 #[serde(skip_serializing_if = "Vec::is_empty", default)]
679 pub assigned: Vec<Label>,
680 #[serde(skip_serializing_if = "Vec::is_empty", default)]
681 pub dismissed: Vec<Label>,
682 #[serde(skip_serializing_if = "should_skip_serializing_optional_vec", default)]
683 pub predicted: Option<Vec<PredictedLabel>>,
684}
685
686#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
687pub struct NewLabelling {
688 pub group: LabelGroupName,
689 #[serde(skip_serializing_if = "Option::is_none", default)]
690 pub assigned: Option<Vec<Label>>,
691 #[serde(skip_serializing_if = "Option::is_none", default)]
692 pub dismissed: Option<Vec<Label>>,
693}
694
695#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
697pub struct NewLegacyLabelling {
698 #[serde(skip_serializing_if = "Option::is_none", default)]
699 pub assigned: Option<Vec<Label>>,
700 #[serde(skip_serializing_if = "Option::is_none", default)]
701 pub dismissed: Option<Vec<Label>>,
702}
703
704#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
705pub struct Label {
706 pub name: LabelName,
707 pub sentiment: Sentiment,
708 #[serde(skip_serializing_if = "Option::is_none", default)]
709 pub metadata: Option<HashMap<String, JsonValue>>,
710}
711
712#[derive(Debug, Hash, Clone, Deserialize, Serialize, PartialEq, Eq)]
713#[serde(untagged)]
714pub enum PredictedLabelName {
715 Parts(Vec<String>),
716 String(LabelName),
717}
718
719impl PredictedLabelName {
720 pub fn to_label_name(&self) -> LabelName {
721 match self {
722 Self::Parts(parts) => LabelName(parts.join(" > ")),
723 Self::String(string) => string.clone(),
724 }
725 }
726}
727
728#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
729pub struct PredictedLabel {
730 pub name: PredictedLabelName,
731 #[serde(skip_serializing_if = "Option::is_none")]
732 pub sentiment: Option<NotNan<f64>>,
733 pub probability: NotNan<f64>,
734 #[serde(skip_serializing_if = "Option::is_none")]
735 pub auto_thresholds: Option<Vec<String>>,
736}
737
738#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
739pub struct LabelProperty {
740 pub id: String,
741 #[serde(skip_serializing_if = "Option::is_none")]
742 pub name: Option<String>,
743 pub value: NotNan<f64>,
744 #[serde(skip_serializing_if = "Option::is_none")]
745 pub breakdown: Option<LabelPropertyBreakdown>,
746}
747
748#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
749pub struct LabelPropertyBreakdown {
750 pub label_contributions: Vec<LabelPropertyContribution>,
751 pub other_group_contributions: Vec<LabelPropertyGroupContribution>,
752}
753
754#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
755pub struct LabelPropertyContribution {
756 pub name: LabelName,
757 pub value: NotNan<f64>,
758}
759
760#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
761pub struct LabelPropertyGroupContribution {
762 pub name: LabelGroupName,
763 pub value: NotNan<f64>,
764}
765
766#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
767pub struct Entities {
768 #[serde(skip_serializing_if = "Vec::is_empty")]
769 pub assigned: Vec<Entity>,
770 #[serde(skip_serializing_if = "Vec::is_empty")]
771 pub dismissed: Vec<Entity>,
772 #[serde(skip_serializing_if = "should_skip_serializing_optional_vec", default)]
773 pub predicted: Option<Vec<Entity>>,
774}
775
776#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
777pub struct NewEntities {
778 #[serde(skip_serializing_if = "Vec::is_empty", default)]
779 pub assigned: Vec<NewEntity>,
780 #[serde(skip_serializing_if = "Vec::is_empty", default)]
781 pub dismissed: Vec<NewEntity>,
782}
783
784#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
785pub struct MoonFormCapture {
786 #[serde(skip_serializing_if = "Vec::is_empty", default)]
787 pub fields: Vec<MoonFormFieldAnnotation>,
788}
789
790#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
791pub struct MoonFormLabelCaptures {
792 pub label: Label,
793 #[serde(skip_serializing_if = "Vec::is_empty", default)]
794 pub captures: Vec<MoonFormCapture>,
795}
796
797#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
798pub struct MoonForm {
799 pub group: LabelGroupName,
800 #[serde(default)]
801 pub assigned: Vec<MoonFormLabelCaptures>,
802 #[serde(skip_serializing_if = "should_skip_serializing_optional_vec", default)]
803 pub predicted: Option<Vec<MoonFormLabelCaptures>>,
804 #[serde(default)]
805 pub dismissed: Vec<MoonFormLabelCaptures>,
806}
807
808#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
809pub struct NewMoonFormCapture {
810 #[serde(skip_serializing_if = "Vec::is_empty", default)]
811 pub fields: Vec<MoonFormFieldAnnotationNew>,
812}
813
814#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
815pub struct NewMoonFormLabelCaptures {
816 pub label: Label,
817 #[serde(skip_serializing_if = "Vec::is_empty", default)]
818 pub captures: Vec<NewMoonFormCapture>,
819}
820
821#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
822pub struct MoonFormDismissedUpdate {
823 #[serde(skip_serializing_if = "Option::is_none")]
824 pub captures: Option<Vec<NewMoonFormLabelCaptures>>,
825 pub entities: Vec<MoonFormFieldAnnotationNew>,
826 #[serde(default)]
827 pub labels: Vec<Label>,
828}
829
830#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
831pub struct NewMoonForm {
832 pub group: LabelGroupName,
833 #[serde(default)]
834 pub assigned: Vec<NewMoonFormLabelCaptures>,
835}
836
837pub trait HasAnnotations {
838 fn has_annotations(&self) -> bool;
839}
840
841impl HasAnnotations for NewEntities {
842 fn has_annotations(&self) -> bool {
843 !self.assigned.is_empty() || !self.dismissed.is_empty()
844 }
845}
846
847pub fn should_skip_serializing_optional_vec<T>(maybe_vec: &Option<Vec<T>>) -> bool {
848 if let Some(actual_vec) = maybe_vec {
849 actual_vec.is_empty()
850 } else {
851 true
852 }
853}
854
855fn should_skip_serializing_labelling(maybe_labelling: &Option<Vec<Labelling>>) -> bool {
856 if let Some(default_labelling) = get_default_labelling_group(maybe_labelling) {
857 default_labelling.assigned.is_empty()
858 && default_labelling.dismissed.is_empty()
859 && should_skip_serializing_optional_vec(&default_labelling.predicted)
860 } else {
861 true
862 }
863}
864
865fn should_skip_serializing_entities(maybe_entities: &Option<Entities>) -> bool {
866 if let Some(entities) = maybe_entities {
867 entities.assigned.is_empty()
868 && entities.dismissed.is_empty()
869 && should_skip_serializing_optional_vec(&entities.predicted)
870 } else {
871 true
872 }
873}
874
875#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
876#[serde(untagged)]
877pub enum NewEntity {
878 WithSpan(NewEntityWithSpan),
879 WithSpans(NewEntityWithSpans),
880}
881
882#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
883pub struct NewEntityWithSpan {
884 pub name: EntityName,
885 pub formatted_value: String,
886 pub span: NewEntitySpan,
887}
888
889#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
890pub struct FieldId(pub String);
891
892#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
893pub struct NewEntityWithSpans {
894 pub name: EntityName,
895 pub formatted_value: String,
896 pub spans: Vec<NewEntitySpan>,
897}
898
899#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Eq)]
900pub struct NewEntitySpan {
901 content_part: String,
902 message_index: usize,
903 utf16_byte_start: usize,
904 utf16_byte_end: usize,
905}
906
907#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
908pub struct Entity {
909 pub name: EntityName,
910 pub formatted_value: String,
911 pub spans: Vec<EntitySpan>,
912 #[serde(skip_serializing_if = "Option::is_none")]
913 pub field_id: Option<FieldId>,
914}
915
916#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
917pub struct MoonFormFieldAnnotation {
918 pub name: String,
919 pub spans: Vec<EntitySpan>,
920 pub kind: EntityName,
921
922 pub formatted_value: String,
923
924 #[serde(skip_serializing_if = "Option::is_none")]
925 pub field_id: Option<FieldId>,
926
927 #[serde(default)]
928 pub document_spans: Vec<DocumentSpan>,
929}
930
931#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
932pub struct EntitySpanUtf16 {
933 content_part: String,
934 message_index: u32,
935 utf16_byte_start: u32,
936 utf16_byte_end: u32,
937}
938
939#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
940pub struct MoonFormFieldAnnotationNew {
941 pub name: String,
942 pub spans: Vec<EntitySpanUtf16>,
943 pub formatted_value: String,
944 #[serde(default)]
945 pub document_spans: Vec<DocumentSpan>,
946}
947
948#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
949pub struct DocumentSpan {
950 page_index: u32,
951 attachment_index: u32,
952 polygon: PagePolygon,
953}
954
955#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
956pub struct PagePolygon {
957 vertices: Vec<Vertex>,
958}
959
960#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
961pub struct Vertex {
962 x: f64,
963 y: f64,
964}
965
966#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Eq)]
967pub struct EntitySpan {
968 content_part: String,
969 message_index: usize,
970 char_start: usize,
971 char_end: usize,
972 utf16_byte_start: usize,
973 utf16_byte_end: usize,
974}
975
976#[inline]
977fn strip_prefix(string: &mut String, prefix: &str) -> bool {
978 if string.starts_with(prefix) {
979 string.drain(..prefix.len());
980 true
981 } else {
982 false
983 }
984}
985
986#[cfg(test)]
987mod tests {
988 use super::*;
989 use serde_json::{self, json, Value as JsonValue};
990 use std::collections::HashMap;
991
992 #[test]
993 fn property_map_empty_serialize() {
994 assert_eq!(serde_json::to_string(&PropertyMap::new()).expect(""), "{}");
995 }
996
997 #[test]
998 fn property_map_one_number_serialize() {
999 let mut map = PropertyMap::new();
1000 map.insert_number("nps".to_owned(), NotNan::new(7.0).unwrap());
1001 assert_eq!(
1002 serde_json::to_string(&map).expect(""),
1003 r#"{"number:nps":7.0}"#
1004 );
1005 }
1006
1007 #[test]
1008 fn property_map_one_string_serialize() {
1009 let mut map = PropertyMap::new();
1010 map.insert_string("age".to_owned(), "18-25".to_owned());
1011 assert_eq!(
1012 serde_json::to_string(&map).expect(""),
1013 r#"{"string:age":"18-25"}"#
1014 );
1015 }
1016
1017 #[test]
1018 fn property_map_mixed_serialize() {
1019 let mut map = PropertyMap::new();
1020 map.insert_string("age".to_owned(), "18-25".to_owned());
1021 map.insert_string("income".to_owned(), "$6000".to_owned());
1022 map.insert_number("nps".to_owned(), NotNan::new(3.0).unwrap());
1023
1024 let actual_map: HashMap<String, JsonValue> =
1025 serde_json::from_str(&serde_json::to_string(&map).expect("ser")).expect("de");
1026
1027 let mut expected_map = HashMap::new();
1028 expected_map.insert(
1029 "string:age".to_owned(),
1030 JsonValue::String("18-25".to_owned()),
1031 );
1032 expected_map.insert(
1033 "string:income".to_owned(),
1034 JsonValue::String("$6000".to_owned()),
1035 );
1036 expected_map.insert("number:nps".to_owned(), json!(3.0));
1037
1038 assert_eq!(actual_map, expected_map);
1039 }
1040
1041 #[test]
1042 fn property_map_empty_deserialize() {
1043 let map: PropertyMap = serde_json::from_str("{}").expect("");
1044 assert_eq!(map, PropertyMap::new());
1045 }
1046
1047 #[test]
1048 fn property_map_null_deserialize() {
1049 let map: PropertyMap = serde_json::from_str("null").expect("");
1050 assert_eq!(map, PropertyMap::new());
1051 }
1052
1053 #[test]
1054 fn property_map_one_number_float_deserialize() {
1055 let actual: PropertyMap = serde_json::from_str(r#"{"number:nps":7.0}"#).expect("");
1056 let mut expected = PropertyMap::new();
1057 expected.insert_number("nps".to_owned(), NotNan::new(7.0).unwrap());
1058 assert_eq!(actual, expected);
1059 }
1060
1061 #[test]
1062 fn property_map_one_number_unsigned_deserialize() {
1063 let actual: PropertyMap = serde_json::from_str(r#"{"number:nps":7}"#).expect("");
1064 let mut expected = PropertyMap::new();
1065 expected.insert_number("nps".to_owned(), NotNan::new(7.0).unwrap());
1066 assert_eq!(actual, expected);
1067 }
1068
1069 #[test]
1070 fn property_map_one_number_negative_deserialize() {
1071 let actual: PropertyMap = serde_json::from_str(r#"{"number:nps":-7}"#).expect("");
1072 let mut expected = PropertyMap::new();
1073 expected.insert_number("nps".to_owned(), NotNan::new(-7.0).unwrap());
1074 assert_eq!(actual, expected);
1075 }
1076
1077 #[test]
1078 fn property_map_one_string_deserialize() {
1079 let actual: PropertyMap = serde_json::from_str(r#"{"string:age":"18-25"}"#).expect("");
1080 let mut expected = PropertyMap::new();
1081 expected.insert_string("age".to_owned(), "18-25".to_owned());
1082 assert_eq!(actual, expected);
1083 }
1084
1085 #[test]
1086 fn property_map_illegal_prefix_deserialize() {
1087 let result: StdResult<PropertyMap, _> =
1088 serde_json::from_str(r#"{"illegal:something":"18-25"}"#);
1089 assert!(result.is_err());
1090 }
1091
1092 #[test]
1093 fn property_map_illegal_value_for_prefix_deserialize() {
1094 let result: StdResult<PropertyMap, _> =
1095 serde_json::from_str(r#"{"string:something":18.0}"#);
1096 assert!(result.is_err());
1097
1098 let result: StdResult<PropertyMap, _> = serde_json::from_str(r#"{"number:something":"x"}"#);
1099 assert!(result.is_err());
1100 }
1101
1102 #[test]
1103 fn property_map_mixed_deserialize() {
1104 let mut expected = PropertyMap::new();
1105 expected.insert_string("age".to_owned(), "18-25".to_owned());
1106 expected.insert_string("income".to_owned(), "$6000".to_owned());
1107 expected.insert_number("nps".to_owned(), NotNan::new(3.0).unwrap());
1108
1109 let actual: PropertyMap = serde_json::from_str(
1110 r#"{"string:age":"18-25","number:nps":3,"string:income":"$6000"}"#,
1111 )
1112 .expect("");
1113
1114 assert_eq!(actual, expected);
1115 }
1116}