added filesystem functions and requests module
This commit is contained in:
parent
19ccfc0daf
commit
9032c6051f
|
@ -0,0 +1,96 @@
|
||||||
|
# Requests Module <!-- {docsify-ignore} -->
|
||||||
|
|
||||||
|
The `requests` module provides an interface for making asynchronous HTTP requests in AwesomeWM using the `lgi` bindings for `Soup`, `Gio`, and `GLib`.
|
||||||
|
It supports common HTTP methods like `GET`, `POST`, `PUT`, and more.
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
### `requests.request(method, args, callback)`
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
- **method**: A string representing the HTTP method (`"GET"`, `"POST"`, etc.).
|
||||||
|
- **args**: A table or string. The table can include:
|
||||||
|
- **url**: The request URL (string).
|
||||||
|
- **params**: Query parameters (table).
|
||||||
|
- **headers**: HTTP headers (table).
|
||||||
|
- **body**: The request body (`GLib.Bytes` or string).
|
||||||
|
- **callback**: A function to handle the `Response` object.
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
requests.request("GET", { url = "https://api.example.com" }, function(response)
|
||||||
|
print(response.status_code, response.text)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `requests.get(args, callback)`
|
||||||
|
|
||||||
|
#### Description:
|
||||||
|
|
||||||
|
A shorthand for making `GET` requests.
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
- **args**: Similar to `requests.request` but defaults to `GET` method.
|
||||||
|
- **callback**: Function called with a `Response` object.
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
requests.get({ url = "https://api.example.com" }, function(response)
|
||||||
|
print(response.status_code, response.text)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `requests.post(args, callback)`
|
||||||
|
|
||||||
|
#### Description:
|
||||||
|
|
||||||
|
A shorthand for making `POST` requests.
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
- **args**: Similar to `requests.request` but defaults to `POST` method.
|
||||||
|
- **callback**: Function called with a `Response` object.
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
requests.post({
|
||||||
|
url = "https://api.example.com",
|
||||||
|
headers = { Authorization = "Bearer token" },
|
||||||
|
body = '{"key": "value"}'
|
||||||
|
}, function(response)
|
||||||
|
print(response.status_code, response.text)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Response Object
|
||||||
|
|
||||||
|
The `Response` object encapsulates the result of a request.
|
||||||
|
|
||||||
|
### Fields:
|
||||||
|
|
||||||
|
- **url**: The final URL after redirections.
|
||||||
|
- **status_code**: The HTTP status code (number).
|
||||||
|
- **ok**: A boolean indicating if the request was successful.
|
||||||
|
- **reason_phrase**: A string with the status reason.
|
||||||
|
- **text**: The response body as a string.
|
||||||
|
- **bytes**: The response body as `GLib.Bytes`.
|
||||||
|
|
||||||
|
### Example Usage:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
print(response.url) -- "https://api.example.com"
|
||||||
|
print(response.status_code) -- 200
|
||||||
|
print(response.ok) -- true
|
||||||
|
print(response.text) -- Response body as string
|
||||||
|
```
|
|
@ -1,6 +1,8 @@
|
||||||
local Gio = require("lgi").Gio
|
local lgi = require("lgi")
|
||||||
|
local Gio = lgi.require("Gio", "2.0")
|
||||||
|
local GLib = lgi.require("GLib", "2.0")
|
||||||
local awful = require("awful")
|
local awful = require("awful")
|
||||||
local string = string
|
local gears = require("gears")
|
||||||
|
|
||||||
local _filesystem = {}
|
local _filesystem = {}
|
||||||
|
|
||||||
|
@ -22,14 +24,14 @@ function _filesystem.list_directory_files(path, exts, recursive)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Build a table of files from the path with the required extensions
|
-- Build a table of files from the path with the required extensions
|
||||||
local file_list = Gio.File.new_for_path(path):enumerate_children(
|
local file_list =
|
||||||
"standard::*",
|
Gio.File.new_for_path(path):enumerate_children("standard::*", 0)
|
||||||
0
|
|
||||||
)
|
|
||||||
if file_list then
|
if file_list then
|
||||||
for file in function()
|
for file in
|
||||||
|
function()
|
||||||
return file_list:next_file()
|
return file_list:next_file()
|
||||||
end do
|
end
|
||||||
|
do
|
||||||
local file_type = file:get_file_type()
|
local file_type = file:get_file_type()
|
||||||
if file_type == "REGULAR" then
|
if file_type == "REGULAR" then
|
||||||
local file_name = file:get_display_name()
|
local file_name = file:get_display_name()
|
||||||
|
@ -43,7 +45,7 @@ function _filesystem.list_directory_files(path, exts, recursive)
|
||||||
local file_name = file:get_display_name()
|
local file_name = file:get_display_name()
|
||||||
files = gears.table.join(
|
files = gears.table.join(
|
||||||
files,
|
files,
|
||||||
list_directory_files(file_name, exts, recursive)
|
_filesystem.list_directory_files(file_name, exts, recursive)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -53,10 +55,106 @@ function _filesystem.list_directory_files(path, exts, recursive)
|
||||||
end
|
end
|
||||||
|
|
||||||
function _filesystem.save_image_async_curl(url, filepath, callback)
|
function _filesystem.save_image_async_curl(url, filepath, callback)
|
||||||
awful.spawn.with_line_callback(string.format("curl -L -s %s -o %s", url, filepath),
|
awful.spawn.with_line_callback(
|
||||||
|
string.format("curl -L -s %s -o %s", url, filepath),
|
||||||
{
|
{
|
||||||
exit=callback
|
exit = callback,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param filepath string | Gio.File
|
||||||
|
---@param callback fun(content: string)
|
||||||
|
---@return nil
|
||||||
|
function _filesystem.read_file_async(filepath, callback)
|
||||||
|
if type(filepath) == "string" then
|
||||||
|
return _filesystem.read_file_async(
|
||||||
|
Gio.File.new_for_path(filepath),
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
elseif type(filepath) == "userdata" then
|
||||||
|
filepath:load_contents_async(nil, function(_, task)
|
||||||
|
local _, content, _ = filepath:load_contents_finish(task)
|
||||||
|
return callback(content)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param filepath string | Gio.File
|
||||||
|
---@return string?
|
||||||
|
function _filesystem.read_file_sync(filepath)
|
||||||
|
if type(filepath) == "string" then
|
||||||
|
return _filesystem.read_file_sync(Gio.File.new_for_path(filepath))
|
||||||
|
elseif type(filepath) == "userdata" then
|
||||||
|
local _, content, _ = filepath:load_contents()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param str string
|
||||||
|
local function tobytes(str)
|
||||||
|
local bytes = {}
|
||||||
|
|
||||||
|
for i = 1, #str do
|
||||||
|
table.insert(bytes, string.byte(str, i))
|
||||||
|
end
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param filepath string | Gio.File
|
||||||
|
---@param content string | string[] | GLib.Bytes
|
||||||
|
---@param callback? fun(file: Gio.File | userdata | nil): nil
|
||||||
|
function _filesystem.write_file_async(filepath, content, callback)
|
||||||
|
if type(filepath) == "string" then
|
||||||
|
return _filesystem.write_file_async(
|
||||||
|
Gio.File.new_for_path(filepath),
|
||||||
|
content,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
elseif type(content) == "string" then
|
||||||
|
return _filesystem.write_file_async(
|
||||||
|
filepath,
|
||||||
|
GLib.Bytes.new(tobytes(content)),
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
elseif type(content) == "table" then
|
||||||
|
return _filesystem.write_file(
|
||||||
|
filepath,
|
||||||
|
table.concat(content, "\n"),
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
elseif type(filepath) == "userdata" and type(content) == "userdata" then
|
||||||
|
callback = callback or function() end
|
||||||
|
|
||||||
|
return filepath:replace_contents_bytes_async(
|
||||||
|
content,
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
Gio.FileCreateFlags.REPLACE_DESTINATION,
|
||||||
|
nil,
|
||||||
|
function(_, task)
|
||||||
|
filepath:replace_contents_finish(task)
|
||||||
|
return callback(filepath)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _filesystem.file_exists(filepath)
|
||||||
|
if filepath then
|
||||||
|
return GLib.file_test(filepath, GLib.FileTest.EXISTS)
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _filesystem.dir_exists(filepath)
|
||||||
|
if filepath then
|
||||||
|
return GLib.file_test(filepath, GLib.FileTest.IS_DIR)
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return _filesystem
|
return _filesystem
|
||||||
|
|
|
@ -4,4 +4,5 @@ return {
|
||||||
filesystem = require(... .. ".filesystem"),
|
filesystem = require(... .. ".filesystem"),
|
||||||
shape = require(... .. ".shape"),
|
shape = require(... .. ".shape"),
|
||||||
time = require(... .. ".time"),
|
time = require(... .. ".time"),
|
||||||
|
requests = require(... .. ".requests"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
local lgi = require("lgi")
|
||||||
|
local Soup = lgi.require("Soup", "3.0")
|
||||||
|
local Gio = lgi.require("Gio", "2.0")
|
||||||
|
local GLib = lgi.require("GLib", "2.0")
|
||||||
|
local bit = require("bit")
|
||||||
|
|
||||||
|
local gears = require("gears")
|
||||||
|
|
||||||
|
---@class Response
|
||||||
|
---@field url string
|
||||||
|
---@field status_code number
|
||||||
|
---@field ok boolean
|
||||||
|
---@field reason_phrase string
|
||||||
|
---@field stream userdata
|
||||||
|
---@field text string
|
||||||
|
---@field bytes GLib.Bytes
|
||||||
|
local Response = {}
|
||||||
|
|
||||||
|
-- ---@return table
|
||||||
|
-- Response.json = function(self)
|
||||||
|
-- local json = require("lib.json")
|
||||||
|
-- return json.decode(self.text)
|
||||||
|
-- end
|
||||||
|
|
||||||
|
function Response.new(url, status_code, ok, reason_phrase, input_stream)
|
||||||
|
local self = setmetatable({}, Response)
|
||||||
|
self.url = url
|
||||||
|
self.status_code = status_code
|
||||||
|
self.ok = ok
|
||||||
|
self.reason_phrase = reason_phrase
|
||||||
|
self.stream = input_stream
|
||||||
|
self.text = ""
|
||||||
|
self.bytes = nil
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param params table<string, any>
|
||||||
|
---@param parent_key string | nil
|
||||||
|
---@return string
|
||||||
|
local function encode_query_params(params, parent_key)
|
||||||
|
local encoded_params = {}
|
||||||
|
local encoded_val, encoded_key
|
||||||
|
|
||||||
|
for key, value in pairs(params) do
|
||||||
|
local full_key = parent_key and (parent_key .. "[" .. key .. "]") or key
|
||||||
|
encoded_key = GLib.Uri.escape_string(tostring(full_key), nil, true)
|
||||||
|
|
||||||
|
if type(value) == "table" then
|
||||||
|
table.insert(encoded_params, encode_query_params(value, full_key))
|
||||||
|
else
|
||||||
|
encoded_val = GLib.Uri.escape_string(tostring(value), nil, true)
|
||||||
|
table.insert(encoded_params, encoded_key .. "=" .. encoded_val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return table.concat(encoded_params, "&")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class request
|
||||||
|
local requests = {}
|
||||||
|
|
||||||
|
---@alias request_args string | { url: string, params: table<string, any>, headers: table<string, string>, body: GLib.Bytes | string}
|
||||||
|
|
||||||
|
---@param method "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
|
||||||
|
---@param args request_args
|
||||||
|
---@param callback fun(response: Response): nil
|
||||||
|
---@return nil
|
||||||
|
function requests.request(method, args, callback)
|
||||||
|
if type(args) == "string" then
|
||||||
|
args = gears.table.crush(
|
||||||
|
{ url = "", params = {} },
|
||||||
|
{ url = args },
|
||||||
|
false
|
||||||
|
)
|
||||||
|
else
|
||||||
|
args = gears.table.crush({ url = "", params = {} }, args, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
local session, message, status_code, input_stream, ok, r, output_stream
|
||||||
|
|
||||||
|
session = Soup.Session.new()
|
||||||
|
|
||||||
|
if args.params then
|
||||||
|
args.url =
|
||||||
|
string.format("%s?%s", args.url, encode_query_params(args.params))
|
||||||
|
end
|
||||||
|
|
||||||
|
message = Soup.Message.new_from_uri(
|
||||||
|
method,
|
||||||
|
GLib.Uri.parse(args.url, GLib.UriFlags.NONE)
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.headers then
|
||||||
|
for header_name, header_value in pairs(args.headers) do
|
||||||
|
message:get_request_headers():append(header_name, header_value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(args.body) == "string" then
|
||||||
|
message.set_request_body_from_bytes(
|
||||||
|
nil,
|
||||||
|
GLib.Bytes.new({
|
||||||
|
string.byte(args.body, 1, #args.body),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
return session:send_async(
|
||||||
|
message,
|
||||||
|
GLib.PRIORITY_DEFAULT,
|
||||||
|
nil,
|
||||||
|
function(_, task)
|
||||||
|
input_stream = session:send_finish(task)
|
||||||
|
status_code = message.status_code
|
||||||
|
ok = status_code >= 200 and status_code < 300
|
||||||
|
|
||||||
|
r = Response.new(
|
||||||
|
message.uri:to_string(),
|
||||||
|
status_code,
|
||||||
|
ok,
|
||||||
|
message.reason_phrase,
|
||||||
|
input_stream
|
||||||
|
)
|
||||||
|
output_stream = Gio.MemoryOutputStream.new_resizable()
|
||||||
|
|
||||||
|
return output_stream:splice_async(
|
||||||
|
r.stream,
|
||||||
|
bit.bor(
|
||||||
|
Gio.OutputStreamSpliceFlags.CLOSE_SOURCE,
|
||||||
|
Gio.OutputStreamSpliceFlags.CLOSE_TARGET
|
||||||
|
),
|
||||||
|
GLib.PRIORITY_DEFAULT,
|
||||||
|
nil,
|
||||||
|
function(_, t)
|
||||||
|
output_stream:splice_finish(t)
|
||||||
|
r.bytes = output_stream:steal_as_bytes()
|
||||||
|
r.text = r.bytes:get_data()
|
||||||
|
output_stream:flush_async(GLib.PRIORITY_DEFAULT, nil)
|
||||||
|
return callback(r)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param args request_args
|
||||||
|
---@param callback fun(response:Response): nil
|
||||||
|
---@return nil
|
||||||
|
function requests.get(args, callback)
|
||||||
|
return requests.request("GET", args, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param args request_args
|
||||||
|
---@param callback fun(response:Response): nil
|
||||||
|
---@return nil
|
||||||
|
function requests.post(args, callback)
|
||||||
|
return requests.request("POST", args, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param args request_args
|
||||||
|
---@param callback fun(response:Response): nil
|
||||||
|
---@return nil
|
||||||
|
function requests.put(args, callback)
|
||||||
|
return requests.request("PUT", args, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param args request_args
|
||||||
|
---@param callback fun(response:Response): nil
|
||||||
|
---@return nil
|
||||||
|
function requests.delete(args, callback)
|
||||||
|
return requests.request("DELETE", args, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
return requests
|
Loading…
Reference in New Issue