Skip to content

Commit b248ecd

Browse files
committed
feat: record query count
1 parent c9affc9 commit b248ecd

File tree

5 files changed

+196
-138
lines changed

5 files changed

+196
-138
lines changed

Cargo.lock

Lines changed: 3 additions & 2 deletions
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
@@ -6,7 +6,7 @@ authors = ["passchaos"]
66

77
[dependencies]
88
serde = "*"
9-
serde_json = "*"
9+
serde_json = "1.0.89"
1010
serde_derive = "*"
1111
ansi_term = "*"
1212
redb = "0.9.0"
@@ -16,3 +16,4 @@ dirs = "4.0.0"
1616
tokio = { version = "1.21.2", features = ["full"] }
1717
termcolor = "1.1.3"
1818
clap = { version = "4.0.22", features = ["derive"] }
19+
once_cell = "1.16.0"

src/db.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use anyhow::bail;
2+
use anyhow::Result;
3+
use once_cell::sync::OnceCell;
4+
use redb::{Database, ReadableTable, TableDefinition};
5+
use serde_derive::{Deserialize, Serialize};
6+
7+
const TABLE: TableDefinition<str, str> = TableDefinition::new("ydcv");
8+
fn get_db<'a>() -> Result<&'a Database> {
9+
static DB: OnceCell<Database> = OnceCell::new();
10+
11+
DB.get_or_try_init(|| {
12+
let Some(mut dir) = dirs::home_dir() else {
13+
bail!("no home dir found");
14+
};
15+
16+
dir.push("proliferation/english");
17+
18+
if !dir.exists() {
19+
std::fs::create_dir_all(&dir)?;
20+
}
21+
22+
dir.push("ydcv.redb");
23+
24+
let db = unsafe { Database::create(dir, 1024 * 1024)? };
25+
26+
Ok(db)
27+
})
28+
}
29+
30+
fn get_value(key: &str) -> Result<Option<String>> {
31+
let db = get_db()?;
32+
33+
let read_txn = db.begin_read()?;
34+
let table = read_txn.open_table(TABLE)?;
35+
36+
let v = table.get(key)?;
37+
38+
Ok(v.map(|s| s.to_string()))
39+
}
40+
41+
fn insert_value(key: &str, value: &str) -> Result<()> {
42+
let write_txn = get_db()?.begin_write()?;
43+
{
44+
let mut table = write_txn.open_table(TABLE)?;
45+
table.insert(key, value)?;
46+
}
47+
write_txn.commit()?;
48+
49+
Ok(())
50+
}
51+
52+
#[derive(Debug, Serialize, Deserialize)]
53+
struct Answer {
54+
explain: String,
55+
query_count: u64,
56+
}
57+
58+
pub fn save_query_explain(query: &str, explain: String) -> Result<()> {
59+
let saved_answer = get_value(query)?;
60+
61+
let query_count = if let Some(answer) = saved_answer {
62+
let answer: Answer = serde_json::from_str(&answer)?;
63+
64+
answer.query_count + 1
65+
} else {
66+
1
67+
};
68+
69+
println!("query count: {query_count}\n\n{explain}");
70+
71+
let answer = Answer {
72+
explain,
73+
query_count,
74+
};
75+
76+
insert_value(query, &serde_json::to_string(&answer)?)?;
77+
78+
Ok(())
79+
}

src/explain.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::io::Write;
2+
3+
use anyhow::Result;
4+
use serde_derive::Deserialize;
5+
use termcolor::{Buffer, ColorSpec, WriteColor};
6+
7+
#[derive(Debug, Deserialize)]
8+
#[serde(untagged)]
9+
enum Phonetic {
10+
En2Zh {
11+
#[serde(rename = "us-phonetic")]
12+
us: String,
13+
#[serde(rename = "uk-phonetic")]
14+
uk: String,
15+
},
16+
Zh2En {
17+
phonetic: String,
18+
},
19+
}
20+
21+
#[derive(Debug, Deserialize)]
22+
struct Basic {
23+
// 有些查询没有这个字段,比如 `cli`
24+
#[serde(flatten)]
25+
phonetic: Option<Phonetic>,
26+
explains: Vec<String>,
27+
}
28+
29+
#[derive(Debug, Deserialize)]
30+
struct Kv {
31+
key: String,
32+
value: Vec<String>,
33+
}
34+
35+
#[derive(Debug, Deserialize)]
36+
pub struct YdcvResp {
37+
query: String,
38+
translation: Vec<String>,
39+
basic: Basic,
40+
web: Option<Vec<Kv>>,
41+
}
42+
43+
impl YdcvResp {
44+
pub fn colorized(&self) -> Result<String> {
45+
let mut f = Buffer::ansi();
46+
47+
f.set_color(ColorSpec::new().set_underline(true))?;
48+
49+
write!(f, "{}", self.query)?;
50+
51+
f.reset().unwrap();
52+
53+
match &self.basic.phonetic {
54+
Some(Phonetic::En2Zh { us, uk }) => {
55+
for (k, v) in [("us", us), ("uk", uk)] {
56+
write!(f, " {k}: [")?;
57+
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Yellow)))?;
58+
write!(f, "{v}")?;
59+
f.reset()?;
60+
write!(f, "]")?;
61+
}
62+
}
63+
Some(Phonetic::Zh2En { phonetic }) => {
64+
write!(f, " [")?;
65+
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Yellow)))?;
66+
write!(f, " {phonetic} ")?;
67+
f.reset()?;
68+
write!(f, "]")?;
69+
}
70+
None => {}
71+
}
72+
73+
for i in &self.translation {
74+
write!(f, " {}", i)?;
75+
}
76+
77+
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Cyan)))?;
78+
write!(f, "\n World Explanation:\n")?;
79+
f.reset()?;
80+
81+
for i in &self.basic.explains {
82+
write!(f, " * {}\n", i)?;
83+
}
84+
85+
if let Some(web) = &self.web {
86+
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Cyan)))?;
87+
write!(f, "\n Web Reference:")?;
88+
f.reset()?;
89+
90+
for Kv { key, value } in web {
91+
write!(f, "\n * ")?;
92+
93+
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Yellow)))?;
94+
write!(f, "{key}\n")?;
95+
f.reset()?;
96+
97+
let v = value.join(",");
98+
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Magenta)))?;
99+
write!(f, " {v}")?;
100+
f.reset()?;
101+
}
102+
}
103+
104+
String::from_utf8(f.into_inner()).map_err(From::from)
105+
}
106+
}

src/main.rs

Lines changed: 6 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,17 @@
1-
use std::io::Write;
2-
31
use anyhow::{Context, Result};
42
use clap::Parser;
5-
use redb::{Database, TableDefinition};
6-
use serde_derive::Deserialize;
7-
use termcolor::{Buffer, ColorSpec, WriteColor};
8-
9-
const REQUEST_BASE: &'static str = "http://fanyi.youdao.com/openapi.do?keyfrom=ydcv-rust&key=379421805&type=data&doctype=json&version=1.1&q=";
10-
11-
#[derive(Debug, Deserialize)]
12-
#[serde(untagged)]
13-
enum Phonetic {
14-
En2Zh {
15-
#[serde(rename = "us-phonetic")]
16-
us: String,
17-
#[serde(rename = "uk-phonetic")]
18-
uk: String,
19-
},
20-
Zh2En {
21-
phonetic: String,
22-
},
23-
}
24-
25-
#[derive(Debug, Deserialize)]
26-
struct Basic {
27-
// 有些查询没有这个字段,比如 `cli`
28-
#[serde(flatten)]
29-
phonetic: Option<Phonetic>,
30-
explains: Vec<String>,
31-
}
32-
33-
#[derive(Debug, Deserialize)]
34-
struct Kv {
35-
key: String,
36-
value: Vec<String>,
37-
}
38-
39-
#[derive(Debug, Deserialize)]
40-
struct YdcvResp {
41-
query: String,
42-
translation: Vec<String>,
43-
basic: Basic,
44-
web: Option<Vec<Kv>>,
45-
}
46-
47-
impl YdcvResp {
48-
fn colorized(&self) -> Result<String> {
49-
let mut f = Buffer::ansi();
50-
51-
f.set_color(ColorSpec::new().set_underline(true))?;
52-
53-
write!(f, "{}", self.query)?;
54-
55-
f.reset().unwrap();
56-
57-
match &self.basic.phonetic {
58-
Some(Phonetic::En2Zh { us, uk }) => {
59-
for (k, v) in [("us", us), ("uk", uk)] {
60-
write!(f, " {k}: [")?;
61-
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Yellow)))?;
62-
write!(f, "{v}")?;
63-
f.reset()?;
64-
write!(f, "]")?;
65-
}
66-
}
67-
Some(Phonetic::Zh2En { phonetic }) => {
68-
write!(f, " [")?;
69-
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Yellow)))?;
70-
write!(f, " {phonetic} ")?;
71-
f.reset()?;
72-
write!(f, "]")?;
73-
}
74-
None => {}
75-
}
76-
77-
for i in &self.translation {
78-
write!(f, " {}", i)?;
79-
}
3+
use explain::YdcvResp;
804

81-
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Cyan)))?;
82-
write!(f, "\n World Explanation:\n")?;
83-
f.reset()?;
5+
mod db;
6+
mod explain;
847

85-
for i in &self.basic.explains {
86-
write!(f, " * {}\n", i)?;
87-
}
88-
89-
if let Some(web) = &self.web {
90-
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Cyan)))?;
91-
write!(f, "\n Web Reference:")?;
92-
f.reset()?;
93-
94-
for Kv { key, value } in web {
95-
write!(f, "\n * ")?;
96-
97-
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Yellow)))?;
98-
write!(f, "{key}\n")?;
99-
f.reset()?;
100-
101-
let v = value.join(",");
102-
f.set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Magenta)))?;
103-
write!(f, " {v}")?;
104-
f.reset()?;
105-
}
106-
}
107-
108-
String::from_utf8(f.into_inner()).map_err(From::from)
109-
}
110-
}
8+
const REQUEST_BASE: &'static str = "http://fanyi.youdao.com/openapi.do?keyfrom=ydcv-rust&key=379421805&type=data&doctype=json&version=1.1&q=";
1119

11210
#[derive(Parser, Debug)]
11311
struct Args {
11412
word: String,
11513
}
11614

117-
fn save_dict_info(query: &str, result: &str) -> Result<()> {
118-
const TABLE: TableDefinition<str, str> = TableDefinition::new("ydcv");
119-
120-
let Some(mut dir) = dirs::home_dir() else {
121-
return Ok(());
122-
};
123-
124-
dir.push("proliferation/english");
125-
126-
if !dir.exists() {
127-
std::fs::create_dir_all(&dir)?;
128-
}
129-
130-
dir.push("ydcv.redb");
131-
132-
let db = unsafe { Database::create(dir, 1024 * 1024)? };
133-
134-
let write_txn = db.begin_write()?;
135-
{
136-
let mut table = write_txn.open_table(TABLE)?;
137-
table.insert(query, result)?;
138-
}
139-
write_txn.commit()?;
140-
141-
Ok(())
142-
}
143-
14415
#[tokio::main]
14516
async fn main() -> Result<()> {
14617
let args = Args::parse();
@@ -156,9 +27,9 @@ async fn main() -> Result<()> {
15627
))?;
15728

15829
let res = resp.colorized()?;
159-
println!("{}", res);
30+
// println!("{}", res);
16031

161-
save_dict_info(&args.word, &res)?;
32+
db::save_query_explain(&args.word, res)?;
16233

16334
Ok(())
16435
}

0 commit comments

Comments
 (0)