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 string = string
|
||||
local gears = require("gears")
|
||||
|
||||
local _filesystem = {}
|
||||
|
||||
|
@ -22,14 +24,14 @@ function _filesystem.list_directory_files(path, exts, recursive)
|
|||
end
|
||||
|
||||
-- Build a table of files from the path with the required extensions
|
||||
local file_list = Gio.File.new_for_path(path):enumerate_children(
|
||||
"standard::*",
|
||||
0
|
||||
)
|
||||
local file_list =
|
||||
Gio.File.new_for_path(path):enumerate_children("standard::*", 0)
|
||||
if file_list then
|
||||
for file in function()
|
||||
return file_list:next_file()
|
||||
end do
|
||||
for file in
|
||||
function()
|
||||
return file_list:next_file()
|
||||
end
|
||||
do
|
||||
local file_type = file:get_file_type()
|
||||
if file_type == "REGULAR" then
|
||||
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()
|
||||
files = gears.table.join(
|
||||
files,
|
||||
list_directory_files(file_name, exts, recursive)
|
||||
_filesystem.list_directory_files(file_name, exts, recursive)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -53,10 +55,106 @@ function _filesystem.list_directory_files(path, exts, recursive)
|
|||
end
|
||||
|
||||
function _filesystem.save_image_async_curl(url, filepath, callback)
|
||||
awful.spawn.with_line_callback(string.format("curl -L -s %s -o %s", url, filepath),
|
||||
{
|
||||
exit=callback
|
||||
})
|
||||
awful.spawn.with_line_callback(
|
||||
string.format("curl -L -s %s -o %s", url, filepath),
|
||||
{
|
||||
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
|
||||
|
||||
return _filesystem
|
||||
|
|
|
@ -4,4 +4,5 @@ return {
|
|||
filesystem = require(... .. ".filesystem"),
|
||||
shape = require(... .. ".shape"),
|
||||
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