diff --git a/Cargo.lock b/Cargo.lock index d9e2946..8b7d6dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -569,7 +569,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "leetcode_entry" +name = "lc_cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "lc_lib", + "rayon", + "regex", + "reqwest", + "serde", + "serde_json", + "strum", + "tokio", +] + +[[package]] +name = "lc_lib" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "rayon", + "regex", + "reqwest", + "serde", + "serde_json", + "strum", + "tokio", +] + +[[package]] +name = "lc_tui" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "lc_lib", + "rayon", + "regex", + "reqwest", + "serde", + "serde_json", + "strum", + "tokio", +] + +[[package]] +name = "leetcode_tracker" version = "0.1.0" dependencies = [ "anyhow", diff --git a/Cargo.toml b/Cargo.toml index e06bf68..6fd4795 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,23 @@ [package] -name = "leetcode_entry" +name = "leetcode_tracker" version = "0.1.0" edition = "2021" +default-run = "lc_cli" + +[workspace] +members = ["lc_lib", "lc_cli", "lc_tui"] + +[lib] +name = "lc_lib" +path = "lc_lib/src/lib.rs" + +[[bin]] +name = "lc_cli" +path = "lc_cli/src/main.rs" + +[[bin]] +name = "lc_tui" +path = "lc_tui/src/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] diff --git a/README.md b/README.md index c9c515d..5d91552 100644 --- a/README.md +++ b/README.md @@ -1,145 +1,13 @@ -# LeetCode CLI Tool +# LeetCode interactive library and tooling -## Repo model -The model that the problems should have is -``` - src -├──  -│ ├──  README.md -│ ├──  src -│ │ ├──  main.rs -│ │ └──  [test.rs] -│ └──  TAGS -├──  -│ ├──  README.md -│ ├──  src -│ │ ├──  main.rs -│ │ └──  [test.rs] -│ └──  TAGS -├──  -│ ├──  README.md -│ ├──  src -│ │ ├──  main.rs -│ │ └──  [test.rs] -│ └──  TAGS -│ ... -└──  - ├──  README.md - ├──  src - │ ├──  main.rs - │ └──  [test.rs] - └──  TAGS -``` +This is a set of a library for interacting with [LeetCode](https://leetcode.com/) through the `lc_lib` crate. +This provides bindings that `lc_cli` and `lc_tui` then use to allow a user to interact with LeetCode in a few different ways. -## Usage Examples +## CLI +This is the primary way to interact with the library. +This gives a way to use a simple CLI interface to get questions, solve them, and then submit them from the terminal. -### New Problem -Create a new problem with a provided link. -```bash -$ lc new -``` - -Read the title-slug and then request data from the server to then build the directory structure - -### Inspecting a Problem -This allows the user to look at the problem and see information about it. -The problem must be one that is already attempted (for now) so that it can show the tags and relevant -user added data. - -In the future this may be something that allows the user to look for and read about a problem in the first place. - -### Tagging a Problem -This allows the users to attribute tags to a question for easier lookup and distinction of what each problem teaches. - -```bash -$ lc tag -``` -Using `tag` has a question pop up about what to do with tags either add, remove, edit, or search for tags. - -#### Usage -The goal is to make no subcommands deeper than `tag add` or similar. This should be handled by -either the prompts after running this command, or a set of flags for more depth. - -### Submitting a Problem -This utility allows the user to submit the problem as a response and see output. - -### Finishing a Problem -This will tag a problem internally as completed and as such will have its `main.rs` used as the solution. - -### Removing a Problem -This will remove the problem from the listings and remove its finish status if it has it. - -### Failing (Hiding) a Problem -This is for the case that a problem has been attempted but given up on temporarily. -Tagged with failed or deferred. - -## Problems -There are some problems that are both seen and unseen. - -### Seen -- `lc tag search` and `lc search tag` These need to be reconciled such that they either overlap completely or one is removed and has its functionality taken into the other. - - -## Notes for API interaction - -All use /graphql/ unless otherwise specified. - -### Question number -The number on the page shown to users is not the internal number all the time. -To get the internal number there is a GraphQL request and response that works. - -Request: -```json -{"query":"\n query consolePanelConfig($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n questionFrontendId\n questionTitle\n enableDebugger\n enableRunCode\n enableSubmit\n enableTestMode\n exampleTestcaseList\n metaData\n }\n}\n ","variables":{"titleSlug":"two-sum"},"operationName":"consolePanelConfig"} -``` - -Response: -```json -{"data":{"question":{"questionId":"1","questionFrontendId":"1","questionTitle":"Two Sum","enableDebugger":true,"enableRunCode":true,"enableSubmit":true,"enableTestMode":false,"exampleTestcaseList":["[2,7,11,15]\n9","[3,2,4]\n6","[3,3]\n6"],"metaData":"{\n \"name\": \"twoSum\",\n \"params\": [\n {\n \"name\": \"nums\",\n \"type\": \"integer[]\"\n },\n {\n \"name\": \"target\",\n \"type\": \"integer\"\n }\n ],\n \"return\": {\n \"type\": \"integer[]\",\n \"size\": 2\n },\n \"manual\": false\n}"}}} -``` - -#### Alternative - -```json -{"query":"\n query questionTitle($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n questionFrontendId\n title\n titleSlug\n isPaidOnly\n difficulty\n likes\n dislikes\n categoryTitle\n }\n}\n ","variables":{"titleSlug":"two-sum"},"operationName":"questionTitle"} -``` - -```json -{"data":{"question":{"questionId":"1","questionFrontendId":"1","title":"Two Sum","titleSlug":"two-sum","isPaidOnly":false,"difficulty":"Easy","likes":51833,"dislikes":1696,"categoryTitle":"Algorithms"}}} -``` - -or - -Below has been tested and working. - -```json -{"query":"\n query questionNote($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n note\n }\n}\n ","variables":{"titleSlug":"two-sum"},"operationName":"questionNote"} -``` - -```json -{"data":{"question":{"questionId":"1","note":""}}} -``` - -where `questionId` is the value from what I can deduce. -This also can be used when trying to get the number and name when given the link as `title-slug` is in the link itself. - -### Provided Code -The code that is provided to the user is at this endpoint -```json -{"query":"\n query questionEditorData($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n questionFrontendId\n codeSnippets {\n lang\n langSlug\n code\n }\n envInfo\n enableRunCode\n hasFrontendPreview\n frontendPreviews\n }\n}\n ","variables":{"titleSlug":"two-sum"},"operationName":"questionEditorData"} -``` - -and what I want is in `codeSnippets` where `lang` is Rust. - -### Title vs Title-Slug -Title is the well formed name of the problem whereas Title-slug is that which is used in the endpoints. - -### Submission -This is how the system has submitted -@ /problems/$title-slug/submit/ send the query as stated below. - -```json -{"lang":"rust","question_id":"1","typed_code":"impl Solution {\n pub fn two_sum(nums: Vec, target: i32) -> Vec {\n use std::collections::HashMap;\n // hash each number with the index as their value\n let mut hash: HashMap = HashMap::new();\n for (k, v) in nums.iter().zip(0..) {\n match hash.get(&(target - k)) {\n Some(i) => return vec![v, *i],\n None => hash.insert(*k, v),\n };\n }\n vec![]\n }\n}"} -``` -if there is an error I will find out about it later. +## TUI +This is the secondary way to interact with the library. +This gives a front end within a terminal that mimics a GUI and gives a more interactive approach to solving and reading about the problems that LeetCode has to solve. diff --git a/lc_cli/Cargo.toml b/lc_cli/Cargo.toml new file mode 100644 index 0000000..d480547 --- /dev/null +++ b/lc_cli/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "lc_cli" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lc_lib = { path = "../lc_lib" } +anyhow = "1.0.75" +clap = { version = "4.4.4", features = ["derive"] } +rayon = "1.8.0" +regex = "1.9.5" +reqwest = { version = "0.11.20", features = ["json", "cookies"] } +serde = { version = "1.0.188", features = ["serde_derive", "derive"] } +serde_json = "1.0.107" +strum = { version = "0.25.0", features = ["derive"] } +tokio = { version = "1.32.0", features = ["full"] } diff --git a/lc_cli/README.md b/lc_cli/README.md new file mode 100644 index 0000000..c9c515d --- /dev/null +++ b/lc_cli/README.md @@ -0,0 +1,145 @@ +# LeetCode CLI Tool + +## Repo model +The model that the problems should have is +``` + src +├──  +│ ├──  README.md +│ ├──  src +│ │ ├──  main.rs +│ │ └──  [test.rs] +│ └──  TAGS +├──  +│ ├──  README.md +│ ├──  src +│ │ ├──  main.rs +│ │ └──  [test.rs] +│ └──  TAGS +├──  +│ ├──  README.md +│ ├──  src +│ │ ├──  main.rs +│ │ └──  [test.rs] +│ └──  TAGS +│ ... +└──  + ├──  README.md + ├──  src + │ ├──  main.rs + │ └──  [test.rs] + └──  TAGS +``` + + +## Usage Examples + +### New Problem +Create a new problem with a provided link. +```bash +$ lc new +``` + +Read the title-slug and then request data from the server to then build the directory structure + +### Inspecting a Problem +This allows the user to look at the problem and see information about it. +The problem must be one that is already attempted (for now) so that it can show the tags and relevant +user added data. + +In the future this may be something that allows the user to look for and read about a problem in the first place. + +### Tagging a Problem +This allows the users to attribute tags to a question for easier lookup and distinction of what each problem teaches. + +```bash +$ lc tag +``` +Using `tag` has a question pop up about what to do with tags either add, remove, edit, or search for tags. + +#### Usage +The goal is to make no subcommands deeper than `tag add` or similar. This should be handled by +either the prompts after running this command, or a set of flags for more depth. + +### Submitting a Problem +This utility allows the user to submit the problem as a response and see output. + +### Finishing a Problem +This will tag a problem internally as completed and as such will have its `main.rs` used as the solution. + +### Removing a Problem +This will remove the problem from the listings and remove its finish status if it has it. + +### Failing (Hiding) a Problem +This is for the case that a problem has been attempted but given up on temporarily. +Tagged with failed or deferred. + +## Problems +There are some problems that are both seen and unseen. + +### Seen +- `lc tag search` and `lc search tag` These need to be reconciled such that they either overlap completely or one is removed and has its functionality taken into the other. + + +## Notes for API interaction + +All use /graphql/ unless otherwise specified. + +### Question number +The number on the page shown to users is not the internal number all the time. +To get the internal number there is a GraphQL request and response that works. + +Request: +```json +{"query":"\n query consolePanelConfig($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n questionFrontendId\n questionTitle\n enableDebugger\n enableRunCode\n enableSubmit\n enableTestMode\n exampleTestcaseList\n metaData\n }\n}\n ","variables":{"titleSlug":"two-sum"},"operationName":"consolePanelConfig"} +``` + +Response: +```json +{"data":{"question":{"questionId":"1","questionFrontendId":"1","questionTitle":"Two Sum","enableDebugger":true,"enableRunCode":true,"enableSubmit":true,"enableTestMode":false,"exampleTestcaseList":["[2,7,11,15]\n9","[3,2,4]\n6","[3,3]\n6"],"metaData":"{\n \"name\": \"twoSum\",\n \"params\": [\n {\n \"name\": \"nums\",\n \"type\": \"integer[]\"\n },\n {\n \"name\": \"target\",\n \"type\": \"integer\"\n }\n ],\n \"return\": {\n \"type\": \"integer[]\",\n \"size\": 2\n },\n \"manual\": false\n}"}}} +``` + +#### Alternative + +```json +{"query":"\n query questionTitle($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n questionFrontendId\n title\n titleSlug\n isPaidOnly\n difficulty\n likes\n dislikes\n categoryTitle\n }\n}\n ","variables":{"titleSlug":"two-sum"},"operationName":"questionTitle"} +``` + +```json +{"data":{"question":{"questionId":"1","questionFrontendId":"1","title":"Two Sum","titleSlug":"two-sum","isPaidOnly":false,"difficulty":"Easy","likes":51833,"dislikes":1696,"categoryTitle":"Algorithms"}}} +``` + +or + +Below has been tested and working. + +```json +{"query":"\n query questionNote($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n note\n }\n}\n ","variables":{"titleSlug":"two-sum"},"operationName":"questionNote"} +``` + +```json +{"data":{"question":{"questionId":"1","note":""}}} +``` + +where `questionId` is the value from what I can deduce. +This also can be used when trying to get the number and name when given the link as `title-slug` is in the link itself. + +### Provided Code +The code that is provided to the user is at this endpoint +```json +{"query":"\n query questionEditorData($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n questionFrontendId\n codeSnippets {\n lang\n langSlug\n code\n }\n envInfo\n enableRunCode\n hasFrontendPreview\n frontendPreviews\n }\n}\n ","variables":{"titleSlug":"two-sum"},"operationName":"questionEditorData"} +``` + +and what I want is in `codeSnippets` where `lang` is Rust. + +### Title vs Title-Slug +Title is the well formed name of the problem whereas Title-slug is that which is used in the endpoints. + +### Submission +This is how the system has submitted +@ /problems/$title-slug/submit/ send the query as stated below. + +```json +{"lang":"rust","question_id":"1","typed_code":"impl Solution {\n pub fn two_sum(nums: Vec, target: i32) -> Vec {\n use std::collections::HashMap;\n // hash each number with the index as their value\n let mut hash: HashMap = HashMap::new();\n for (k, v) in nums.iter().zip(0..) {\n match hash.get(&(target - k)) {\n Some(i) => return vec![v, *i],\n None => hash.insert(*k, v),\n };\n }\n vec![]\n }\n}"} +``` +if there is an error I will find out about it later. diff --git a/src/main.rs b/lc_cli/src/main.rs similarity index 92% rename from src/main.rs rename to lc_cli/src/main.rs index b28d0c4..8845a94 100644 --- a/src/main.rs +++ b/lc_cli/src/main.rs @@ -1,5 +1,4 @@ -mod commands; -use commands::{ +use lc_lib::{ common::*, edit::*, finish::*, hide::*, info::*, interpret::*, new::*, search::*, submit::*, tag::*, }; @@ -13,9 +12,9 @@ async fn main() -> Result<()> { match &args.command { /**********************************************************************/ // These are the commands that are most necessary - Commands::New { link } => commands::new::run(link).await?, + Commands::New { link } => lc_lib::new::run(link).await?, - Commands::Edit { num } => commands::edit::run(num)?, + Commands::Edit { num } => lc_lib::edit::run(num)?, Commands::Tag { cmd } => tag_subcommands(cmd)?, diff --git a/src/test.rs b/lc_cli/src/test.rs similarity index 100% rename from src/test.rs rename to lc_cli/src/test.rs diff --git a/lc_lib/Cargo.toml b/lc_lib/Cargo.toml new file mode 100644 index 0000000..6d5c059 --- /dev/null +++ b/lc_lib/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "lc_lib" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.75" +clap = { version = "4.4.4", features = ["derive"] } +rayon = "1.8.0" +regex = "1.9.5" +reqwest = { version = "0.11.20", features = ["json", "cookies"] } +serde = { version = "1.0.188", features = ["serde_derive", "derive"] } +serde_json = "1.0.107" +strum = { version = "0.25.0", features = ["derive"] } +tokio = { version = "1.32.0", features = ["full"] } diff --git a/lc_lib/README.md b/lc_lib/README.md new file mode 100644 index 0000000..4ce2678 --- /dev/null +++ b/lc_lib/README.md @@ -0,0 +1,5 @@ +# LeetCode Library + +This is the library that everything is depending on. + +Inside are bindings to multiple functions and methods to interact with the LeetCode website and GraphQL endpoints. diff --git a/src/commands/common.rs b/lc_lib/src/commands/common.rs similarity index 100% rename from src/commands/common.rs rename to lc_lib/src/commands/common.rs diff --git a/lc_lib/src/commands/edit.rs b/lc_lib/src/commands/edit.rs new file mode 100644 index 0000000..4aa39d1 --- /dev/null +++ b/lc_lib/src/commands/edit.rs @@ -0,0 +1,5 @@ +use anyhow::Result; + +pub fn run(_prob: &usize) -> Result<()> { + todo!(); +} diff --git a/src/commands/finish.rs b/lc_lib/src/commands/finish.rs similarity index 100% rename from src/commands/finish.rs rename to lc_lib/src/commands/finish.rs diff --git a/src/commands/hide.rs b/lc_lib/src/commands/hide.rs similarity index 100% rename from src/commands/hide.rs rename to lc_lib/src/commands/hide.rs diff --git a/src/commands/info.rs b/lc_lib/src/commands/info.rs similarity index 100% rename from src/commands/info.rs rename to lc_lib/src/commands/info.rs diff --git a/src/commands/interpret.rs b/lc_lib/src/commands/interpret.rs similarity index 100% rename from src/commands/interpret.rs rename to lc_lib/src/commands/interpret.rs diff --git a/src/commands/mod.rs b/lc_lib/src/commands/mod.rs similarity index 100% rename from src/commands/mod.rs rename to lc_lib/src/commands/mod.rs diff --git a/src/commands/new.rs b/lc_lib/src/commands/new.rs similarity index 100% rename from src/commands/new.rs rename to lc_lib/src/commands/new.rs diff --git a/src/commands/search.rs b/lc_lib/src/commands/search.rs similarity index 100% rename from src/commands/search.rs rename to lc_lib/src/commands/search.rs diff --git a/src/commands/submit.rs b/lc_lib/src/commands/submit.rs similarity index 100% rename from src/commands/submit.rs rename to lc_lib/src/commands/submit.rs diff --git a/src/commands/tag.rs b/lc_lib/src/commands/tag.rs similarity index 100% rename from src/commands/tag.rs rename to lc_lib/src/commands/tag.rs diff --git a/lc_lib/src/lib.rs b/lc_lib/src/lib.rs new file mode 100644 index 0000000..b3de740 --- /dev/null +++ b/lc_lib/src/lib.rs @@ -0,0 +1,8 @@ +mod commands; +pub use commands::*; + +#[cfg(test)] +mod tests { + #[allow(unused_imports)] + use super::*; +} diff --git a/lc_tui/Cargo.toml b/lc_tui/Cargo.toml new file mode 100644 index 0000000..096901b --- /dev/null +++ b/lc_tui/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "lc_tui" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lc_lib = { path = "../lc_lib" } +anyhow = "1.0.75" +clap = { version = "4.4.4", features = ["derive"] } +rayon = "1.8.0" +regex = "1.9.5" +reqwest = { version = "0.11.20", features = ["json", "cookies"] } +serde = { version = "1.0.188", features = ["serde_derive", "derive"] } +serde_json = "1.0.107" +strum = { version = "0.25.0", features = ["derive"] } +tokio = { version = "1.32.0", features = ["full"] } diff --git a/lc_tui/README.md b/lc_tui/README.md new file mode 100644 index 0000000..2d1af67 --- /dev/null +++ b/lc_tui/README.md @@ -0,0 +1,3 @@ +# LeetCode TUI Tool + +This is the crate with the front end contents for the TUI tool that I am developing. diff --git a/lc_tui/src/main.rs b/lc_tui/src/main.rs new file mode 100644 index 0000000..a910436 --- /dev/null +++ b/lc_tui/src/main.rs @@ -0,0 +1,5 @@ +use lc_lib; + +fn main() { + println!("Hello, world!"); +} diff --git a/src/commands/edit.rs b/src/commands/edit.rs deleted file mode 100644 index 00e7b92..0000000 --- a/src/commands/edit.rs +++ /dev/null @@ -1,5 +0,0 @@ -use anyhow::Result; - -pub fn run(prob: &usize) -> Result<()> { - todo!(); -}