irongit

A Neovim plugin enabling you to solve LeetCode problems.

145 lines3.6 KBLua
1local path = require("plenary.path")
2local problems_api = require("leetcode.api.problems")
3
4local log = require("leetcode.logger")
5local config = require("leetcode.config")
6local interval = config.user.cache.update_interval
7
8---@type Path
9local file = config.storage.cache:joinpath(("problemlist%s"):format(config.is_cn and "_cn" or ""))
10
11---@type { at: integer, payload: lc.cache.payload }
12local hist = nil
13
14---@class lc.cache.Question
15---@field id integer
16---@field frontend_id string
17---@field link string
18---@field title string
19---@field title_cn string
20---@field title_slug string
21---@field status string
22---@field paid_only boolean
23---@field ac_rate number
24---@field difficulty "Easy" | "Medium" | "Hard"
25---@field topic_tags { name:string, slug: string }
26
27---@class lc.cache.Problemlist
28local Problemlist = {}
29
30---@return lc.cache.Question[]
31function Problemlist.get()
32 return Problemlist.read().data
33end
34
35---@return lc.cache.payload
36function Problemlist.read()
37 if not file:exists() then
38 return Problemlist.populate()
39 end
40
41 local time = os.time()
42 if hist and (time - hist.at) <= math.min(60, interval) then
43 return hist.payload
44 end
45
46 local contents = file:read()
47 if not contents or type(contents) ~= "string" then
48 return Problemlist.populate()
49 end
50
51 local cached = Problemlist.parse(contents)
52
53 if not cached or (cached.version ~= config.version or cached.username ~= config.auth.name) then
54 return Problemlist.populate()
55 end
56
57 hist = { at = time, payload = cached }
58 if (time - cached.updated_at) > interval then
59 Problemlist.update()
60 end
61
62 return cached
63end
64
65---@return lc.cache.payload
66function Problemlist.populate()
67 local res, err = problems_api.all(nil, true)
68
69 if not res or err then
70 local msg = (err or {}).msg or "failed to fetch problem list"
71 error(msg)
72 end
73
74 Problemlist.write({ data = res })
75 return hist.payload
76end
77
78function Problemlist.update()
79 problems_api.all(function(res, err)
80 if not err then
81 Problemlist.write({ data = res })
82 end
83 end, true)
84end
85
86---@return lc.cache.Question
87function Problemlist.get_by_title_slug(title_slug)
88 local problems = Problemlist.get()
89
90 local problem = vim.tbl_filter(function(e)
91 return e.title_slug == title_slug
92 end, problems)[1]
93
94 assert(problem, ("Problem `%s` not found. Try updating cache?"):format(title_slug))
95 return problem
96end
97
98---@param payload? lc.cache.payload
99function Problemlist.write(payload)
100 payload = vim.tbl_deep_extend("force", {
101 version = config.version,
102 updated_at = os.time(),
103 username = config.auth.name,
104 }, payload)
105
106 if not payload.data then
107 payload.data = Problemlist.get()
108 end
109
110 file:write(vim.json.encode(payload), "w")
111 hist = { at = os.time(), payload = payload }
112end
113
114---@alias lc.cache.payload { version: string, data: lc.cache.Question[], updated_at: integer, username: string }
115
116---@param str string
117---
118---@return lc.cache.payload
119function Problemlist.parse(str)
120 return vim.json.decode(str)
121end
122
123---@param title_slug string
124---@param status "ac" | "notac"
125Problemlist.change_status = vim.schedule_wrap(function(title_slug, status)
126 local cached = Problemlist.read()
127
128 cached.data = vim.tbl_map(function(p)
129 if p.title_slug == title_slug then
130 p.status = status
131 end
132 return p
133 end, cached.data)
134
135 Problemlist.write(cached)
136end)
137
138function Problemlist.delete()
139 if not file:exists() then
140 return false
141 end
142 return pcall(path.rm, file)
143end
144
145return Problemlist