bucky_objects/objects/
group.rs

1use itertools::Itertools;
2
3use crate::*;
4
5use std::collections::{HashMap, HashSet};
6use std::convert::TryFrom;
7use std::str::FromStr;
8use bucky_error::{BuckyError, BuckyErrorCode, BuckyResult};
9use crate::protos::standard_objects;
10
11pub enum GroupMemberScope {
12    Admin,
13    Member,
14    All,
15}
16
17#[derive(Clone, Debug, RawEncode, RawDecode)]
18pub enum GroupDescContent {
19    SimpleGroup(SimpleGroupDescContent),
20    Org(OrgDescContent),
21}
22
23impl GroupDescContent {
24    pub fn founder_id(&self) -> &Option<ObjectId> {
25        match self {
26            GroupDescContent::SimpleGroup(desc) => &desc.founder_id,
27            GroupDescContent::Org(desc) => &desc.founder_id,
28        }
29    }
30}
31
32#[derive(Clone, Debug, RawEncode, RawDecode)]
33pub enum GroupBodyContent {
34    SimpleGroup(SimpleGroupBodyContent),
35    Org(OrgBodyContent),
36}
37
38impl DescContent for GroupDescContent {
39    fn obj_type() -> u16 {
40        ObjectTypeCode::Group.into()
41    }
42
43    type OwnerType = SubDescNone;
44    type AreaType = Option<Area>;
45    type AuthorType = SubDescNone;
46    type PublicKeyType = SubDescNone;
47}
48
49impl BodyContent for GroupBodyContent {
50    fn format(&self) -> u8 {
51        OBJECT_CONTENT_CODEC_FORMAT_RAW
52    }
53}
54
55impl GroupBodyContent {
56    pub fn name(&self) -> &Option<String> {
57        &self.common().name
58    }
59
60    pub fn icon(&self) -> &Option<String> {
61        &self.common().icon
62    }
63
64    pub fn description(&self) -> &Option<String> {
65        &self.common().description
66    }
67
68    pub fn members(&self) -> &HashMap<ObjectId, GroupMember> {
69        &self.common().members
70    }
71
72    pub fn members_mut(&mut self) -> &mut HashMap<ObjectId, GroupMember> {
73        &mut self.common_mut().members
74    }
75
76    pub fn ood_list(&self) -> &Vec<DeviceId> {
77        &self.common().ood_list
78    }
79
80    pub fn ood_list_mut(&mut self) -> &mut Vec<DeviceId> {
81        &mut self.common_mut().ood_list
82    }
83
84    pub fn version(&self) -> u64 {
85        self.common().version
86    }
87
88    pub fn prev_shell_id(&self) -> &Option<ObjectId> {
89        &self.common().prev_shell_id
90    }
91
92    fn common(&self) -> &CommonGroupBodyContent {
93        match self {
94            GroupBodyContent::Org(body) => &body.common,
95            GroupBodyContent::SimpleGroup(body) => &body.common,
96        }
97    }
98
99    fn common_mut(&mut self) -> &mut CommonGroupBodyContent {
100        match self {
101            GroupBodyContent::Org(body) => &mut body.common,
102            GroupBodyContent::SimpleGroup(body) => &mut body.common,
103        }
104    }
105}
106
107pub type GroupType = NamedObjType<GroupDescContent, GroupBodyContent>;
108pub type GroupBuilder = NamedObjectBuilder<GroupDescContent, GroupBodyContent>;
109
110pub type GroupDesc = NamedObjectDesc<GroupDescContent>;
111pub type GroupId = NamedObjectId<GroupType>;
112pub type Group = NamedObjectBase<GroupType>;
113
114impl GroupDesc {
115    pub fn group_id(&self) -> GroupId {
116        GroupId::try_from(self.calculate_id()).unwrap()
117    }
118}
119
120impl Group {
121    pub fn new_simple_group(
122        founder_id: Option<ObjectId>,
123        admins: Vec<GroupMember>,
124        area: Area,
125    ) -> GroupBuilder {
126        let desc_content = SimpleGroupDescContent {
127            unique_id: UniqueId::create_with_random(),
128            admins: HashMap::from_iter(admins.into_iter().map(|m| (m.id, m))),
129            founder_id,
130        };
131
132        let body_content = SimpleGroupBodyContent::default();
133
134        GroupBuilder::new(
135            GroupDescContent::SimpleGroup(desc_content),
136            GroupBodyContent::SimpleGroup(body_content),
137        )
138        .area(area)
139    }
140
141    pub fn new_org(founder_id: Option<ObjectId>, area: Area) -> GroupBuilder {
142        let desc_content = OrgDescContent {
143            founder_id,
144            unique_id: UniqueId::create_with_random(),
145        };
146
147        let body_content = OrgBodyContent::default();
148
149        GroupBuilder::new(
150            GroupDescContent::Org(desc_content),
151            GroupBodyContent::Org(body_content),
152        )
153        .area(area)
154    }
155
156    pub fn founder_id(&self) -> &Option<ObjectId> {
157        match self.desc().content() {
158            GroupDescContent::SimpleGroup(s) => &s.founder_id,
159            GroupDescContent::Org(o) => &o.founder_id,
160        }
161    }
162
163    pub fn name(&self) -> &Option<String> {
164        &self.common().name
165    }
166
167    pub fn set_name(&mut self, name: Option<String>) {
168        self.common_mut().name = name;
169    }
170
171    pub fn icon(&self) -> &Option<String> {
172        &self.common().icon
173    }
174
175    pub fn set_icon(&mut self, icon: Option<String>) {
176        self.common_mut().icon = icon;
177    }
178
179    pub fn description(&self) -> &Option<String> {
180        &self.common().description
181    }
182
183    pub fn set_description(&mut self, description: Option<String>) {
184        self.common_mut().description = description;
185    }
186
187    pub fn admins(&self) -> &HashMap<ObjectId, GroupMember> {
188        if self.is_org() {
189            &self.check_org_body_content().admins
190        } else {
191            &self.check_simple_group_desc_content().admins
192        }
193    }
194
195    pub fn members(&self) -> &HashMap<ObjectId, GroupMember> {
196        &self.common().members
197    }
198
199    pub fn set_members(&mut self, members: Vec<GroupMember>) {
200        self.common_mut().members = HashMap::from_iter(members.into_iter().map(|m| (m.id, m)));
201    }
202
203    pub fn ood_list(&self) -> &Vec<DeviceId> {
204        &self.common().ood_list
205    }
206
207    pub fn set_ood_list(&mut self, oods: Vec<DeviceId>) {
208        self.common_mut().ood_list = HashSet::<DeviceId>::from_iter(oods.into_iter())
209            .into_iter()
210            .sorted()
211            .collect();
212    }
213
214    pub fn contain_ood(&self, ood_id: &ObjectId) -> bool {
215        match DeviceId::try_from(ood_id) {
216            Ok(device_id) => self.ood_list().contains(&device_id),
217            Err(_) => false,
218        }
219    }
220
221    pub fn is_same_ood_list(&self, other: &Group) -> bool {
222        let my_oods = self.ood_list();
223        let other_oods = other.ood_list();
224
225        if my_oods.len() != other_oods.len() {
226            return false;
227        }
228
229        for id in my_oods {
230            if !other_oods.contains(id) {
231                return false;
232            }
233        }
234
235        true
236    }
237
238    pub fn version(&self) -> u64 {
239        self.common().version
240    }
241
242    pub fn set_version(&mut self, version: u64) {
243        self.common_mut().version = version;
244    }
245
246    pub fn prev_shell_id(&self) -> &Option<ObjectId> {
247        &self.common().prev_shell_id
248    }
249
250    pub fn set_prev_shell_id(&mut self, prev_shell_id: Option<ObjectId>) {
251        self.common_mut().prev_shell_id = prev_shell_id;
252    }
253
254    pub fn is_simple_group(&self) -> bool {
255        match self.desc().content() {
256            GroupDescContent::SimpleGroup(_) => true,
257            _ => false,
258        }
259    }
260
261    pub fn is_org(&self) -> bool {
262        match self.desc().content() {
263            GroupDescContent::Org(_) => true,
264            _ => false,
265        }
266    }
267
268    pub fn check_simple_group_desc_content(&self) -> &SimpleGroupDescContent {
269        match self.desc().content() {
270            GroupDescContent::SimpleGroup(desc) => desc,
271            _ => panic!("group type not match, expect: simple"),
272        }
273    }
274
275    pub fn check_org_desc_content(&self) -> &OrgDescContent {
276        match self.desc().content() {
277            GroupDescContent::Org(desc) => desc,
278            _ => panic!("group type not match, expect: org"),
279        }
280    }
281
282    pub fn check_simple_group_body_content(&self) -> &SimpleGroupBodyContent {
283        match self.body().as_ref().unwrap().content() {
284            GroupBodyContent::SimpleGroup(body) => body,
285            _ => panic!("group type not match, expect: simple"),
286        }
287    }
288
289    pub fn check_org_body_content(&self) -> &OrgBodyContent {
290        match self.body().as_ref().unwrap().content() {
291            GroupBodyContent::Org(body) => body,
292            _ => panic!("group type not match, expect: org"),
293        }
294    }
295
296    pub fn check_simple_group_body_content_mut(&mut self) -> &mut SimpleGroupBodyContent {
297        match self.body_mut().as_mut().unwrap().content_mut() {
298            GroupBodyContent::SimpleGroup(body) => body,
299            _ => panic!("group type not match, expect: simple"),
300        }
301    }
302
303    pub fn check_org_body_content_mut(&mut self) -> &mut OrgBodyContent {
304        match self.body_mut().as_mut().unwrap().content_mut() {
305            GroupBodyContent::Org(body) => body,
306            _ => panic!("group type not match, expect: org"),
307        }
308    }
309
310    pub fn select_members_with_distance(
311        &self,
312        target: &ObjectId,
313        scope: GroupMemberScope,
314    ) -> Vec<&ObjectId> {
315        let mut members = match scope {
316            GroupMemberScope::Admin => self.admins().keys().collect::<Vec<_>>(),
317            GroupMemberScope::Member => self.members().keys().collect::<Vec<_>>(),
318            GroupMemberScope::All => [
319                self.admins().keys().collect::<Vec<_>>(),
320                self.members().keys().collect::<Vec<_>>(),
321            ]
322            .concat(),
323        };
324
325        members.sort_unstable_by(|l, r| {
326            let dl = l.distance_of(target);
327            let dr = r.distance_of(target);
328            dl.cmp(&dr)
329        });
330        members
331    }
332
333    pub fn ood_list_with_distance(&self, target: &ObjectId) -> Vec<&ObjectId> {
334        let oods = self
335            .ood_list()
336            .iter()
337            .map(|id| id.object_id())
338            .sorted_unstable_by(|l, r| {
339                let dl = l.distance_of(target);
340                let dr = r.distance_of(target);
341                dl.cmp(&dr)
342            })
343            .collect::<Vec<_>>();
344        oods
345    }
346
347    fn common(&self) -> &CommonGroupBodyContent {
348        self.body().as_ref().unwrap().content().common()
349    }
350
351    fn common_mut(&mut self) -> &mut CommonGroupBodyContent {
352        self.body_mut().as_mut().unwrap().content_mut().common_mut()
353    }
354}
355
356#[derive(Clone, Debug)]
357pub struct GroupMember {
358    pub id: ObjectId,
359    pub title: String,
360}
361
362impl GroupMember {
363    pub fn new(id: ObjectId, title: String) -> Self {
364        GroupMember { id, title }
365    }
366    pub fn from_member_id(id: ObjectId) -> GroupMember {
367        GroupMember {
368            id,
369            title: "".to_string(),
370        }
371    }
372}
373
374impl TryFrom<protos::GroupMember> for GroupMember {
375    type Error = BuckyError;
376
377    fn try_from(value: protos::GroupMember) -> BuckyResult<Self> {
378        let ret = Self {
379            id: ProtobufCodecHelper::decode_buf(value.id)?,
380            title: value.title,
381        };
382
383        Ok(ret)
384    }
385}
386
387impl TryFrom<&GroupMember> for protos::GroupMember {
388    type Error = BuckyError;
389
390    fn try_from(value: &GroupMember) -> BuckyResult<Self> {
391        let mut ret = Self::new();
392
393        ret.id = value.id.to_vec()?;
394        ret.title = value.title.clone();
395
396        Ok(ret)
397    }
398}
399
400impl FromStr for GroupMember {
401    type Err = BuckyError;
402
403    fn from_str(s: &str) -> Result<Self, Self::Err> {
404        let mut fields = s.split(":");
405
406        let id = if let Some(id) = fields.next() {
407            PeopleId::from_str(id)?
408        } else {
409            return Err(BuckyError::new(
410                BuckyErrorCode::InvalidFormat,
411                "need peopleid of member.",
412            ));
413        };
414
415        let title = fields.next().unwrap_or("");
416
417        Ok(Self {
418            id: id.object_id().clone(),
419            title: title.to_string(),
420        })
421    }
422}
423
424impl ToString for &GroupMember {
425    fn to_string(&self) -> String {
426        format!("{}:{}", self.id, self.title)
427    }
428}
429
430#[derive(Clone, Debug, Default)]
431struct CommonGroupBodyContent {
432    name: Option<String>,
433    icon: Option<String>,
434    description: Option<String>,
435
436    members: HashMap<ObjectId, GroupMember>,
437
438    ood_list: Vec<DeviceId>,
439
440    version: u64,
441    prev_shell_id: Option<ObjectId>,
442}
443
444impl CommonGroupBodyContent {
445    fn new(
446        name: Option<String>,
447        icon: Option<String>,
448        description: Option<String>,
449        members: Vec<GroupMember>,
450        ood_list: Vec<DeviceId>,
451    ) -> Self {
452        Self {
453            name,
454            icon,
455            description,
456            members: HashMap::from_iter(members.into_iter().map(|m| (m.id, m))),
457            ood_list: HashSet::<DeviceId>::from_iter(ood_list.into_iter())
458                .into_iter()
459                .sorted()
460                .collect::<Vec<_>>(),
461            version: 0,
462            prev_shell_id: None,
463        }
464    }
465}
466
467impl TryFrom<protos::CommonGroupBodyContent> for CommonGroupBodyContent {
468    type Error = BuckyError;
469
470    fn try_from(mut value: protos::CommonGroupBodyContent) -> BuckyResult<Self> {
471        let mut ood_list = ProtobufCodecHelper::decode_buf_list(value.take_ood_list())?;
472        ood_list.sort();
473
474        let ret = Self {
475            name: if value.has_name() {
476                Some(value.take_name())
477            } else {
478                None
479            },
480            icon: if value.has_icon() {
481                Some(value.take_icon())
482            } else {
483                None
484            },
485            description: if value.has_description() {
486                Some(value.take_description())
487            } else {
488                None
489            },
490            members:
491                HashMap::from_iter(
492                    ProtobufCodecHelper::decode_value_list::<
493                        GroupMember,
494                        standard_objects::GroupMember,
495                    >(value.take_members())?
496                    .into_iter()
497                    .map(|m| (m.id, m)),
498                ),
499            ood_list,
500            version: value.version,
501            prev_shell_id: if value.has_prev_shell_id() {
502                Some(ProtobufCodecHelper::decode_buf(value.take_prev_shell_id())?)
503            } else {
504                None
505            },
506        };
507
508        Ok(ret)
509    }
510}
511
512impl TryFrom<&CommonGroupBodyContent> for protos::CommonGroupBodyContent {
513    type Error = BuckyError;
514
515    fn try_from(value: &CommonGroupBodyContent) -> BuckyResult<Self> {
516        let mut ret = Self::new();
517
518        if let Some(name) = value.name.as_ref() {
519            ret.set_name(name.clone());
520        }
521        if let Some(icon) = value.icon.as_ref() {
522            ret.set_icon(icon.clone());
523        }
524        if let Some(description) = value.description.as_ref() {
525            ret.set_description(description.clone());
526        }
527
528        let members = value
529            .members
530            .values()
531            .sorted_by(|l, r| l.id.cmp(&r.id))
532            .map(|m| m.clone())
533            .collect::<Vec<_>>();
534        ret.set_members(ProtobufCodecHelper::encode_nested_list(&members)?);
535
536        let oods = value
537            .ood_list
538            .iter()
539            .sorted()
540            .map(|id| id.clone())
541            .collect::<Vec<_>>();
542        ret.set_ood_list(ProtobufCodecHelper::encode_buf_list(oods.as_slice())?);
543
544        ret.version = value.version;
545        if let Some(prev_shell_id) = &value.prev_shell_id {
546            ret.set_prev_shell_id(prev_shell_id.to_vec()?);
547        }
548
549        Ok(ret)
550    }
551}
552
553#[derive(Clone, Debug)]
554pub struct SimpleGroupDescContent {
555    unique_id: UniqueId,
556    founder_id: Option<ObjectId>,
557    admins: HashMap<ObjectId, GroupMember>,
558}
559
560impl SimpleGroupDescContent {
561    pub fn admins(&self) -> &HashMap<ObjectId, GroupMember> {
562        &self.admins
563    }
564}
565
566impl TryFrom<protos::SimpleGroupDescContent> for SimpleGroupDescContent {
567    type Error = BuckyError;
568
569    fn try_from(mut value: protos::SimpleGroupDescContent) -> BuckyResult<Self> {
570        let ret = Self {
571            founder_id: if value.has_founder_id() {
572                ProtobufCodecHelper::decode_buf(value.take_founder_id())?
573            } else {
574                None
575            },
576            unique_id: ProtobufCodecHelper::decode_buf(value.unique_id)?,
577            admins:
578                HashMap::from_iter(
579                    ProtobufCodecHelper::decode_value_list::<
580                        GroupMember,
581                        standard_objects::GroupMember,
582                    >(value.admins)?
583                    .into_iter()
584                    .map(|m| (m.id, m)),
585                ),
586        };
587
588        Ok(ret)
589    }
590}
591
592impl TryFrom<&SimpleGroupDescContent> for protos::SimpleGroupDescContent {
593    type Error = BuckyError;
594
595    fn try_from(value: &SimpleGroupDescContent) -> BuckyResult<Self> {
596        let mut ret = Self::new();
597
598        ret.unique_id = value.unique_id.to_vec()?;
599        if let Some(founder_id) = value.founder_id.as_ref() {
600            ret.set_founder_id(founder_id.to_vec()?);
601        }
602
603        let admins = value
604            .admins
605            .values()
606            .sorted_by(|l, r| l.id.cmp(&r.id))
607            .map(|m| m.clone())
608            .collect::<Vec<_>>();
609        ret.set_admins(ProtobufCodecHelper::encode_nested_list(&admins)?);
610
611        Ok(ret)
612    }
613}
614
615#[derive(Clone, Debug, Default)]
616pub struct SimpleGroupBodyContent {
617    common: CommonGroupBodyContent,
618}
619
620impl SimpleGroupBodyContent {
621    fn new(
622        name: Option<String>,
623        icon: Option<String>,
624        description: Option<String>,
625        members: Vec<GroupMember>,
626        ood_list: Vec<DeviceId>,
627    ) -> Self {
628        Self {
629            common: CommonGroupBodyContent::new(name, icon, description, members, ood_list),
630        }
631    }
632}
633
634impl TryFrom<protos::SimpleGroupBodyContent> for SimpleGroupBodyContent {
635    type Error = BuckyError;
636
637    fn try_from(mut value: protos::SimpleGroupBodyContent) -> BuckyResult<Self> {
638        let ret = Self {
639            common: ProtobufCodecHelper::decode_value(value.take_common())?,
640        };
641
642        Ok(ret)
643    }
644}
645
646impl TryFrom<&SimpleGroupBodyContent> for protos::SimpleGroupBodyContent {
647    type Error = BuckyError;
648
649    fn try_from(value: &SimpleGroupBodyContent) -> BuckyResult<Self> {
650        let mut ret = Self::new();
651
652        ret.set_common(ProtobufCodecHelper::encode_nested_item(&value.common)?);
653
654        Ok(ret)
655    }
656}
657
658#[derive(Clone, Debug)]
659pub struct OrgDescContent {
660    unique_id: UniqueId,
661    founder_id: Option<ObjectId>,
662}
663
664impl TryFrom<protos::OrgDescContent> for OrgDescContent {
665    type Error = BuckyError;
666
667    fn try_from(mut value: protos::OrgDescContent) -> BuckyResult<Self> {
668        let ret = Self {
669            founder_id: if value.has_founder_id() {
670                Some(ProtobufCodecHelper::decode_buf(value.take_founder_id())?)
671            } else {
672                None
673            },
674            unique_id: ProtobufCodecHelper::decode_buf(value.unique_id)?,
675        };
676
677        Ok(ret)
678    }
679}
680
681impl TryFrom<&OrgDescContent> for protos::OrgDescContent {
682    type Error = BuckyError;
683
684    fn try_from(value: &OrgDescContent) -> BuckyResult<Self> {
685        let mut ret = Self::new();
686
687        ret.unique_id = value.unique_id.to_vec()?;
688        if let Some(founder_id) = value.founder_id.as_ref() {
689            ret.set_founder_id(founder_id.to_vec()?);
690        }
691
692        Ok(ret)
693    }
694}
695
696#[derive(Clone, Debug, Default)]
697pub struct OrgBodyContent {
698    admins: HashMap<ObjectId, GroupMember>,
699    common: CommonGroupBodyContent,
700}
701
702impl OrgBodyContent {
703    fn new(
704        name: Option<String>,
705        icon: Option<String>,
706        description: Option<String>,
707        admins: Vec<GroupMember>,
708        members: Vec<GroupMember>,
709        ood_list: Vec<DeviceId>,
710    ) -> Self {
711        Self {
712            common: CommonGroupBodyContent::new(name, icon, description, members, ood_list),
713            admins: HashMap::from_iter(admins.into_iter().map(|m| (m.id, m))),
714        }
715    }
716
717    pub fn admins(&self) -> &HashMap<ObjectId, GroupMember> {
718        &self.admins
719    }
720
721    pub fn set_admins(&mut self, admins: Vec<GroupMember>) {
722        self.admins = HashMap::from_iter(admins.into_iter().map(|m| (m.id, m)));
723    }
724}
725
726impl TryFrom<protos::OrgBodyContent> for OrgBodyContent {
727    type Error = BuckyError;
728
729    fn try_from(mut value: protos::OrgBodyContent) -> BuckyResult<Self> {
730        let ret = Self {
731            admins:
732                HashMap::from_iter(
733                    ProtobufCodecHelper::decode_value_list::<
734                        GroupMember,
735                        standard_objects::GroupMember,
736                    >(value.take_admins())?
737                    .into_iter()
738                    .map(|m| (m.id, m)),
739                ),
740            common: ProtobufCodecHelper::decode_value(value.take_common())?,
741        };
742
743        Ok(ret)
744    }
745}
746
747impl TryFrom<&OrgBodyContent> for protos::OrgBodyContent {
748    type Error = BuckyError;
749
750    fn try_from(value: &OrgBodyContent) -> BuckyResult<Self> {
751        let mut ret = Self::new();
752
753        let admins = value
754            .admins
755            .values()
756            .sorted_by(|l, r| l.id.cmp(&r.id))
757            .map(|m| m.clone())
758            .collect::<Vec<_>>();
759
760        ret.set_admins(ProtobufCodecHelper::encode_nested_list(&admins)?);
761        ret.set_common(ProtobufCodecHelper::encode_nested_item(&value.common)?);
762
763        Ok(ret)
764    }
765}
766
767crate::inner_impl_default_protobuf_raw_codec!(SimpleGroupDescContent);
768crate::inner_impl_default_protobuf_raw_codec!(SimpleGroupBodyContent);
769
770crate::inner_impl_default_protobuf_raw_codec!(OrgDescContent);
771crate::inner_impl_default_protobuf_raw_codec!(OrgBodyContent);
772
773#[cfg(test)]
774mod test {
775    use crate::*;
776
777    #[test]
778    fn simple_group() {
779        // let threshold = 0;
780
781        // let members = vec![ObjectId::default()];
782
783        // let ood_list = vec![DeviceId::default()];
784
785        // let obj = SimpleGroup::new(
786        //     threshold,
787        //     vec![],
788        //     members,
789        //     OODWorkMode::Standalone,
790        //     ood_list,
791        //     Area::default(),
792        // )
793        // .build();
794        // // let p = Path::new("f:\\temp\\simple_group.obj");
795        // // if p.parent().unwrap().exists() {
796        // //     obj.clone().encode_to_file(p, false);
797        // // }
798
799        // let buf = obj.to_vec().unwrap();
800
801        // let decode_obj = SimpleGroup::clone_from_slice(&buf).unwrap();
802
803        // assert!(obj.desc().simple_group_id() == decode_obj.desc().simple_group_id());
804    }
805}