Skip to content

Commit

Permalink
feat: use db to trace paths
Browse files Browse the repository at this point in the history
  • Loading branch information
wtlin1228 committed Oct 11, 2024
1 parent e44c781 commit 4994621
Show file tree
Hide file tree
Showing 8 changed files with 357 additions and 53 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 94 additions & 4 deletions crates/api_server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ use actix_cors::Cors;
use actix_web::{error, get, web, App, HttpServer, Result};
use clap::Parser;
use dt_core::{
database::{models, Database, SqliteDb},
graph::used_by_graph::UsedByGraph,
portable::Portable,
tracker::{DependencyTracker, TraceTarget},
tracker::{
db_version::DependencyTracker as DependencyTrackerV2, DependencyTracker, TraceTarget,
},
};
use serde::{Deserialize, Serialize};
use std::{
Expand Down Expand Up @@ -39,8 +42,8 @@ struct Info {
exact_match: bool,
}

#[get("/search")]
async fn search(
#[get("/search/in-memory")]
async fn search_in_memory(
data: web::Data<AppState>,
info: web::Query<Info>,
) -> Result<web::Json<SearchResponse>> {
Expand Down Expand Up @@ -141,6 +144,92 @@ async fn search(
}))
}

#[get("/search/db")]
async fn search_db(info: web::Query<Info>) -> Result<web::Json<SearchResponse>> {
let search = &info.q;
let exact_match = info.exact_match;
// TODO: share the db connection
let db = SqliteDb::open("./database/1010.db3").unwrap();
let project = models::Project::retrieve_by_name(&db.conn, "kirby").unwrap();
let matched_i18n_keys = project
.search_translation(&db.conn, search, exact_match)
.unwrap();
if matched_i18n_keys.len() == 0 {
return Err(error::ErrorNotFound(format!("No result for {}", search)));
}
let mut dependency_tracker = DependencyTrackerV2::new(&db, project.clone(), true);
let mut trace_result = HashMap::new();
for translation in matched_i18n_keys.iter() {
let mut route_to_paths = HashMap::new();
let translation_used_by = translation.get_used_by(&db.conn).unwrap();
for symbol in translation_used_by.iter() {
let module = models::Module::retrieve_by_id(&db.conn, symbol.module_id).unwrap();
let full_paths = dependency_tracker
.trace((
module.path.to_string(),
TraceTarget::LocalVar(symbol.name.to_string()),
))
.unwrap();
// traverse each path and check if any symbol is used in some routes
for mut full_path in full_paths {
full_path.reverse();
for (i, (step_module_path, step_trace_target)) in full_path.iter().enumerate() {
match step_trace_target {
TraceTarget::LocalVar(step_symbol_name) => {
let step_module =
project.get_module(&db.conn, &step_module_path).unwrap();
let step_symbol = step_module
.get_symbol(
&db.conn,
models::SymbolVariant::LocalVariable,
&step_symbol_name,
)
.unwrap();
let routes = step_symbol.get_used_by_routes(&db.conn).unwrap();
if routes.len() > 0 {
let dependency_from_target_to_route: Vec<Step> = full_path[0..i]
.iter()
.map(|(path, target)| Step {
module_path: path.clone(),
symbol_name: target.to_string(),
})
.collect();
for route in routes.iter() {
let route = &route.path;
let symbol = &symbol.name;
if !route_to_paths.contains_key(route) {
route_to_paths.insert(route.clone(), HashMap::new());
}
if !route_to_paths.get(route).unwrap().contains_key(symbol) {
route_to_paths
.get_mut(route)
.unwrap()
.insert(symbol.to_string(), vec![]);
}
route_to_paths
.get_mut(route)
.unwrap()
.get_mut(symbol)
.unwrap()
.push(dependency_from_target_to_route.clone());
}
}
}
_ => (),
}
}
}
}

trace_result.insert(translation.key.to_string(), route_to_paths);
}

Ok(web::Json(SearchResponse {
project_root: "".to_string(),
trace_result,
}))
}

#[derive(Parser)]
#[command(version, about = "Start the server to provide search API", long_about = None)]
struct Cli {
Expand All @@ -167,7 +256,8 @@ async fn main() -> std::io::Result<()> {
symbol_to_route: portable.symbol_to_route.clone(),
used_by_graph: portable.used_by_graph.clone(),
}))
.service(search)
.service(search_in_memory)
.service(search_db)
})
.bind(("127.0.0.1", 8080))?
.run()
Expand Down
8 changes: 4 additions & 4 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ struct Project {
}

impl Project {
pub fn new(project_root: &str, db_path: &str) -> anyhow::Result<Self> {
pub fn new(project_name: &str, project_root: &str, db_path: &str) -> anyhow::Result<Self> {
let db = SqliteDb::open(db_path)?;
db.create_tables()?;
let project = models::Project::create(&db.conn, project_root)?;
let project = models::Project::create(&db.conn, project_root, project_name)?;
Ok(Self {
db,
project_root: project_root.to_owned(),
Expand Down Expand Up @@ -345,7 +345,7 @@ impl Project {
&self,
symbol_dependency: &SymbolDependency,
) -> anyhow::Result<models::Module> {
let module = self.project.add_module(
let module = self.project.get_or_create_module(
&self.db.conn,
&self.remove_prefix(&symbol_dependency.canonical_path),
)?;
Expand Down Expand Up @@ -429,7 +429,7 @@ impl Project {
fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
let project_root = PathBuf::from(&cli.input).to_canonical_string()?;
let project = Project::new(&project_root, "./database/1010.db3")?;
let project = Project::new("kirby", &project_root, "./database/1010.db3")?;
let translation_file = File::open(&cli.translation_path)?;
let translation_json_reader = BufReader::new(translation_file);
let mut scheduler = ParserCandidateScheduler::new(&project_root);
Expand Down
39 changes: 0 additions & 39 deletions crates/dt_database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,42 +48,3 @@ impl Database for SqliteDb {
Ok(())
}
}

#[test]
fn db_works() {
let db =
SqliteDb::open("/Users/linweitang/rust/js-symbol-dependency-tracker/database/test.db3")
.unwrap();
db.create_tables().unwrap();
println!("{:#?}", db);

for project in db
.conn
.prepare("SELECT * FROM project")
.unwrap()
.query_map([], Project::from_row)
.unwrap()
{
println!("{:?}", project.unwrap());
}

for module in db
.conn
.prepare("SELECT * FROM module")
.unwrap()
.query_map([], Module::from_row)
.unwrap()
{
println!("{:?}", module.unwrap());
}

for symbol in db
.conn
.prepare("SELECT * FROM symbol")
.unwrap()
.query_map([], Symbol::from_row)
.unwrap()
{
println!("{:?}", symbol.unwrap());
}
}
125 changes: 121 additions & 4 deletions crates/dt_database/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ pub trait Model {
fn table() -> String;
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Project {
pub id: usize,
pub path: String,
pub name: String,
}

impl Model for Project {
fn table() -> String {
"
project (
id INTEGER PRIMARY KEY,
path TEXT NOT NULL
path TEXT NOT NULL,
name TEXT UNIQUE NOT NULL
)
"
.to_string()
Expand All @@ -27,12 +29,16 @@ impl Project {
Ok(Self {
id: row.get(0)?,
path: row.get(1)?,
name: row.get(2)?,
})
}

/// single thread only: last_insert_rowid()
pub fn create(conn: &Connection, path: &str) -> anyhow::Result<Self> {
conn.execute("INSERT INTO project (path) VALUES (?1)", params![path])?;
pub fn create(conn: &Connection, path: &str, name: &str) -> anyhow::Result<Self> {
conn.execute(
"INSERT INTO project (path, name) VALUES (?1, ?2)",
params![path, name],
)?;
let project = conn.query_row(
"SELECT * FROM project WHERE id=last_insert_rowid()",
(),
Expand All @@ -41,6 +47,15 @@ impl Project {
Ok(project)
}

pub fn retrieve_by_name(conn: &Connection, name: &str) -> anyhow::Result<Self> {
let project = conn.query_row(
"SELECT * FROM project WHERE (name) = (?1)",
params![name],
Self::from_row,
)?;
Ok(project)
}

pub fn add_module(&self, conn: &Connection, path: &str) -> anyhow::Result<Module> {
Module::create(conn, self, path)
}
Expand Down Expand Up @@ -70,6 +85,18 @@ impl Project {
Translation::retrieve(conn, self, key)
}

pub fn search_translation(
&self,
conn: &Connection,
search: &str,
exact_match: bool,
) -> anyhow::Result<Vec<Translation>> {
match exact_match {
true => Translation::search_value_exact_match(conn, self, search),
false => Translation::search_value_contain(conn, self, search),
}
}

pub fn add_route(&self, conn: &Connection, path: &str) -> anyhow::Result<Route> {
Route::create(conn, self, path)
}
Expand Down Expand Up @@ -118,6 +145,15 @@ impl Module {
Ok(module)
}

pub fn retrieve_by_id(conn: &Connection, module_id: usize) -> anyhow::Result<Self> {
let module = conn.query_row(
"SELECT * FROM module WHERE id = ?1",
params![module_id],
Self::from_row,
)?;
Ok(module)
}

pub fn retrieve(conn: &Connection, project: &Project, path: &str) -> anyhow::Result<Self> {
let module = conn.query_row(
"SELECT * FROM module WHERE (project_id, path) = (?1, ?2)",
Expand Down Expand Up @@ -263,6 +299,38 @@ impl Symbol {
)?;
Ok(symbol)
}

pub fn get_used_by(&self, conn: &Connection) -> anyhow::Result<Vec<Symbol>> {
let used_by: Vec<Symbol> = conn
.prepare(
"
SELECT s.*
FROM symbol s
JOIN symbol_dependency sd ON s.id = sd.symbol_id
WHERE sd.depend_on_symbol_id = ?1;
",
)?
.query_map(params![self.id], Symbol::from_row)?
.map(|s| s.unwrap())
.collect();
Ok(used_by)
}

pub fn get_used_by_routes(&self, conn: &Connection) -> anyhow::Result<Vec<Route>> {
let used_by_routes: Vec<Route> = conn
.prepare(
"
SELECT r.*
FROM route r
JOIN route_usage ru ON r.id = ru.route_id
WHERE ru.symbol_id = ?1;
",
)?
.query_map(params![self.id], Route::from_row)?
.map(|s| s.unwrap())
.collect();
Ok(used_by_routes)
}
}

// Join Table
Expand Down Expand Up @@ -373,6 +441,55 @@ impl Translation {
)?;
Ok(translation)
}

pub fn search_value_exact_match(
conn: &Connection,
project: &Project,
search: &str,
) -> anyhow::Result<Vec<Self>> {
let translations: Vec<Self> = conn
.prepare("SELECT * FROM translation WHERE (project_id, value) = (?1, ?2)")?
.query_map(params![project.id, search], Self::from_row)?
.map(|s| s.unwrap())
.collect();
Ok(translations)
}

pub fn search_value_contain(
conn: &Connection,
project: &Project,
search: &str,
) -> anyhow::Result<Vec<Self>> {
let translations: Vec<Self> = conn
.prepare(
"
SELECT *
FROM translation
WHERE project_id = ?1
AND value LIKE '%' || ?2 || '%'
",
)?
.query_map(params![project.id, search], Self::from_row)?
.map(|s| s.unwrap())
.collect();
Ok(translations)
}

pub fn get_used_by(&self, conn: &Connection) -> anyhow::Result<Vec<Symbol>> {
let used_by: Vec<Symbol> = conn
.prepare(
"
SELECT s.*
FROM symbol s
JOIN translation_usage tu ON s.id = tu.symbol_id
WHERE tu.translation_id = ?1;
",
)?
.query_map(params![self.id], Symbol::from_row)?
.map(|s| s.unwrap())
.collect();
Ok(used_by)
}
}

// Join Table
Expand Down
Loading

0 comments on commit 4994621

Please sign in to comment.