From efd3b65bc0ff05f81fb90fae6bce9b2d4eb63cbc Mon Sep 17 00:00:00 2001 From: wtlin1228 Date: Fri, 11 Oct 2024 15:31:05 +0800 Subject: [PATCH] docs: add sub-command for cli and update readme --- README.md | 60 +++++++++++++++++++-- assets/erd.jpeg | Bin 0 -> 49186 bytes crates/cli/src/main.rs | 117 +++++++++++++++++++++++++++++++++++------ 3 files changed, 157 insertions(+), 20 deletions(-) create mode 100644 assets/erd.jpeg diff --git a/README.md b/README.md index 7a1f8c7..a1d04ca 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ flowchart TD reexport all the library crates: +- database - graph - i18n - parser @@ -88,6 +89,12 @@ reexport all the library crates: - scheduler - tracker +### Database + +`Database` defines the models using in the `cli` and `api_server` crate. + +![ERD](./assets/erd.jpeg) + ### Graph `DependOnGraph` takes the `SymbolDependency` one by one to construct a DAG. You have to add the `SymbolDependency` by topological order so that `DependOnGraph` can handle the wildcard import and export for you. @@ -203,11 +210,58 @@ let paths = dt.trace("", TraceTarget::LocalVar("variable_name")).un ### Demo -See the `demo` crate. You can run `cargo run --bin demo -- -s ./test-project/everybodyyyy -d ~/tmp`. +See the `demo` crate. -### Portable +``` +Track fine-grained symbol dependency graph + +Usage: demo -s -d + +Options: + -s Path of project to trace + -d Path of the output folder + -h, --help Print help + -V, --version Print version +``` + +### CLI + +See the `cli` crate. + +``` +Parse a project and serialize its output -See the `cli` crate. You can run `cargo run --bin cli -- -i -o `. +Usage: cli + +Commands: + portable Parse and export the project in portable format + database Parse and export the project in database format + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help + -V, --version Print version +``` + +Usage: + +- `cli portable -i -t -o ` +- `cli database -i -t -o ` + +### API Server + +see the `api_server` crate. The database is the one generated by CLI with `database` command. + +``` +Start the server to provide search API + +Usage: api_server --db + +Options: + --db The path of your database + -h, --help Print help + -V, --version Print version +``` ## Client diff --git a/assets/erd.jpeg b/assets/erd.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..52b16fe04055a343752e3bc2307375f1d5a6bd5c GIT binary patch literal 49186 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 U`hgji4M=HVlsS|`fbsuL05_vm4FCWD literal 0 HcmV?d00001 diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index e2b1ce9..c3d419a 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,8 +1,9 @@ use anyhow::Context; -use clap::Parser; +use clap::{Parser, Subcommand}; use dt_core::{ database::{models, Database, SqliteDb}, - i18n::collect_translation, + graph::{depend_on_graph::DependOnGraph, used_by_graph::UsedByGraph}, + i18n::{collect_translation, I18nToSymbol}, parser::{ anonymous_default_export::SYMBOL_NAME_FOR_ANONYMOUS_DEFAULT_EXPORT, collect_symbol_dependency, @@ -10,42 +11,124 @@ use dt_core::{ Input, }, path_resolver::{PathResolver, ToCanonicalString}, - route::{collect_route_dependency, Route}, + portable::Portable, + route::{collect_route_dependency, Route, SymbolToRoutes}, scheduler::ParserCandidateScheduler, }; use std::{ collections::{HashMap, HashSet}, fs::File, - io::BufReader, + io::{BufReader, Write}, path::PathBuf, }; #[derive(Parser)] #[command(version, about = "Parse a project and serialize its output", long_about = None)] struct Cli { - /// Input path - #[arg(short)] - input: String, + #[command(subcommand)] + command: Command, +} + +#[derive(Subcommand)] +enum Command { + /// Parse and export the project in portable format + Portable { + /// Input path + #[arg(short)] + input: String, + + /// translation.json path + #[arg(short)] + translation_path: String, + + /// Output path + #[arg(short)] + output: String, + }, + + /// Parse and export the project in database format + Database { + /// Input path + #[arg(short)] + input: String, - /// translation.json path - #[arg(short)] - translation_path: String, + /// translation.json path + #[arg(short)] + translation_path: String, - /// Output path - #[arg(short)] - output: String, + /// Output path + #[arg(short)] + output: String, + }, } fn main() -> anyhow::Result<()> { - let cli = Cli::parse(); + match Cli::parse().command { + Command::Portable { + input, + translation_path, + output, + } => { + parse_and_export_project_to_portable(&input, &output, &translation_path) + .context("parse and export project to portable")?; + } + Command::Database { + input, + translation_path, + output, + } => { + parse_and_export_project_to_database(&input, &output, &translation_path) + .context("parse and export project to database")?; + } + } + Ok(()) +} + +fn parse_and_export_project_to_portable( + project_root: &str, + output_portable_path: &str, + translation_file_path: &str, +) -> anyhow::Result<()> { + let project_root = PathBuf::from(project_root).to_canonical_string()?; + let translation_json = File::open(&translation_file_path)?; + let translation_json_reader = BufReader::new(translation_json); + + let mut scheduler = ParserCandidateScheduler::new(&project_root); + let mut depend_on_graph = DependOnGraph::new(&project_root); + let mut symbol_to_route = SymbolToRoutes::new(); + let mut i18n_to_symbol = I18nToSymbol::new(); + loop { + match scheduler.get_one_candidate() { + Some(c) => { + let module_src = c.to_str().context(format!("to_str() failed: {:?}", c))?; + let module_ast = Input::Path(module_src).get_module_ast()?; + let symbol_dependency = collect_symbol_dependency(&module_ast, module_src)?; + i18n_to_symbol.collect_i18n_usage(module_src, &module_ast)?; + symbol_to_route.collect_route_dependency(&module_ast, &symbol_dependency)?; + + depend_on_graph.add_symbol_dependency(symbol_dependency)?; + scheduler.mark_candidate_as_parsed(c); + } + None => break, + } + } + + let portable = Portable::new( + project_root.to_owned(), + serde_json::from_reader(translation_json_reader)?, + i18n_to_symbol.table, + symbol_to_route.table, + UsedByGraph::from(&depend_on_graph), + ); - parse_export_project_to_database(&cli.input, &cli.output, &cli.translation_path) - .context("parse and export project to database")?; + let serialized = portable.export()?; + let mut file = File::create(&output_portable_path)?; + file.write_all(serialized.as_bytes())?; Ok(()) } -fn parse_export_project_to_database( +fn parse_and_export_project_to_database( project_root: &str, output_database_path: &str, translation_file_path: &str,