Skip to content

Commit f654625

Browse files
committed
feat: add back the api_server with portable
1 parent db0e90d commit f654625

File tree

5 files changed

+204
-3
lines changed

5 files changed

+204
-3
lines changed

Cargo.lock

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[workspace]
22
members = [
33
"crates/api_server",
4+
"crates/api_server_portable",
45
"crates/cli",
56
"crates/demo",
67
"crates/dt_core",
@@ -25,4 +26,4 @@ swc_core = { version = "0.104.2", features = ["common", "ecma_ast", "ecma
2526
swc_ecma_parser = { version = "0.150.0", features = ["typescript"] }
2627
clap = { version = "4.5", features = ["derive"] }
2728
rusqlite = { version = "0.32.1", features = ["bundled"] }
28-
indicatif = "0.17.8"
29+
indicatif = "0.17.8"

crates/api_server/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ version = "0.1.0"
77

88

99
[dependencies]
10-
anyhow = { workspace = true }
1110
serde = { workspace = true }
1211
clap = { workspace = true }
1312

crates/api_server_portable/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
authors = ["Leo Lin <[email protected]>"]
3+
description = "run the tracker and provide APIs"
4+
edition = "2021"
5+
name = "api_server_portable"
6+
version = "0.1.0"
7+
8+
9+
[dependencies]
10+
serde = { workspace = true }
11+
clap = { workspace = true }
12+
13+
actix-web = "4"
14+
actix-cors = "0.7"
15+
16+
dt_core = { version = "0.1.0", path = "../dt_core" }
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
use actix_cors::Cors;
2+
use actix_web::{error, get, web, App, HttpServer, Result};
3+
use clap::Parser;
4+
use dt_core::{
5+
graph::used_by_graph::UsedByGraph,
6+
portable::Portable,
7+
tracker::{DependencyTracker, TraceTarget},
8+
};
9+
use serde::{Deserialize, Serialize};
10+
use std::{
11+
collections::{HashMap, HashSet},
12+
fs::File,
13+
io::Read,
14+
};
15+
16+
struct AppState {
17+
project_root: String,
18+
translation_json: HashMap<String, String>,
19+
i18n_to_symbol: HashMap<String, HashMap<String, HashSet<String>>>,
20+
symbol_to_route: HashMap<String, HashMap<String, Vec<String>>>,
21+
used_by_graph: UsedByGraph,
22+
}
23+
24+
#[derive(Serialize, Clone)]
25+
struct Step {
26+
module_path: String,
27+
symbol_name: String,
28+
}
29+
30+
#[derive(Serialize)]
31+
struct SearchResponse {
32+
project_root: String,
33+
trace_result: HashMap<String, HashMap<String, HashMap<String, Vec<Vec<Step>>>>>,
34+
}
35+
36+
#[derive(Deserialize)]
37+
struct Info {
38+
q: String,
39+
exact_match: bool,
40+
}
41+
42+
#[get("/search")]
43+
async fn search(
44+
data: web::Data<AppState>,
45+
info: web::Query<Info>,
46+
) -> Result<web::Json<SearchResponse>> {
47+
let search = &info.q;
48+
let exact_match = info.exact_match;
49+
50+
let mut matched_i18n_keys: Vec<String> = Vec::new();
51+
match exact_match {
52+
true => {
53+
for (i18n_key, translation) in data.translation_json.iter() {
54+
if translation == search {
55+
matched_i18n_keys.push(i18n_key.to_owned());
56+
}
57+
}
58+
}
59+
false => {
60+
for (i18n_key, translation) in data.translation_json.iter() {
61+
if translation.contains(search) {
62+
matched_i18n_keys.push(i18n_key.to_owned());
63+
}
64+
}
65+
}
66+
}
67+
68+
if matched_i18n_keys.len() == 0 {
69+
return Err(error::ErrorNotFound(format!("No result for {}", search)));
70+
}
71+
72+
let mut dependency_tracker = DependencyTracker::new(&data.used_by_graph, true);
73+
let mut trace_result = HashMap::new();
74+
for i18n_key in matched_i18n_keys.iter() {
75+
let mut route_to_paths = HashMap::new();
76+
if let Some(i18n_key_usage) = data.i18n_to_symbol.get(i18n_key) {
77+
for (module_path, symbols) in i18n_key_usage {
78+
for symbol in symbols {
79+
let full_paths = dependency_tracker
80+
.trace((module_path.clone(), TraceTarget::LocalVar(symbol.clone())))
81+
.unwrap();
82+
// traverse each path and check if any symbol is used in some routes
83+
for mut full_path in full_paths {
84+
full_path.reverse();
85+
for (i, (step_module_path, step_trace_target)) in
86+
full_path.iter().enumerate()
87+
{
88+
match step_trace_target {
89+
TraceTarget::LocalVar(step_symbol_name) => {
90+
if let Some(symbol_to_routes) =
91+
data.symbol_to_route.get(step_module_path)
92+
{
93+
if let Some(routes) = symbol_to_routes.get(step_symbol_name)
94+
{
95+
let dependency_from_target_to_route: Vec<Step> =
96+
full_path[0..i]
97+
.iter()
98+
.map(|(path, target)| Step {
99+
module_path: path.clone(),
100+
symbol_name: target.to_string(),
101+
})
102+
.collect();
103+
for route in routes.iter() {
104+
if !route_to_paths.contains_key(route) {
105+
route_to_paths
106+
.insert(route.clone(), HashMap::new());
107+
}
108+
if !route_to_paths
109+
.get(route)
110+
.unwrap()
111+
.contains_key(symbol)
112+
{
113+
route_to_paths
114+
.get_mut(route)
115+
.unwrap()
116+
.insert(symbol.to_string(), vec![]);
117+
}
118+
route_to_paths
119+
.get_mut(route)
120+
.unwrap()
121+
.get_mut(symbol)
122+
.unwrap()
123+
.push(dependency_from_target_to_route.clone());
124+
}
125+
}
126+
}
127+
}
128+
_ => (),
129+
}
130+
}
131+
}
132+
}
133+
}
134+
}
135+
trace_result.insert(i18n_key.to_string(), route_to_paths);
136+
}
137+
138+
Ok(web::Json(SearchResponse {
139+
project_root: data.project_root.to_owned(),
140+
trace_result,
141+
}))
142+
}
143+
144+
#[derive(Parser)]
145+
#[command(version, about = "Start the server to provide search API", long_about = None)]
146+
struct Cli {
147+
/// Portable path
148+
#[arg(short)]
149+
portable: String,
150+
}
151+
152+
#[actix_web::main]
153+
async fn main() -> std::io::Result<()> {
154+
let cli = Cli::parse();
155+
let mut file = File::open(cli.portable)?;
156+
let mut exported = String::new();
157+
file.read_to_string(&mut exported)?;
158+
let portable = Portable::import(&exported).unwrap();
159+
160+
HttpServer::new(move || {
161+
App::new()
162+
.wrap(Cors::default().allow_any_method().allow_any_origin())
163+
.app_data(web::Data::new(AppState {
164+
project_root: portable.project_root.clone(),
165+
translation_json: portable.translation_json.clone(),
166+
i18n_to_symbol: portable.i18n_to_symbol.clone(),
167+
symbol_to_route: portable.symbol_to_route.clone(),
168+
used_by_graph: portable.used_by_graph.clone(),
169+
}))
170+
.service(search)
171+
})
172+
.bind(("127.0.0.1", 8080))?
173+
.run()
174+
.await
175+
}

0 commit comments

Comments
 (0)