-
Notifications
You must be signed in to change notification settings - Fork 3
/
ecosystem.rs
155 lines (138 loc) · 4.3 KB
/
ecosystem.rs
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use clap::Args;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, error::Error, fs};
use toml_edit::{Array, Document, Formatted, Item, Table, Value};
#[derive(Deserialize)]
struct EcosystemSrc {
topics: HashMap<String, TopicSrc>,
#[serde(rename = "project")]
projects: Vec<ProjectSrc>,
showcase: Vec<ShowcaseExhibit>,
}
pub struct Ecosystem {
pub topics: Vec<Topic>,
pub projects: Vec<Project>,
pub showcase: Vec<ShowcaseExhibit>,
}
#[derive(Args, Clone, Deserialize)]
pub struct ProjectSrc {
/// The name of the project
pub name: String,
/// The description of the project
#[clap(long, short = 'D')]
pub description: Option<String>,
/// A link to the project's repository
#[clap(long, short = 'R')]
pub repo: Option<String>,
/// Crates which are part of this project
#[clap(name = "CRATE", long = "crate", short = 'c')]
pub crates: Option<Vec<String>>,
// in case there are separate docs, apart from the crates
/// Supply a link to docs not hosted on docs.rs
#[clap(long, short = 'd')]
pub docs: Option<String>,
/// Topics which this crate falls under (e.g. lexing, parsing)
#[clap(name = "TOPIC", long = "topic", short = 't')]
pub topics: Vec<String>,
}
#[derive(Debug, Serialize)]
pub struct Project {
pub name: String,
pub description: Option<String>,
pub repo: Option<String>,
// we have both so that if there's only one, we can collapse it
pub only_crate: Option<String>,
pub crates: Option<Vec<String>>,
pub docs: Option<String>,
pub topics: Vec<String>,
}
impl From<ProjectSrc> for Project {
fn from(src: ProjectSrc) -> Self {
let (only_crate, crates) = if let Some(crates) = src.crates {
if crates.len() == 1 {
(Some(crates[0].clone()), None)
} else {
(None, Some(crates))
}
} else {
(None, None)
};
Self {
name: src.name,
description: src.description,
repo: src.repo,
only_crate,
crates,
docs: src.docs,
topics: src.topics,
}
}
}
#[derive(Deserialize)]
struct TopicSrc {
name: String,
description: String,
}
#[derive(Serialize, Clone)]
pub struct Topic {
pub id: String,
pub name: String,
pub description: String,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ShowcaseExhibit {
pub name: String,
pub repo: Option<String>,
pub description: Option<String>,
pub crates: Option<Vec<String>>,
pub docs: Option<String>,
}
pub fn parse(source: &str) -> Result<Ecosystem, Box<dyn Error>> {
let parsed_data: EcosystemSrc = toml_edit::easy::from_slice(&fs::read(source)?)?;
let mut topics: Vec<Topic> = parsed_data
.topics
.iter()
.map(|(k, v)| Topic {
id: k.clone(),
name: v.name.clone(),
description: v.description.clone(),
})
.collect();
topics.sort_by(|a, b| a.id.to_lowercase().cmp(&b.id.to_lowercase()));
let projects = parsed_data.projects.into_iter().map(Into::into).collect();
Ok(Ecosystem {
projects,
topics,
showcase: parsed_data.showcase,
})
}
pub fn add_project(source: &str, project: &ProjectSrc) -> Result<String, Box<dyn Error>> {
let mut doc = fs::read_to_string(source)?.parse::<Document>()?;
macro_rules! value_str {
($value:expr) => {
Item::Value(Value::String(Formatted::new($value.to_string())))
};
}
macro_rules! value_arr {
($value:expr) => {
Item::Value(Value::Array(Array::from_iter($value)))
};
}
let mut table = Table::new();
table.insert("name", value_str!(&project.name));
if let Some(description) = &project.description {
table.insert("description", value_str!(description));
}
if let Some(repo) = &project.repo {
table.insert("repo", value_str!(repo));
}
if let Some(crates) = &project.crates {
table.insert("crates", value_arr!(crates));
}
if let Some(docs) = &project.docs {
table.insert("docs", value_str!(docs));
}
table.insert("topics", value_arr!(&project.topics));
doc["project"].as_array_of_tables_mut().unwrap().push(table);
Ok(doc.to_string())
}