1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use std::{
    cmp::Ordering,
    collections::{BTreeMap, BTreeSet},
    fmt::{Display, Formatter},
};

use indexmap::IndexMap;
use serde::{Deserialize, Serialize};

use vos_error::{for_3rd::EmailAddress, VosResult};

use crate::*;

pub mod authors;
pub mod document;
pub mod edition;
pub mod license;
pub mod objects;

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Project {
    pub kind: ProjectKind,
    pub license: ProjectLicense,
    pub edition: ProjectEdition,
    pub authors: BTreeSet<ProjectAuthor>,
    pub description: Document,
    pub extra: BTreeMap<String, Object>,
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum ProjectKind {
    Server,
    Client,
    Library,
}

impl Default for ProjectKind {
    fn default() -> Self {
        Self::Library
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Module {
    schemas: IndexMap<String, Schema>,
    objects: IndexMap<String, Object>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Schema {
    String(StringConstraint),
    Integer(IntegerConstraint),
    Decimal(DecimalConstraint),
    List(ListConstraint),
    Dict(DictConstraint),
}

impl Project {
    pub fn extra<K, V>(&mut self, key: K, value: V) -> Option<Object>
    where
        K: Into<String>,
        V: Into<Object>,
    {
        self.extra.insert(key.into(), value.into())
    }
    pub fn document(&mut self, document: &str) {
        if document.trim().is_empty() {
            return;
        }
        if !self.description.text.is_empty() {
            self.description.text.push('\n')
        }
        self.description.text.push_str(document);
    }
}