diff --git a/.gitmodules b/.gitmodules index 6f7a96b..c7aedcf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,10 +1,10 @@ -[submodule "backend/vendor/hare-http"] +[submodule "vendor/hare-http"] path = vendor/hare-http url = https://git.fnordig.de/jer/hare-http.git branch = host-in-uri -[submodule "backend/vendor/hare-logfmt"] +[submodule "vendor/hare-logfmt"] path = vendor/hare-logfmt url = https://git.sr.ht/~blainsmith/hare-logfmt -[submodule "backend/vendor/hare-json"] +[submodule "vendor/hare-json"] path = vendor/hare-json url = https://git.sr.ht/~sircmpwn/hare-json diff --git a/vendor/hare-http b/vendor/hare-http new file mode 160000 index 0000000..f3a9625 --- /dev/null +++ b/vendor/hare-http @@ -0,0 +1 @@ +Subproject commit f3a96257c7fc3594a1ec2cde37c248730a174e6f diff --git a/vendor/hare-http/COPYING b/vendor/hare-http/COPYING deleted file mode 100644 index c257317..0000000 --- a/vendor/hare-http/COPYING +++ /dev/null @@ -1,367 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. diff --git a/vendor/hare-http/cmd/http/main.ha b/vendor/hare-http/cmd/http/main.ha deleted file mode 100644 index f640c24..0000000 --- a/vendor/hare-http/cmd/http/main.ha +++ /dev/null @@ -1,74 +0,0 @@ -use getopt; -use io; -use log; -use net::dial; -use net::http; -use net::uri; -use os; -use strings; - -const usage: [_]getopt::help = [ - "HTTP client", - ('H', "Name:value", "Sets an HTTP header"), - "url" -]; - -export fn main() void = { - const client = http::newclient("Hare net::http test client"); - defer http::client_finish(&client); - - const cmd = getopt::parse(os::args, usage...); - defer getopt::finish(&cmd); - - if (len(cmd.args) != 1) { - getopt::printusage(os::stderr, "http", usage)!; - os::exit(os::status::FAILURE); - }; - - const targ = match (uri::parse(cmd.args[0])) { - case let u: uri::uri => - yield u; - case uri::invalid => - log::fatal("Invalid URI"); - }; - defer uri::finish(&targ); - - let req = http::new_request(&client, "GET", &targ)!; - for (let i = 0z; i < len(cmd.opts); i += 1) { - const (opt, val) = cmd.opts[i]; - switch (opt) { - case 'H' => - const (name, val) = strings::cut(val, ":"); - http::header_add(&req.header, name, val); - case 'X' => - req.method = val; - case => abort(); - }; - }; - - const resp = match (http::do(&client, &req)) { - case let err: http::error => - log::fatal("HTTP error:", http::strerror(err)); - case let resp: http::response => - yield resp; - }; - defer http::response_finish(&resp); - - log::printfln("HTTP/{}.{}: {} {}", - resp.version.0, resp.version.1, - resp.status, resp.reason); - - for (let i = 0z; i < len(resp.header); i += 1) { - const (name, val) = resp.header[i]; - log::printfln("{}: {}", name, val); - }; - - const body = match (resp.body) { - case let st: *io::stream => - yield st; - case null => - return; - }; - io::copy(os::stdout, body)!; - io::close(body)!; -}; diff --git a/vendor/hare-http/cmd/httpd/main.ha b/vendor/hare-http/cmd/httpd/main.ha deleted file mode 100644 index 819a2f5..0000000 --- a/vendor/hare-http/cmd/httpd/main.ha +++ /dev/null @@ -1,92 +0,0 @@ -use getopt; -use net; -use net::ip; -use net::http; -use net::dial; -use os; -use memio; -use io; -use fmt; -use bufio; -use strings; - -const usage: [_]getopt::help = [ - "HTTP server", - ('a', "address", "listened address (ex: 127.0.0.1:8080)") -]; - -export fn main() void = { - const cmd = getopt::parse(os::args, usage...); - defer getopt::finish(&cmd); - - let port: u16 = 8080; - let ip_addr: ip::addr4 = [127, 0, 0, 1]; - - for (let i = 0z; i < len(cmd.opts); i += 1) { - const opt = cmd.opts[i]; - switch (opt.0) { - case 'a' => - match (dial::splitaddr(opt.1, "")) { - case let value: (str, u16) => - ip_addr = ip::parsev4(value.0)!; - port = value.1; - case dial::invalid_address => - abort("Invalid address"); - }; - case => abort(); // unreachable - }; - }; - - const server = match (http::listen(ip_addr, port)) { - case let this: *http::server => - yield this; - case net::error => abort("failure while listening"); - }; - defer http::server_finish(server); - - for (true) { - const serv_req = match (http::serve(server)) { - case let this: *http::server_request => - yield this; - case net::error => abort("failure while serving"); - }; - defer http::serve_finish(serv_req); - - let buf = memio::dynamic(); - defer io::close(&buf)!; - handlereq(&buf, &serv_req.request); - - http::response_write( - serv_req.socket, - http::STATUS_OK, - &buf, - ("Content-Type", "text/plain") - )!; - }; -}; - -export fn handlereq(buf: *io::stream, request: *http::request) void = { - fmt::fprintfln(buf, "Method: {}", request.method)!; - fmt::fprintfln(buf, "Path: {}", request.target.path)!; - fmt::fprintfln(buf, "Fragment: {}", request.target.fragment)!; - fmt::fprintfln(buf, "Query: {}", request.target.query)!; - fmt::fprintfln(buf, "Headers: < void; - case let body: io::handle => - fmt::fprintfln(buf, "Body: < - fmt::fprintln(buf, strings::fromutf8(line)!)!; - break; - case io::EOF => - break; - }; - }; - fmt::fprintfln(buf, "EOF")!; - }; -}; diff --git a/vendor/hare-http/net/http/README b/vendor/hare-http/net/http/README deleted file mode 100644 index 4180b4a..0000000 --- a/vendor/hare-http/net/http/README +++ /dev/null @@ -1,17 +0,0 @@ -net::http provides an implementation of an HTTP 1.1 client and server as defined -by RFC 9110 et al. - -TODO: Flesh me out - -Caveats: - -- No attempt is made to validate that the input for client requests or responses - are valid according to the HTTP grammar; such cases will fail when rejected by - the other party. -- Details indicated by RFC 7230 et al as "obsolete" are not implemented -- Max header length including "name: value" is 4KiB - -TODO: - -- Server stuff -- TLS diff --git a/vendor/hare-http/net/http/client.ha b/vendor/hare-http/net/http/client.ha deleted file mode 100644 index 26d3b68..0000000 --- a/vendor/hare-http/net/http/client.ha +++ /dev/null @@ -1,95 +0,0 @@ -use io; -use net::uri; - -export type client = struct { - default_header: header, - default_transport: transport, -}; - -// Creates a new HTTP [[client]] with the provided User-Agent string. -// -// The HTTP client implements a number of sane defaults, which may be tuned. The -// set of default headers is configured with [[client_default_header]], and the -// default transport behavior with [[client_default_transport]]. -// -// TODO: Implement and document the connection pool -// -// The caller must pass the client object to [[client_finish]] to free resources -// associated with this client after use. -export fn newclient(ua: str) client = { - let client = client { ... }; - header_add(&client, "User-Agent", ua); - return client; -}; - -// Frees resources associated with an HTTP [[client]]. -export fn client_finish(client: *client) void = { - header_free(&client.default_header); -}; - -// Returns the default headers used by this HTTP client, so that the user can -// examine or modify the net::http defaults (such as User-Agent or -// Accept-Encoding), or add their own. -export fn client_default_header(client: *client) *header = { - return &client.default_header; -}; - -// Returns the default [[transport]] configuration used by this HTTP client. -export fn client_default_transport(client: *client) *transport = { - return &client.default_transport; -}; - -fn uri_origin_form(target: *uri::uri) uri::uri = { - let target = *target; - target.scheme = ""; - target.host = ""; - target.fragment = ""; - target.userinfo = ""; - target.port = 0; - if (target.path == "") { - target.path = "/"; - }; - return target; -}; - -// Performs a synchronous HTTP GET request with the given client. -export fn get(client: *client, target: *uri::uri) (response | error) = { - const req = new_request(client, GET, target)?; - defer request_finish(&req); - return do(client, &req); -}; - -// Performs a synchronous HTTP HEAD request with the given client. -export fn head(client: *client, target: *uri::uri) (response | error) = { - const req = new_request(client, HEAD, target)?; - defer request_finish(&req); - return do(client, &req); -}; - -// Performs a synchronous HTTP POST request with the given client. -// -// If the provided I/O handle is seekable, the Content-Length header is added -// automatically. Otherwise, Transfer-Encoding: chunked will be used. -export fn post( - client: *client, - target: *uri::uri, - body: io::handle, -) (response | error) = { - const req = new_request_body(client, POST, target, body)?; - defer request_finish(&req); - return do(client, &req); -}; - -// Performs a synchronous HTTP PUT request with the given client. -// -// If the provided I/O handle is seekable, the Content-Length header is added -// automatically. Otherwise, Transfer-Encoding: chunked will be used. -export fn put( - client: *client, - target: *uri::uri, - body: io::handle, -) (response | error) = { - const req = new_request_body(client, PUT, target, body)?; - defer request_finish(&req); - return do(client, &req); -}; diff --git a/vendor/hare-http/net/http/constants.ha b/vendor/hare-http/net/http/constants.ha deleted file mode 100644 index da8bb1e..0000000 --- a/vendor/hare-http/net/http/constants.ha +++ /dev/null @@ -1,112 +0,0 @@ -// HTTP "GET" method. -export def GET: str = "GET"; -// HTTP "HEAD" method. -export def HEAD: str = "HEAD"; -// HTTP "POST" method. -export def POST: str = "POST"; -// HTTP "PUT" method. -export def PUT: str = "PUT"; -// HTTP "DELETE" method. -export def DELETE: str = "DELETE"; -// HTTP "OPTIONS" method. -export def OPTIONS: str = "OPTIONS"; -// HTTP "PATCH" method. -export def PATCH: str = "PATCH"; -// HTTP "CONNECT" method. -export def CONNECT: str = "CONNECT"; - -// HTTP "Continue" response status (100). -export def STATUS_CONTINUE: uint = 100; -// HTTP "Switching Protocols" response status (101). -export def STATUS_SWITCHING_PROTOCOLS: uint = 101; - -// HTTP "OK" response status (200). -export def STATUS_OK: uint = 200; -// HTTP "Created" response status (201). -export def STATUS_CREATED: uint = 201; -// HTTP "Accepted" response status (202). -export def STATUS_ACCEPTED: uint = 202; -// HTTP "Non-authoritative Info" response status (203). -export def STATUS_NONAUTHORITATIVE_INFO: uint = 203; -// HTTP "No Content" response status (204). -export def STATUS_NO_CONTENT: uint = 204; -// HTTP "Reset Content" response status (205). -export def STATUS_RESET_CONTENT: uint = 205; -// HTTP "Partial Content" response status (206). -export def STATUS_PARTIAL_CONTENT: uint = 206; - -// HTTP "Multiple Choices" response status (300). -export def STATUS_MULTIPLE_CHOICES: uint = 300; -// HTTP "Moved Permanently" response status (301). -export def STATUS_MOVED_PERMANENTLY: uint = 301; -// HTTP "Found" response status (302). -export def STATUS_FOUND: uint = 302; -// HTTP "See Other" response status (303). -export def STATUS_SEE_OTHER: uint = 303; -// HTTP "Not Modified" response status (304). -export def STATUS_NOT_MODIFIED: uint = 304; -// HTTP "Use Proxy" response status (305). -export def STATUS_USE_PROXY: uint = 305; - -// HTTP "Temporary Redirect" response status (307). -export def STATUS_TEMPORARY_REDIRECT: uint = 307; -// HTTP "Permanent Redirect" response status (308). -export def STATUS_PERMANENT_REDIRECT: uint = 308; - -// HTTP "Bad Request" response status (400). -export def STATUS_BAD_REQUEST: uint = 400; -// HTTP "Unauthorized" response status (401). -export def STATUS_UNAUTHORIZED: uint = 401; -// HTTP "Payment Required" response status (402). -export def STATUS_PAYMENT_REQUIRED: uint = 402; -// HTTP "Forbidden" response status (403). -export def STATUS_FORBIDDEN: uint = 403; -// HTTP "Not Found" response status (404). -export def STATUS_NOT_FOUND: uint = 404; -// HTTP "Method Not Allowed" response status (405). -export def STATUS_METHOD_NOT_ALLOWED: uint = 405; -// HTTP "Not Acceptable" response status (406). -export def STATUS_NOT_ACCEPTABLE: uint = 406; -// HTTP "Proxy Authentication Required" response status (407). -export def STATUS_PROXY_AUTH_REQUIRED: uint = 407; -// HTTP "Request Timeout" response status (408). -export def STATUS_REQUEST_TIMEOUT: uint = 408; -// HTTP "Conflict" response status (409). -export def STATUS_CONFLICT: uint = 409; -// HTTP "Gone" response status (410). -export def STATUS_GONE: uint = 410; -// HTTP "Length Required" response status (411). -export def STATUS_LENGTH_REQUIRED: uint = 411; -// HTTP "Precondition Failed" response status (412). -export def STATUS_PRECONDITION_FAILED: uint = 412; -// HTTP "Request Entity Too Large" response status (413). -export def STATUS_REQUEST_ENTITY_TOO_LARGE: uint = 413; -// HTTP "Request URI Too Long" response status (414). -export def STATUS_REQUEST_URI_TOO_LONG: uint = 414; -// HTTP "Unsupported Media Type" response status (415). -export def STATUS_UNSUPPORTED_MEDIA_TYPE: uint = 415; -// HTTP "Requested Range Not Satisfiable" response status (416). -export def STATUS_REQUESTED_RANGE_NOT_SATISFIABLE: uint = 416; -// HTTP "Expectation Failed" response status (417). -export def STATUS_EXPECTATION_FAILED: uint = 417; -// HTTP "I'm a Teapot" response status (418). -export def STATUS_TEAPOT: uint = 418; -// HTTP "Misdirected Request" response status (421). -export def STATUS_MISDIRECTED_REQUEST: uint = 421; -// HTTP "Unprocessable Entity" response status (422). -export def STATUS_UNPROCESSABLE_ENTITY: uint = 422; -// HTTP "Upgrade Required" response status (426). -export def STATUS_UPGRADE_REQUIRED: uint = 426; - -// HTTP "Internal Server Error" response status (500). -export def STATUS_INTERNAL_SERVER_ERROR: uint = 500; -// HTTP "Not Implemented" response status (501). -export def STATUS_NOT_IMPLEMENTED: uint = 501; -// HTTP "Bad Gateway" response status (502). -export def STATUS_BAD_GATEWAY: uint = 502; -// HTTP "Service Unavailable" response status (503). -export def STATUS_SERVICE_UNAVAILABLE: uint = 503; -// HTTP "Gateway Timeout" response status (504). -export def STATUS_GATEWAY_TIMEOUT: uint = 504; -// HTTP "HTTP Version Not Supported" response status (505). -export def STATUS_HTTP_VERSION_NOT_SUPPORTED: uint = 505; diff --git a/vendor/hare-http/net/http/do.ha b/vendor/hare-http/net/http/do.ha deleted file mode 100644 index 9b5905b..0000000 --- a/vendor/hare-http/net/http/do.ha +++ /dev/null @@ -1,144 +0,0 @@ -use bufio; -use encoding::utf8; -use errors; -use fmt; -use io; -use net::dial; -use net::uri; -use net; -use os; -use strconv; -use strings; -use types; - -// Performs an HTTP [[request]] with the given [[client]]. The request is -// performed synchronously; this function blocks until the server has returned -// the response status and all HTTP headers associated with the response. -// -// If the provided [[response]] has a non-null body, the user must pass it to -// [[io::close]] before calling [[response_finish]]. -export fn do(client: *client, req: *request) (response | error) = { - assert(req.target.scheme == "http"); // TODO: https - const conn = dial::dial_uri("tcp", req.target)?; - - let buf: [os::BUFSZ]u8 = [0...]; - let file = bufio::init(conn, [], buf); - bufio::setflush(&file, []); - - fmt::fprintf(&file, "{} ", req.method)?; - - // TODO: Support other request-targets than origin-form - const target = uri_origin_form(req.target); - uri::fmt(&file, &target)?; - fmt::fprintf(&file, " HTTP/1.1\r\n")?; - - write_header(&file, &req.header)?; - fmt::fprintf(&file, "\r\n")?; - bufio::flush(&file)?; - - const trans = match (req.transport) { - case let t: *transport => - yield t; - case => - yield &client.default_transport; - }; - // TODO: Implement None - assert(trans.request_transport == transport_mode::AUTO); - assert(trans.response_transport == transport_mode::AUTO); - assert(trans.request_content == content_mode::AUTO); - assert(trans.response_content == content_mode::AUTO); - - match (req.body) { - case let body: io::handle => - io::copy(conn, body)?; - case void => - yield; - }; - - let resp = response { ... }; - const scan = bufio::newscanner(conn, 512); - read_statusline(&resp, &scan)?; - read_header(&resp.header, &scan)?; - - const response_complete = - req.method == "HEAD" || - resp.status == STATUS_NO_CONTENT || - resp.status == STATUS_NOT_MODIFIED || - (resp.status >= 100 && resp.status < 200) || - (req.method == "CONNECT" && resp.status >= 200 && resp.status < 300); - if (!response_complete) { - resp.body = new_reader(conn, &resp, &scan)?; - } else if (req.method != "CONNECT") { - io::close(conn)!; - }; - return resp; -}; - -fn read_statusline( - resp: *response, - scan: *bufio::scanner, -) (void | error) = { - const status = match (bufio::scan_string(scan, "\r\n")) { - case let line: const str => - yield line; - case let err: io::error => - return err; - case utf8::invalid => - return protoerr; - case io::EOF => - return protoerr; - }; - - const tok = strings::tokenize(status, " "); - - const version = match (strings::next_token(&tok)) { - case let ver: str => - yield ver; - case done => - return protoerr; - }; - - const status = match (strings::next_token(&tok)) { - case let status: str => - yield status; - case done => - return protoerr; - }; - - const reason = match (strings::next_token(&tok)) { - case let reason: str => - yield reason; - case done => - return protoerr; - }; - - const (_, version) = strings::cut(version, "/"); - const (major, minor) = strings::cut(version, "."); - - const major = match (strconv::stou(major)) { - case let u: uint => - yield u; - case => - return protoerr; - }; - const minor = match (strconv::stou(minor)) { - case let u: uint => - yield u; - case => - return protoerr; - }; - resp.version = (major, minor); - - if (resp.version.0 > 1) { - return errors::unsupported; - }; - - resp.status = match (strconv::stou(status)) { - case let u: uint => - yield u; - case => - return protoerr; - }; - - resp.reason = strings::dup(reason); -}; diff --git a/vendor/hare-http/net/http/error.ha b/vendor/hare-http/net/http/error.ha deleted file mode 100644 index e0dbd1a..0000000 --- a/vendor/hare-http/net/http/error.ha +++ /dev/null @@ -1,26 +0,0 @@ -use errors; -use io; -use net::dial; - -// Errors possible while servicing HTTP requests. Note that these errors are for -// errors related to the processing of the HTTP connection; semantic HTTP errors -// such as [[STATUS_NOT_FOUND]] are not handled by this type. -export type error = !(dial::error | io::error | errors::unsupported | protoerr); - -// An HTTP protocol error occurred, indicating that the remote party is not -// conformant with HTTP semantics. -export type protoerr = !void; - -// Converts an [[error]] to a string. -export fn strerror(err: error) const str = { - match (err) { - case let err: dial::error => - return dial::strerror(err); - case let err: io::error => - return io::strerror(err); - case errors::unsupported => - return "Unsupported HTTP feature"; - case protoerr => - return "HTTP protocol error"; - }; -}; diff --git a/vendor/hare-http/net/http/header.ha b/vendor/hare-http/net/http/header.ha deleted file mode 100644 index 62510d4..0000000 --- a/vendor/hare-http/net/http/header.ha +++ /dev/null @@ -1,107 +0,0 @@ -use ascii; -use bufio; -use encoding::utf8; -use fmt; -use io; -use strings; - -// List of HTTP headers. -// TODO: [](str, []str) -export type header = [](str, str); - -// Adds a given HTTP header, which may be added more than once. The name should -// be canonicalized by the caller. -export fn header_add(head: *header, name: str, val: str) void = { - assert(len(name) >= 1 && len(val) >= 1); - let name = ascii::strlower(name); - append(head, (name, strings::dup(val))); -}; - -// Sets the value of a given HTTP header, removing any previous values. The name -// should be canonicalized by the caller. -export fn header_set(head: *header, name: str, val: str) void = { - header_del(head, name); - header_add(head, name, val); -}; - -// Removes an HTTP header from a list of [[header]]. If multiple headers match -// the given name, all matching headers are removed. -export fn header_del(head: *header, name: str) void = { - for (let i = 0z; i < len(head); i += 1) { - if (ascii::strcasecmp(head[i].0, name) == 0) { - free(head[i].0); - free(head[i].1); - delete(head[i]); - i -= 1; - }; - }; -}; - -// Retrieves a value, or values, from a header. An empty string indicates the -// absence of a header. -export fn header_get(head: *header, name: str) str = { - for (let i = 0z; i < len(head); i += 1) { - const (key, val) = head[i]; - if (ascii::strcasecmp(key, name) == 0) { - return val; - }; - }; - return ""; -}; - -// Frees state associated with an HTTP [[header]]. -export fn header_free(head: *header) void = { - for (let i = 0z; i < len(head); i += 1) { - free(head[i].0); - free(head[i].1); - }; - free(*head); -}; - -// Duplicates a set of HTTP headers. -export fn header_dup(head: *header) header = { - let new: header = []; - for (let i = 0z; i < len(head); i += 1) { - const (key, val) = head[i]; - header_add(&new, key, val); - }; - return new; -}; - -// Writes a list of HTTP headers to the provided I/O handle in the HTTP wire -// format. -export fn write_header(sink: io::handle, head: *header) (size | io::error) = { - let z = 0z; - for (let i = 0z; i < len(head); i += 1) { - const (name, val) = head[i]; - z += fmt::fprintf(sink, "{}: {}\r\n", name, val)?; - }; - return z; -}; - -fn read_header(head: *header, scan: *bufio::scanner) (void | io::error | protoerr) = { - for (true) { - const item = match (bufio::scan_string(scan, "\r\n")) { - case let line: const str => - yield line; - case io::EOF => - break; - case let err: io::error => - return err; - case utf8::invalid => - return protoerr; - }; - if (item == "") { - break; - }; - - let (name, val) = strings::cut(item, ":"); - val = strings::trim(val); - if (val == "") { - continue; - }; - // TODO: validate field-name - - header_add(head, name, val); - }; -}; diff --git a/vendor/hare-http/net/http/request.ha b/vendor/hare-http/net/http/request.ha deleted file mode 100644 index acc56ae..0000000 --- a/vendor/hare-http/net/http/request.ha +++ /dev/null @@ -1,278 +0,0 @@ -use errors; -use fmt; -use io; -use net::ip; -use net::uri; -use strconv; -use strings; -use bufio; -use memio; -use encoding::utf8; -use types; - -// Stores state related to an HTTP request. -// -// For a request to be processable by an HTTP [[client]], i.e. via [[do]], the -// method and target must be filled in appropriately. The target must include at -// least a host, port, and scheme. The default values for other fields are -// suitable if appropriate for the request you wish to perform. -export type request = struct { - // HTTP request method, e.g. GET - method: str, - // Request target URI. - // - // Note that the normal constraints for [[uri::parse]] are not upheld in - // the case of a request using the origin-form (e.g. /index.html), i.e. - // the scheme field may be empty. - target: *uri::uri, - - // List of HTTP request headers. - header: header, - - // Transport configuration, or null to use the [[client]] default. - transport: nullable *transport, - - // I/O reader for the request body, or void if there is no body. - body: (io::handle | void), -}; - -// Frees state associated with an HTTP [[request]]. -export fn request_finish(req: *request) void = { - header_free(&req.header); - uri::finish(req.target); - free(req.target); -}; - -// Creates a new HTTP [[request]] using the given HTTP [[client]] defaults. -export fn new_request( - client: *client, - method: str, - target: *uri::uri, -) (request | errors::unsupported) = { - let req = request { - method = method, - target = alloc(uri::dup(target)), - header = header_dup(&client.default_header), - transport = null, - body = void, - }; - switch (req.target.scheme) { - case "http" => - if (req.target.port == 0) { - req.target.port = 80; - }; - case "https" => - if (req.target.port == 0) { - req.target.port = 443; - }; - case => - return errors::unsupported; - }; - - let host = match (req.target.host) { - case let host: str => - yield host; - case let ip: ip::addr4 => - yield ip::string(ip); - case let ip: ip::addr6 => - static let buf: [64 + 2]u8 = [0...]; - yield fmt::bsprintf(buf, "[{}]", ip::string(ip)); - }; - - if (req.target.scheme == "http" && req.target.port != 80) { - host = fmt::asprintf("{}:{}", host, req.target.port); - } else if (target.scheme == "https" && target.port != 443) { - host = fmt::asprintf("{}:{}", host, req.target.port); - } else { - host = strings::dup(host); - }; - defer free(host); - header_add(&req.header, "Host", host); - return req; -}; - -// Creates a new HTTP [[request]] using the given HTTP [[client]] defaults and -// the provided request body. -// -// If the provided I/O handle is seekable, the Content-Length header is added -// automatically. Otherwise, Transfer-Encoding: chunked will be used. -export fn new_request_body( - client: *client, - method: str, - target: *uri::uri, - body: io::handle, -) (request | errors::unsupported) = { - let req = new_request(client, method, target)?; - req.body = body; - - const offs = match (io::seek(body, 0, io::whence::CUR)) { - case let off: io::off => - yield off; - case io::error => - header_add(&req.header, "Transfer-Encoding", "chunked"); - return req; - }; - const ln = io::seek(body, 0, io::whence::END)!; - io::seek(body, offs, io::whence::SET)!; - header_add(&req.header, "Content-Length", strconv::ztos(ln: size)); - return req; -}; - -export type request_server = void; -export type authority = void; - -export type request_uri = ( - request_server | - authority | - *uri::uri | - str | -); - -export type request_line = struct { - method: str, - uri: request_uri, - version: version, -}; - -export fn request_parse(file: io::handle) (request | protoerr | io::error) = { - const scan = bufio::newscanner(file, types::SIZE_MAX); - defer bufio::finish(&scan); - - const req_line = request_line_parse(&scan)?; - defer request_line_finish(&req_line); - - let header: header = []; - read_header(&header, &scan)?; - - const target = match (req_line.uri) { - case let uri: request_server => return errors::unsupported; - case let uri: authority => return errors::unsupported; - case let uri: *uri::uri => yield uri; - case let path: str => - const host = header_get(&header, "Host"); - const uri = fmt::asprintf("http://{}{}", host, path); - defer free(uri); - yield alloc(uri::parse(uri)!); - }; - - const length: (void | size) = void; - const head_length = header_get(&header, "Content-Length"); - if ("" != head_length) { - match (strconv::stoz(head_length)) { - case let s: size => length = s; - case strconv::invalid => return protoerr; - case strconv::overflow => return protoerr; - }; - }; - - let body: (io::handle | void) = void; - if (length is size) { - const limit = io::limitreader(&scan, length as size); - let _body = alloc(memio::dynamic()); - io::copy(_body, &limit)!; - io::seek(_body, 0, io::whence::SET)!; - body = _body; - }; - - return request { - method = req_line.method, - target = target, - header = header, - body = body, - ... - }; -}; - -export fn parsed_request_finish(request: *request) void = { - uri::finish(request.target); - free(request.target); - - match (request.body) { - case void => yield; - case let body: io::handle => - io::close(body)!; - free(body: *memio::stream); - }; - - header_free(&request.header); -}; - -export fn request_line_parse(scan: *bufio::scanner) (request_line | protoerr | io::error) = { - const line = match (bufio::scan_string(scan, "\r\n")) { - case let line: const str => - yield line; - case let err: io::error => - return err; - case utf8::invalid => - return protoerr; - case io::EOF => - return protoerr; - }; - - const tok = strings::tokenize(line, " "); - - const method = match (strings::next_token(&tok)) { - case let method: str => - yield strings::dup(method); - case done => - return protoerr; - }; - - const uri: request_uri = match (strings::next_token(&tok)) { - case let req_uri: str => - if ("*" == req_uri) { - yield request_server; - }; - - yield match (uri::parse(req_uri)) { - case let uri: uri::uri => yield alloc(uri); - case => yield strings::dup(req_uri); // as path - }; - case done => - return protoerr; - }; - - const version = match (strings::next_token(&tok)) { - case let ver: str => - yield ver; - case done => - return protoerr; - }; - - const (_, version) = strings::cut(version, "/"); - const (major, minor) = strings::cut(version, "."); - - const major = match (strconv::stou(major)) { - case let u: uint => - yield u; - case => - return protoerr; - }; - const minor = match (strconv::stou(minor)) { - case let u: uint => - yield u; - case => - return protoerr; - }; - - if (major > 1) { - return errors::unsupported; - }; - - if (uri is request_server && method != OPTIONS) { - return protoerr; - }; - - return request_line { - method = method, - uri = uri, - version = (major, minor), - }; -}; - -export fn request_line_finish(line: *request_line) void = { - match (line.uri) { - case let path: str => free(path); - case => yield; - }; -}; diff --git a/vendor/hare-http/net/http/response.ha b/vendor/hare-http/net/http/response.ha deleted file mode 100644 index 84f0c81..0000000 --- a/vendor/hare-http/net/http/response.ha +++ /dev/null @@ -1,63 +0,0 @@ -use io; -use os; -use fmt; -use strconv; - -export type version = (uint, uint); - -// Stores state related to an HTTP response. -export type response = struct { - // HTTP protocol version (major, minor) - version: version, - // The HTTP status for this request as an integer. - status: uint, - // The HTTP status reason phrase. - reason: str, - // The HTTP headers provided by the server. - header: header, - // The response body, if any. - body: nullable *io::stream, -}; - -// Frees state associated with an HTTP [[response]]. If the response has a -// non-null body, the user must call [[io::close]] prior to calling this -// function. -export fn response_finish(resp: *response) void = { - header_free(&resp.header); - free(resp.reason); - free(resp.body); -}; - -export fn response_write( - rw: io::handle, - status: uint, - body: (void | io::handle), - header: (str, str)... -) (void | io::error) = { - fmt::fprintfln(rw, "HTTP/1.1 {} {}", status, status_reason(status))?; - - let header = header_dup(&header); - defer header_free(&header); - - match (body) { - case void => void; - case let body: io::handle => - match (io::tell(body)) { - case io::error => void; - case let off: io::off => - header_add(&header, "Content-Length", strconv::i64tos(off)); - io::seek(body, 0, io::whence::SET)!; - body = &io::limitreader(body, off: size); - }; - }; - - write_header(rw, &header)?; - - fmt::fprintln(rw)!; - - match (body) { - case void => void; - case let body: io::handle => - io::copy(rw, body)!; - }; -}; diff --git a/vendor/hare-http/net/http/server.ha b/vendor/hare-http/net/http/server.ha deleted file mode 100644 index 42e21ac..0000000 --- a/vendor/hare-http/net/http/server.ha +++ /dev/null @@ -1,44 +0,0 @@ -use net; -use net::ip; -use net::tcp; -use net::tcp::{listen_option}; - -export type server = struct { - socket: net::socket, -}; - -export type server_request = struct { - socket: net::socket, - request: request, -}; - -export fn listen(ip: ip::addr, port: u16, options: listen_option...) (*server | net::error) = { - return alloc(server { - socket = tcp::listen(ip, port, options...)?, - }); -}; - -export fn server_finish(server: *server) void = { - net::close(server.socket)!; - free(server); -}; - -export fn accept(server: *server) (net::socket | net::error) = { - return net::accept(server.socket)?; -}; - -export fn serve(server: *server) (*server_request | net::error) = { - const socket = accept(server)?; - const request = request_parse(socket)!; - - return alloc(server_request { - request = request, - socket = socket, - }); -}; - -export fn serve_finish(serv_req: *server_request) void = { - parsed_request_finish(&serv_req.request); - net::close(serv_req.socket)!; - free(serv_req); -}; diff --git a/vendor/hare-http/net/http/status.ha b/vendor/hare-http/net/http/status.ha deleted file mode 100644 index bbb418a..0000000 --- a/vendor/hare-http/net/http/status.ha +++ /dev/null @@ -1,111 +0,0 @@ -// A semantic HTTP error and its status code. -export type httperror = !uint; - -// Checks if an HTTP status code is semantically considered an error, returning -// [[httperror]] if so, or otherwise returning the original status code. -export fn check(status: uint) (uint | httperror) = { - if (status >= 400 && status < 600) { - return status: httperror; - }; - return status; -}; - -// Converts a standard HTTP status code into the reason text typically -// associated with this status code (or "Unknown Status" if the status code is -// not known to net::http). -export fn status_reason(status: uint) const str = { - switch (status) { - case STATUS_CONTINUE => - return "Continue"; - case STATUS_SWITCHING_PROTOCOLS => - return "Switching Protocols"; - case STATUS_OK => - return "Continue"; - case STATUS_CREATED => - return "Continue"; - case STATUS_ACCEPTED => - return "Accepted"; - case STATUS_NONAUTHORITATIVE_INFO => - return "Non-Authoritative Information"; - case STATUS_NO_CONTENT => - return "No Content"; - case STATUS_RESET_CONTENT => - return "Reset Content"; - case STATUS_PARTIAL_CONTENT => - return "Partial Content"; - case STATUS_MULTIPLE_CHOICES => - return "Multiple Choices"; - case STATUS_MOVED_PERMANENTLY => - return "Moved Permanently"; - case STATUS_FOUND => - return "Found"; - case STATUS_SEE_OTHER => - return "See Other"; - case STATUS_NOT_MODIFIED => - return "Not Modified"; - case STATUS_USE_PROXY => - return "Use Proxy"; - case STATUS_TEMPORARY_REDIRECT => - return "Temporary Redirect"; - case STATUS_PERMANENT_REDIRECT => - return "Permanent Redirect"; - case STATUS_BAD_REQUEST => - return "Bad Request"; - case STATUS_UNAUTHORIZED => - return "Unauthorized"; - case STATUS_PAYMENT_REQUIRED => - return "Payment Required"; - case STATUS_FORBIDDEN => - return "Forbidden"; - case STATUS_NOT_FOUND => - return "Not Found"; - case STATUS_METHOD_NOT_ALLOWED => - return "Method Not Allowed"; - case STATUS_NOT_ACCEPTABLE => - return "Not Acceptable"; - case STATUS_PROXY_AUTH_REQUIRED => - return "Proxy Authentication Required"; - case STATUS_REQUEST_TIMEOUT => - return "Request Timeout"; - case STATUS_CONFLICT => - return "Conflict"; - case STATUS_GONE => - return "Gone"; - case STATUS_LENGTH_REQUIRED => - return "Length Required"; - case STATUS_PRECONDITION_FAILED => - return "Precondition Failed"; - case STATUS_REQUEST_ENTITY_TOO_LARGE => - return "Request Entity Too Large"; - case STATUS_REQUEST_URI_TOO_LONG => - return "Request URI Too Long"; - case STATUS_UNSUPPORTED_MEDIA_TYPE => - return "Unsupported Media Type"; - case STATUS_REQUESTED_RANGE_NOT_SATISFIABLE => - return "Requested Range Not Satisfiable"; - case STATUS_EXPECTATION_FAILED => - return "Expectation Failed"; - case STATUS_TEAPOT => - return "I'm A Teapot"; - case STATUS_MISDIRECTED_REQUEST => - return "Misdirected Request"; - case STATUS_UNPROCESSABLE_ENTITY => - return "Unprocessable Entity"; - case STATUS_UPGRADE_REQUIRED => - return "Upgrade Required"; - case STATUS_INTERNAL_SERVER_ERROR => - return "Internal Server Error"; - case STATUS_NOT_IMPLEMENTED => - return "Not Implemented"; - case STATUS_BAD_GATEWAY => - return "Bad Gateway"; - case STATUS_SERVICE_UNAVAILABLE => - return "Service Unavailable"; - case STATUS_GATEWAY_TIMEOUT => - return "Gateway Timeout"; - case STATUS_HTTP_VERSION_NOT_SUPPORTED => - return "HTTP Version Not Supported"; - case => - return "Unknown status"; - }; -}; diff --git a/vendor/hare-http/net/http/transport.ha b/vendor/hare-http/net/http/transport.ha deleted file mode 100644 index 6343ccf..0000000 --- a/vendor/hare-http/net/http/transport.ha +++ /dev/null @@ -1,296 +0,0 @@ -use errors; -use bufio; -use bytes; -use io; -use os; -use strconv; -use strings; -use types; - -// Configures the Transport-Encoding behavior. -// -// If set to NONE, no transport decoding or encoding is performed on the message -// body, irrespective of the value of the Transport-Encoding header. The user -// must perform any required encoding or decoding themselves in this mode. If -// set to AUTO, the implementation will examine the Transport-Encoding header -// and encode the message body appropriately. -// -// Most users will want this to be set to auto. -export type transport_mode = enum { - AUTO = 0, - NONE, -}; - -// Configures the Content-Encoding behavior. -// -// If set to NONE, no transport decoding or encoding is performed on the message -// body, irrespective of the value of the Content-Encoding header. The user must -// perform any required encoding or decoding themselves in this mode. If set to -// AUTO, the implementation will examine the Content-Encoding header and encode -// the message body appropriately. -// -// Most users will want this to be set to AUTO. -export type content_mode = enum { - AUTO = 0, - NONE, -}; - -// Describes an HTTP [[client]]'s transport configuration for a given request. -// -// The default value of this type sets all parameters to "auto". -export type transport = struct { - // Desired Transport-Encoding configuration, see [[transport_mode]] for - // details. - request_transport: transport_mode, - response_transport: transport_mode, - // Desired Content-Encoding configuration, see [[content_mode]] for - // details. - request_content: content_mode, - response_content: content_mode, -}; - -fn new_reader( - conn: io::file, - resp: *response, - scan: *bufio::scanner, -) (*io::stream | errors::unsupported | protoerr) = { - // TODO: Content-Encoding support - const cl = header_get(&resp.header, "Content-Length"); - const te = header_get(&resp.header, "Transfer-Encoding"); - - if (cl != "" || te == "") { - let length = types::SIZE_MAX; - if (cl != "") { - length = match (strconv::stoz(cl)) { - case let z: size => - yield z; - case => - return protoerr; - }; - }; - return new_identity_reader(conn, scan, length); - }; - - // TODO: Figure out the semantics for closing the stream - // The caller should probably be required to close it - // It should close/free any intermediate transport/content decoders - // And it should not close the actual connection if it's still in the - // connection pool - // Unless it isn't in the pool, then it should! - let stream: io::handle = conn; - let buffer: []u8 = bufio::scan_buffer(scan); - const iter = strings::tokenize(te, ","); - for (const tok => strings::next_token(&iter)) { - const te = strings::trim(tok); - - // XXX: We could add lzw support if someone added it to - // hare-compress - const next = switch (te) { - case "chunked" => - yield new_chunked_reader(stream, buffer); - case "deflate" => - abort(); // TODO - case "gzip" => - abort(); // TODO - case => - return errors::unsupported; - }; - stream = next; - - buffer = []; - }; - - if (!(stream is *io::stream)) { - // Empty Transfer-Encoding header - return protoerr; - }; - return stream as *io::stream; -}; - -type identity_reader = struct { - vtable: io::stream, - conn: io::file, - scan: *bufio::scanner, - src: io::limitstream, -}; - -const identity_reader_vtable = io::vtable { - reader = &identity_read, - closer = &identity_close, - ... -}; - -// Creates a new reader that reads data until the response's Content-Length is -// reached; i.e. the null Transport-Encoding. -fn new_identity_reader( - conn: io::file, - scan: *bufio::scanner, - content_length: size, -) *io::stream = { - const scan = alloc(*scan); - return alloc(identity_reader { - vtable = &identity_reader_vtable, - conn = conn, - scan = scan, - src = io::limitreader(scan, content_length), - ... - }); -}; - -fn identity_read( - s: *io::stream, - buf: []u8, -) (size | io::EOF | io::error) = { - let rd = s: *identity_reader; - assert(rd.vtable == &identity_reader_vtable); - return io::read(&rd.src, buf)?; -}; - -fn identity_close(s: *io::stream) (void | io::error) = { - let rd = s: *identity_reader; - assert(rd.vtable == &identity_reader_vtable); - - // Flush the remainder of the response in case the caller did not read - // it out entirely - io::copy(io::empty, &rd.src)?; - - bufio::finish(rd.scan); - free(rd.scan); - io::close(rd.conn)?; -}; - -type chunk_state = enum { - HEADER, - DATA, - FOOTER, -}; - -type chunked_reader = struct { - vtable: io::stream, - conn: io::handle, - buffer: [os::BUFSZ]u8, - state: chunk_state, - // Amount of read-ahead data in buffer - pending: size, - // Length of current chunk - length: size, -}; - -fn new_chunked_reader( - conn: io::handle, - buffer: []u8, -) *io::stream = { - let rd = alloc(chunked_reader { - vtable = &chunked_reader_vtable, - conn = conn, - ... - }); - rd.buffer[..len(buffer)] = buffer[..]; - rd.pending = len(buffer); - return rd; -}; - -const chunked_reader_vtable = io::vtable { - reader = &chunked_read, - ... -}; - -fn chunked_read( - s: *io::stream, - buf: []u8, -) (size | io::EOF | io::error) = { - // XXX: I am not satisfied with this code - let rd = s: *chunked_reader; - assert(rd.vtable == &chunked_reader_vtable); - - for (true) switch (rd.state) { - case chunk_state::HEADER => - let crlf = 0z; - for (true) { - const n = rd.pending; - match (bytes::index(rd.buffer[..n], ['\r', '\n'])) { - case let z: size => - crlf = z; - break; - case void => - yield; - }; - if (rd.pending >= len(rd.buffer)) { - // Chunk header exceeds buffer size - return errors::overflow; - }; - - match (io::read(rd.conn, rd.buffer[rd.pending..])?) { - case let n: size => - rd.pending += n; - case io::EOF => - if (rd.pending > 0) { - return errors::invalid; - }; - return io::EOF; - }; - }; - - // XXX: Should we do anything with chunk-ext? - const header = rd.buffer[..crlf]; - const (ln, _) = bytes::cut(header, ';'); - const ln = match (strings::fromutf8(ln)) { - case let s: str => - yield s; - case => - return errors::invalid; - }; - - match (strconv::stoz(ln, strconv::base::HEX)) { - case let z: size => - rd.length = z; - case => - return errors::invalid; - }; - if (rd.length == 0) { - return io::EOF; - }; - - const n = crlf + 2; - rd.buffer[..rd.pending - n] = rd.buffer[n..rd.pending]; - rd.pending -= n; - rd.state = chunk_state::DATA; - case chunk_state::DATA => - if (rd.pending == 0) { - match (io::read(rd.conn, rd.buffer)?) { - case let n: size => - rd.pending += n; - case io::EOF => - return io::EOF; - }; - }; - let n = len(buf); - if (n > rd.pending) { - n = rd.pending; - }; - if (n > rd.length) { - n = rd.length; - }; - buf[..n] = rd.buffer[..n]; - rd.buffer[..rd.pending - n] = rd.buffer[n..rd.pending]; - rd.pending -= n; - rd.length -= n; - rd.state = chunk_state::FOOTER; - return n; - case chunk_state::FOOTER => - for (rd.pending < 2) { - match (io::read(rd.conn, rd.buffer[rd.pending..])?) { - case let n: size => - rd.pending += n; - case io::EOF => - return io::EOF; - }; - }; - if (!bytes::equal(rd.buffer[..2], ['\r', '\n'])) { - return errors::invalid; - }; - rd.buffer[..rd.pending - 2] = rd.buffer[2..rd.pending]; - rd.pending -= 2; - rd.state = chunk_state::HEADER; - }; -}; diff --git a/vendor/hare-json b/vendor/hare-json new file mode 160000 index 0000000..b6aeae9 --- /dev/null +++ b/vendor/hare-json @@ -0,0 +1 @@ +Subproject commit b6aeae96199607a1f3b4d437d5c99f821bd6a6b6 diff --git a/vendor/hare-json/COPYING b/vendor/hare-json/COPYING deleted file mode 100644 index c257317..0000000 --- a/vendor/hare-json/COPYING +++ /dev/null @@ -1,367 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. diff --git a/vendor/hare-json/Makefile b/vendor/hare-json/Makefile deleted file mode 100644 index f9bcca5..0000000 --- a/vendor/hare-json/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -.POSIX: -.SUFFIXES: -HARE=hare -HAREFLAGS= -HAREDOC=haredoc - -DESTDIR= -PREFIX=/usr/local -SRCDIR=$(PREFIX)/src -HARESRCDIR=$(SRCDIR)/hare -THIRDPARTYDIR=$(HARESRCDIR)/third-party - -all: - @true # no-op - -check: - $(HARE) test - -clean: - rm -rf docs - -docs: - mkdir -p docs/encoding/json - $(HAREDOC) -Fhtml encoding > docs/encoding/index.html - $(HAREDOC) -Fhtml encoding::json > docs/encoding/json/index.html - -install: - mkdir -p "$(DESTDIR)$(THIRDPARTYDIR)"/encoding - mkdir -p "$(DESTDIR)$(THIRDPARTYDIR)"/encoding/json - install -m644 encoding/json/README "$(DESTDIR)$(THIRDPARTYDIR)"/encoding/json/README - install -m644 encoding/json/*.ha "$(DESTDIR)$(THIRDPARTYDIR)"/encoding/json - -uninstall: - rm -rf $(DESTDIR)$(THIRDPARTYDIR)/encoding/json - -.PHONY: all docs clean check install uninstall diff --git a/vendor/hare-json/README.md b/vendor/hare-json/README.md deleted file mode 100644 index 36cac8b..0000000 --- a/vendor/hare-json/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# hare-json - -This package provides JSON support for Hare. - -## Installation - -### From your distribution - -The recommended name for this package is "hare-json". Look for this, or -something similar, in your local package manager. This is the preferred way to -install this package. - -### System-wide installation - -``` -make install -``` - -### Vendoring - -``` -git subtree -P vendor/hare-json/ add https://git.sr.ht/~sircmpwn/hare-json master -``` diff --git a/vendor/hare-json/encoding/json/+test/lexer.ha b/vendor/hare-json/encoding/json/+test/lexer.ha deleted file mode 100644 index b4c098e..0000000 --- a/vendor/hare-json/encoding/json/+test/lexer.ha +++ /dev/null @@ -1,62 +0,0 @@ -use io; -use memio; -use strings; - -@test fn lex() void = { - const cases: [_](str, []token) = [ - ("true", [true]), - ("false", [false]), - ("null", [_null]), - ("1234", [1234.0]), - ("12.34", [12.34]), - ("12.34e5", [12.34e5]), - ("12.34E5", [12.34e5]), - ("12.34e+5", [12.34e5]), - ("12.34e-5", [12.34e-5]), - ("12e5", [12.0e5]), - ("-1234", [-1234.0]), - (`"hello world"`, ["hello world"]), - (`"\"\\\/\b\f\n\r\t\u0020"`, ["\"\\/\b\f\n\r\t\u0020"]), - ("[ null, null ]", [arraystart, _null, comma, _null, arrayend]), - ]; - - for (let i = 0z; i < len(cases); i += 1) { - const src = strings::toutf8(cases[i].0); - const src = memio::fixed(src); - const lexer = newlexer(&src); - defer close(&lexer); - - for (let j = 0z; j < len(cases[i].1); j += 1) { - const want = cases[i].1[j]; - const have = lex(&lexer)! as token; - assert(tokeq(want, have)); - }; - - assert(lex(&lexer) is io::EOF); - }; -}; - -fn tokeq(want: token, have: token) bool = { - match (want) { - case _null => - return have is _null; - case comma => - return have is comma; - case colon => - return have is colon; - case arraystart => - return have is arraystart; - case arrayend => - return have is arrayend; - case objstart => - return have is objstart; - case objend => - return have is objend; - case let b: bool => - return have as bool == b; - case let f: f64 => - return have as f64 == f; - case let s: str => - return have as str == s; - }; -}; diff --git a/vendor/hare-json/encoding/json/+test/test_load.ha b/vendor/hare-json/encoding/json/+test/test_load.ha deleted file mode 100644 index bf53777..0000000 --- a/vendor/hare-json/encoding/json/+test/test_load.ha +++ /dev/null @@ -1,164 +0,0 @@ -use fmt; - -fn roundtrip(input: str, expected: value) void = { - const val = loadstr(input)!; - defer finish(val); - assert(equal(val, expected)); - const s = dumpstr(val); - defer free(s); - const val = loadstr(s)!; - defer finish(val); - assert(equal(val, expected)); -}; - -fn errassert(input: str, expected_loc: (uint, uint)) void = { - const loc = loadstr(input) as invalid; - if (loc.0 != expected_loc.0 || loc.1 != expected_loc.1) { - fmt::errorfln("=== JSON:\n{}", input)!; - fmt::errorfln("=== expected error location:\n({}, {})", - expected_loc.0, expected_loc.1)!; - fmt::errorfln("=== actual error location:\n({}, {})", - loc.0, loc.1)!; - abort(); - }; -}; - -@test fn load() void = { - let obj = newobject(); - defer finish(obj); - let obj2 = newobject(); - defer finish(obj2); - - roundtrip(`1234`, 1234.0); - roundtrip(`[]`, []); - roundtrip(`[1, 2, 3, null]`, [1.0, 2.0, 3.0, _null]); - roundtrip(`{}`, obj); - set(&obj, "hello", "world"); - set(&obj, "answer", 42.0); - roundtrip(`{ "hello": "world", "answer": 42 }`, obj); - reset(&obj); - roundtrip(`[[] ]`, [[]]); - roundtrip(`[""]`, [""]); - roundtrip(`["a"]`, ["a"]); - roundtrip(`[false]`, [false]); - roundtrip(`[null, 1, "1", {}]`, [_null, 1.0, "1", obj]); - roundtrip(`[null]`, [_null]); - roundtrip("[1\n]", [1.0]); - roundtrip(`[1,null,null,null,2]`, [1.0, _null, _null, _null, 2.0]); - set(&obj, "", 0.0); - roundtrip(`{"":0}`, obj); - reset(&obj); - set(&obj, "foo\0bar", 42.0); - roundtrip(`{"foo\u0000bar": 42}`, obj); - reset(&obj); - set(&obj, "min", -1.0e+28); - set(&obj, "max", 1.0e+28); - roundtrip(`{"min": -1.0e+28, "max": 1.0e+28}`, obj); - reset(&obj); - set(&obj, "id", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - set(&obj2, "id", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - set(&obj, "x", [obj2]); - roundtrip(`{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}`, obj); - reset(&obj); - reset(&obj2); - set(&obj, "a", []); - roundtrip(`{"a":[]}`, obj); - roundtrip("{\n" `"a": []` "\n}", obj); - reset(&obj); - roundtrip(`"\u0060\u012a\u12AB"`, "\u0060\u012a\u12AB"); - roundtrip(`"\"\\\/\b\f\n\r\t"`, "\"\\/\b\f\n\r\t"); - roundtrip(`"\\u0000"`, `\u0000`); - roundtrip(`"\""`, `"`); - roundtrip(`"a/*b*/c/*d//e"`, "a/*b*/c/*d//e"); - roundtrip(`"\\a"`, `\a`); - roundtrip(`"\\n"`, `\n`); - roundtrip(`"\u0012"`, "\u0012"); - roundtrip(`[ "asd"]`, ["asd"]); - roundtrip(`"new\u000Aline"`, "new\nline"); - roundtrip(`"\u0000"`, "\0"); - roundtrip(`"\u002c"`, "\u002c"); - roundtrip(`"asd "`, "asd "); - roundtrip(`" "`, " "); - roundtrip(`"\u0821"`, "\u0821"); - roundtrip(`"\u0123"`, "\u0123"); - roundtrip(`"\u0061\u30af\u30EA\u30b9"`, "\u0061\u30af\u30EA\u30b9"); - roundtrip(`"\uA66D"`, "\uA66D"); - roundtrip(`"\u005C"`, `\`); - roundtrip(`"\u0022"`, `"`); - roundtrip(`""`, ""); - roundtrip(` [] `, []); - - errassert(`[1,,]`, (1, 4)); - errassert(`[1 true]`, (1, 7)); - errassert(`["": 1]`, (1, 4)); - errassert(`[,1]`, (1, 2)); - errassert(`[1,,2]`, (1, 4)); - errassert(`["",]`, (1, 5)); - errassert(`["x"`, (1, 5)); - errassert(`[x`, (1, 2)); - errassert(`[3[4]]`, (1, 3)); - errassert(`[1:2]`, (1, 3)); - errassert(`[,]`, (1, 2)); - errassert(`[-]`, (1, 3)); - errassert(`[ , ""]`, (1, 5)); - errassert("[\"a\",\n4\n,1,", (3, 4)); - errassert(`[1,]`, (1, 4)); - errassert("[\"\va\"\\f", (1, 3)); - errassert(`[*]`, (1, 2)); - errassert(`[1,`, (1, 4)); - errassert("[1,\n1\n,1", (3, 3)); - errassert(`[{}`, (1, 4)); - errassert(`["x", truth]`, (1, 11)); - errassert(`{[: "x"}`, (1, 2)); - errassert(`{"x", null}`, (1, 5)); - errassert(`{"x"::"b"}`, (1, 6)); - errassert(`{"a":"a" 123}`, (1, 12)); - errassert(`{"a" b}`, (1, 6)); - errassert(`{:"b"}`, (1, 2)); - errassert(`{"a" "b"}`, (1, 8)); - errassert(`{"a":`, (1, 6)); - errassert(`{"a"`, (1, 5)); - errassert(`{1:1}`, (1, 2)); - errassert(`{9999E9999:1}`, (1, 10)); - errassert(`{null:null,null:null}`, (1, 5)); - errassert(`{"id":0,,,,,}`, (1, 9)); - errassert(`{'a':0}`, (1, 2)); - errassert(`{"id":0,}`, (1, 9)); - errassert(`{"a":"b",,"c":"d"}`, (1, 10)); - errassert(`{true: false}`, (1, 5)); - errassert(`{"a":"a`, (1, 8)); - errassert(`{ "foo" : "bar", "a" }`, (1, 22)); - errassert(` `, (1, 2)); - errassert(``, (1, 1)); - errassert(`["asd]`, (1, 7)); - errassert(`True`, (1, 4)); - errassert(`]`, (1, 1)); - errassert(`}`, (1, 1)); - errassert(`{"x": true,`, (1, 12)); - errassert(`[`, (1, 2)); - errassert(`{`, (1, 2)); - errassert(``, (1, 1)); - errassert("\0", (1, 1)); - errassert(`{"":`, (1, 5)); - errassert(`['`, (1, 2)); - errassert(`["`, (1, 3)); - errassert(`[,`, (1, 2)); - errassert(`[{`, (1, 3)); - errassert(`{[`, (1, 2)); - errassert(`{]`, (1, 2)); - errassert(`[}`, (1, 2)); - errassert(`{'`, (1, 2)); - errassert(`{"`, (1, 3)); - errassert(`{,`, (1, 2)); - errassert(`["\{["\{["\{["\{`, (1, 4)); - errassert(`*`, (1, 1)); - errassert(`\u000A""`, (1, 1)); - errassert("\f", (1, 1)); -}; - -@test fn nestlimit() void = { - const s = `{ "foo": [[[{"bar": ["baz"]}]]] }`; - const val = loadstr(s, 6: nestlimit)!; - finish(val); - assert(loadstr(s, 5: nestlimit) is limitreached); -}; diff --git a/vendor/hare-json/encoding/json/+test/test_value.ha b/vendor/hare-json/encoding/json/+test/test_value.ha deleted file mode 100644 index eca7dcf..0000000 --- a/vendor/hare-json/encoding/json/+test/test_value.ha +++ /dev/null @@ -1,49 +0,0 @@ -// License: MPL-2.0 -// (c) 2022 Drew DeVault - -@test fn object() void = { - let obj = newobject(); - defer finish(obj); - - set(&obj, "hello", "world"); - set(&obj, "foo", "bar"); - set(&obj, "the answer", 42.0); - - // XXX: Match overhaul? - assert(*(get(&obj, "hello") as *value) as str == "world"); - assert(*(get(&obj, "foo") as *value) as str == "bar"); - assert(*(get(&obj, "the answer") as *value) as f64 == 42.0); - assert(get(&obj, "nonexistent") is void); - - del(&obj, "hello"); - assert(get(&obj, "hello") is void); -}; - -@test fn iterator() void = { - let obj = newobject(); - defer finish(obj); - - set(&obj, "hello", "world"); - set(&obj, "foo", "bar"); - set(&obj, "the answer", 42.0); - - let it = iter(&obj); - assert(next(&it) is (const str, const *value)); - assert(next(&it) is (const str, const *value)); - assert(next(&it) is (const str, const *value)); - assert(next(&it) is void); -}; - -@test fn equal() void = { - let a = newobject(); - defer finish(a); - set(&a, "a", 42.0); - set(&a, "A", "hello"); - - let b = newobject(); - defer finish(b); - set(&b, "A", "hello"); - set(&b, "a", 42.0); - - assert(equal(a, b)); -}; diff --git a/vendor/hare-json/encoding/json/README b/vendor/hare-json/encoding/json/README deleted file mode 100644 index fa917d5..0000000 --- a/vendor/hare-json/encoding/json/README +++ /dev/null @@ -1,15 +0,0 @@ -This module provides an implementation of the JavaScript Object Notation (JSON) -format, as defined by RFC 8259. Note that several other, incompatible -specifications exist. This implementation does not include any extensions; only -features which are strictly required by the spec are implemented. - -A lexer for JSON values is provided, which may be initialized with [[lex]] and -provides tokens via [[next]], and which uses a relatively small amount of memory -and provides relatively few guarantees regarding the compliance of the input with -the JSON grammar. - -Additionally, the [[value]] type is provided to store any value JSON value, as -well as helpers like [[newobject]], [[get]], and [[set]]. One can load a JSON -value from an input stream into a heap-allocated [[value]] via [[load]], which -enforces all of JSON's grammar constraints and returns an object which must be -freed with [[finish]]. diff --git a/vendor/hare-json/encoding/json/dump.ha b/vendor/hare-json/encoding/json/dump.ha deleted file mode 100644 index 7e7dd8d..0000000 --- a/vendor/hare-json/encoding/json/dump.ha +++ /dev/null @@ -1,81 +0,0 @@ -// License: MPL-2.0 -// (c) 2022 Sebastian -use fmt; -use io; -use strings; -use memio; - -// Dumps a [[value]] into an [[io::handle]] as a string without any additional -// formatting. -export fn dump(out: io::handle, val: value) (size | io::error) = { - let z = 0z; - match (val) { - case let v: (f64 | bool) => - z += fmt::fprint(out, v)?; - case let s: str => - z += fmt::fprint(out, `"`)?; - let it = strings::iter(s); - for (const r => strings::next(&it)) { - switch (r) { - case '\b' => - z += fmt::fprint(out, `\b`)?; - case '\f' => - z += fmt::fprint(out, `\f`)?; - case '\n' => - z += fmt::fprint(out, `\n`)?; - case '\r' => - z += fmt::fprint(out, `\r`)?; - case '\t' => - z += fmt::fprint(out, `\t`)?; - case '\"' => - z += fmt::fprint(out, `\"`)?; - case '\\' => - z += fmt::fprint(out, `\\`)?; - case => - if (iscntrl(r)) { - z += fmt::fprintf(out, `\u{:.4x}`, - r: u32)?; - } else { - z += fmt::fprint(out, r)?; - }; - }; - }; - z += fmt::fprint(out, `"`)?; - case _null => - z += fmt::fprint(out, "null")?; - case let a: []value => - z += fmt::fprint(out, "[")?; - for (let i = 0z; i < len(a); i += 1) { - z += dump(out, a[i])?; - if (i < len(a) - 1) { - z += fmt::fprint(out, ",")?; - }; - }; - z += fmt::fprint(out, "]")?; - case let o: object => - z += fmt::fprint(out, "{")?; - let comma = false; - let it = iter(&o); - for (true) match (next(&it)) { - case void => break; - case let pair: (const str, const *value) => - if (comma) { - z += fmt::fprint(out, ",")?; - }; - comma = true; - z += dump(out, pair.0)?; - z += fmt::fprint(out, ":")?; - z += dump(out, *pair.1)?; - }; - z += fmt::fprint(out, "}")?; - }; - return z; -}; - -// Dumps a [[value]] into a string without any additional formatting. The caller -// must free the return value. -export fn dumpstr(val: value) str = { - let s = memio::dynamic(); - dump(&s, val)!; - return memio::string(&s)!; -}; diff --git a/vendor/hare-json/encoding/json/lex.ha b/vendor/hare-json/encoding/json/lex.ha deleted file mode 100644 index 0f14a0c..0000000 --- a/vendor/hare-json/encoding/json/lex.ha +++ /dev/null @@ -1,377 +0,0 @@ -// License: MPL-2.0 -// (c) 2022 Drew DeVault -use ascii; -use bufio; -use encoding::utf8; -use io; -use os; -use strconv; -use strings; -use memio; - -export type lexer = struct { - src: io::handle, - strbuf: memio::stream, - un: (token | void), - rb: (rune | void), - loc: (uint, uint), - prevloc: (uint, uint), - nextloc: (uint, uint), - prevrloc: (uint, uint), -}; - -// Creates a new JSON lexer. The caller may obtain tokens with [[lex]] and -// should pass the result to [[close]] when they're done with it. -export fn newlexer(src: io::handle) lexer = lexer { - src = src, - strbuf = memio::dynamic(), - un = void, - rb = void, - loc = (1, 0), - ... -}; - -// Frees state associated with a JSON lexer. -export fn close(lex: *lexer) void = { - io::close(&lex.strbuf)!; -}; - -// Returns the next token from a JSON lexer. The return value is borrowed from -// the lexer and will be overwritten on subsequent calls. -export fn lex(lex: *lexer) (token | io::EOF | error) = { - match (lex.un) { - case void => - lex.prevloc = lex.loc; - case let tok: token => - lex.un = void; - lex.prevloc = lex.loc; - lex.loc = lex.nextloc; - return tok; - }; - - const rn = match (nextrunews(lex)?) { - case io::EOF => - return io::EOF; - case let rn: rune => - yield rn; - }; - - switch (rn) { - case '[' => - return arraystart; - case ']' => - return arrayend; - case '{' => - return objstart; - case '}' => - return objend; - case ',' => - return comma; - case ':' => - return colon; - case '"' => - return scan_str(lex)?; - case => - yield; - }; - - if (ascii::isdigit(rn) || rn == '-') { - unget(lex, rn); - return scan_number(lex)?; - }; - - if (!ascii::isalpha(rn)) { - return lex.loc: invalid; - }; - - unget(lex, rn); - const word = scan_word(lex)?; - switch (word) { - case "true" => - return true; - case "false" => - return false; - case "null" => - return _null; - case => - return lex.loc: invalid; - }; -}; - -// "Unlexes" a token from the lexer, such that the next call to [[lex]] will -// return that token again. Only one token can be unlexed at a time, otherwise -// the program will abort. -export fn unlex(lex: *lexer, tok: token) void = { - assert(lex.un is void, "encoding::json::unlex called twice in a row"); - lex.un = tok; - lex.nextloc = lex.loc; - lex.loc = lex.prevloc; -}; - -// Scans until encountering a non-alphabetical character, returning the -// resulting word. -fn scan_word(lex: *lexer) (str | error) = { - memio::reset(&lex.strbuf); - - for (true) { - const rn = match (nextrune(lex)?) { - case let rn: rune => - yield rn; - case io::EOF => - break; - }; - if (!ascii::isalpha(rn)) { - unget(lex, rn); - break; - }; - memio::appendrune(&lex.strbuf, rn)!; - }; - - return memio::string(&lex.strbuf)!; -}; - -type numstate = enum { - SIGN, - START, - ZERO, - INTEGER, - FRACSTART, - FRACTION, - EXPSIGN, - EXPSTART, - EXPONENT, -}; - -fn scan_number(lex: *lexer) (token | error) = { - memio::reset(&lex.strbuf); - - let state = numstate::SIGN; - for (true) { - const rn = match (nextrune(lex)?) { - case let rn: rune => - yield rn; - case io::EOF => - break; - }; - - switch (state) { - case numstate::SIGN => - state = numstate::START; - if (rn != '-') { - unget(lex, rn); - continue; - }; - case numstate::START => - switch (rn) { - case '0' => - state = numstate::ZERO; - case => - if (!ascii::isdigit(rn)) { - return lex.loc: invalid; - }; - state = numstate::INTEGER; - }; - case numstate::ZERO => - switch (rn) { - case '.' => - state = numstate::FRACSTART; - case 'e', 'E' => - state = numstate::EXPSIGN; - case => - if (ascii::isdigit(rn)) { - return lex.loc: invalid; - }; - unget(lex, rn); - break; - }; - case numstate::INTEGER => - switch (rn) { - case '.' => - state = numstate::FRACSTART; - case 'e', 'E' => - state = numstate::EXPSIGN; - case => - if (!ascii::isdigit(rn)) { - unget(lex, rn); - break; - }; - }; - case numstate::FRACSTART => - if (!ascii::isdigit(rn)) { - return lex.loc: invalid; - }; - state = numstate::FRACTION; - case numstate::FRACTION => - switch (rn) { - case 'e', 'E' => - state = numstate::EXPSIGN; - case => - if (!ascii::isdigit(rn)) { - unget(lex, rn); - break; - }; - }; - case numstate::EXPSIGN => - state = numstate::EXPSTART; - if (rn != '+' && rn != '-') { - unget(lex, rn); - continue; - }; - case numstate::EXPSTART => - if (!ascii::isdigit(rn)) { - return lex.loc: invalid; - }; - state = numstate::EXPONENT; - case numstate::EXPONENT => - if (!ascii::isdigit(rn)) { - unget(lex, rn); - break; - }; - }; - - memio::appendrune(&lex.strbuf, rn)!; - }; - - match (strconv::stof64(memio::string(&lex.strbuf)!)) { - case let f: f64 => - return f; - case => - return lex.loc: invalid; - }; -}; - -fn scan_str(lex: *lexer) (token | error) = { - memio::reset(&lex.strbuf); - - for (true) { - const rn = match (nextrune(lex)?) { - case let rn: rune => - yield rn; - case io::EOF => - lex.loc.1 += 1; - return lex.loc: invalid; - }; - - switch (rn) { - case '"' => - break; - case '\\' => - const rn = scan_escape(lex)?; - memio::appendrune(&lex.strbuf, rn)!; - case => - if (iscntrl(rn)) { - return lex.loc: invalid; - }; - memio::appendrune(&lex.strbuf, rn)!; - }; - }; - - return memio::string(&lex.strbuf)!; -}; - -fn scan_escape(lex: *lexer) (rune | error) = { - const rn = match (nextrune(lex)?) { - case let rn: rune => - yield rn; - case io::EOF => - return lex.loc: invalid; - }; - - switch (rn) { - case '\"' => - return '\"'; - case '\\' => - return '\\'; - case '/' => - return '/'; - case 'b' => - return '\b'; - case 'f' => - return '\f'; - case 'n' => - return '\n'; - case 'r' => - return '\r'; - case 't' => - return '\t'; - case 'u' => - let buf: [4]u8 = [0...]; - match (io::readall(lex.src, buf)?) { - case io::EOF => - return lex.loc: invalid; - case size => - yield; - }; - const s = match (strings::fromutf8(buf)) { - case let s: str => - yield s; - case => - return lex.loc: invalid; - }; - match (strconv::stou32(s, strconv::base::HEX)) { - case let u: u32 => - lex.loc.1 += 4; - return u: rune; - case => - return lex.loc: invalid; - }; - case => - return lex.loc: invalid; - }; -}; - -// Gets the next rune from the lexer. -fn nextrune(lex: *lexer) (rune | io::EOF | error) = { - if (lex.rb is rune) { - lex.prevrloc = lex.loc; - const r = lex.rb as rune; - lex.rb = void; - if (r == '\n') { - lex.loc = (lex.loc.0 + 1, 0); - } else { - lex.loc.1 += 1; - }; - return r; - }; - match (bufio::read_rune(lex.src)) { - case let err: io::error => - return err; - case utf8::invalid => - return lex.loc: invalid; - case io::EOF => - return io::EOF; - case let rn: rune => - lex.prevrloc = lex.loc; - if (rn == '\n') { - lex.loc = (lex.loc.0 + 1, 0); - } else { - lex.loc.1 += 1; - }; - return rn; - }; -}; - -// Like nextrune but skips whitespace. -fn nextrunews(lex: *lexer) (rune | io::EOF | error) = { - for (true) { - match (nextrune(lex)?) { - case let rn: rune => - if (isspace(rn)) { - continue; - }; - return rn; - case io::EOF => - return io::EOF; - }; - }; -}; - -fn unget(lex: *lexer, r: rune) void = { - assert(lex.rb is void); - lex.rb = r; - lex.loc = lex.prevrloc; -}; - -fn iscntrl(r: rune) bool = r: u32 < 0x20; - -fn isspace(r: rune) bool = ascii::isspace(r) && r != '\f'; diff --git a/vendor/hare-json/encoding/json/load.ha b/vendor/hare-json/encoding/json/load.ha deleted file mode 100644 index 8dc2b56..0000000 --- a/vendor/hare-json/encoding/json/load.ha +++ /dev/null @@ -1,148 +0,0 @@ -use memio; -use io; -use strings; -use types; - -// Options for [[load]]. -export type load_option = nestlimit; - -// The maximum number of nested objects or arrays that can be entered before -// erroring out. -export type nestlimit = uint; - -// Parses a JSON value from the given [[io::handle]], returning the value or an -// error. The return value is allocated on the heap; use [[finish]] to free it -// up when you're done using it. -// -// By default, this function assumes non-antagonistic inputs, and does not limit -// recursion depth or memory usage. You may want to set a custom [[nestlimit]], -// or incorporate an [[io::limitreader]] or similar. Alternatively, you can use -// the JSON lexer ([[lex]]) directly if dealing with potentially malicious -// inputs. -export fn load(src: io::handle, opts: load_option...) (value | error) = { - let limit = types::UINT_MAX; - for (let i = 0z; i < len(opts); i += 1) { - limit = opts[i]: nestlimit: uint; - }; - const lex = newlexer(src); - defer close(&lex); - return _load(&lex, 0, limit); -}; - -// Parses a JSON value from the given string, returning the value or an error. -// The return value is allocated on the heap; use [[finish]] to free it up when -// you're done using it. -// -// See the documentation for [[load]] for information on dealing with -// potentially malicious inputs. -export fn loadstr(input: str, opts: load_option...) (value | error) = { - let src = memio::fixed(strings::toutf8(input)); - return load(&src, opts...); -}; - -fn _load(lexer: *lexer, level: uint, limit: uint) (value | error) = { - const tok = mustscan(lexer)?; - match (tok) { - case _null => - return _null; - case let b: bool => - return b; - case let f: f64 => - return f; - case let s: str => - return strings::dup(s); - case arraystart => - if (level == limit) { - return limitreached; - }; - return _load_array(lexer, level + 1, limit); - case objstart => - if (level == limit) { - return limitreached; - }; - return _load_obj(lexer, level + 1, limit); - case (arrayend | objend | colon | comma) => - return lexer.loc: invalid; - }; -}; - -fn _load_array(lexer: *lexer, level: uint, limit: uint) (value | error) = { - let success = false; - let array: []value = []; - defer if (!success) finish(array); - let tok = mustscan(lexer)?; - match (tok) { - case arrayend => - success = true; - return array; - case => - unlex(lexer, tok); - }; - - for (true) { - append(array, _load(lexer, level, limit)?); - - tok = mustscan(lexer)?; - match (tok) { - case comma => void; - case arrayend => break; - case => - return lexer.loc: invalid; - }; - }; - success = true; - return array; -}; - -fn _load_obj(lexer: *lexer, level: uint, limit: uint) (value | error) = { - let success = false; - let obj = newobject(); - defer if (!success) finish(obj); - let tok = mustscan(lexer)?; - match (tok) { - case objend => - success = true; - return obj; - case => - unlex(lexer, tok); - }; - - for (true) { - let tok = mustscan(lexer)?; - const key = match (tok) { - case let s: str => - yield strings::dup(s); - case => - return lexer.loc: invalid; - }; - defer free(key); - - tok = mustscan(lexer)?; - if (!(tok is colon)) { - return lexer.loc: invalid; - }; - - put(&obj, key, _load(lexer, level, limit)?); - - tok = mustscan(lexer)?; - match (tok) { - case comma => void; - case objend => break; - case => - return lexer.loc: invalid; - }; - }; - - success = true; - return obj; -}; - -fn mustscan(lexer: *lexer) (token | error) = { - match (lex(lexer)?) { - case io::EOF => - lexer.loc.1 += 1; - return lexer.loc: invalid; - case let tok: token => - return tok; - }; -}; diff --git a/vendor/hare-json/encoding/json/path/path.ha b/vendor/hare-json/encoding/json/path/path.ha deleted file mode 100644 index 819e9f5..0000000 --- a/vendor/hare-json/encoding/json/path/path.ha +++ /dev/null @@ -1,26 +0,0 @@ -// A compiled JSONPath query. -export type query = []segment; - -export type segment_type = enum { - CHILD, - DESCENDANT, -}; - -export type segment = struct { - stype: segment_type, - selector: selector, -}; - -export type selector = (str | wild | index | slice | filter); - -export type wild = void; - -export type index = int; - -export type slice = struct { - start: (int | void), - end: (int | void), - step: (int | void), -}; - -export type filter = void; // TODO diff --git a/vendor/hare-json/encoding/json/types.ha b/vendor/hare-json/encoding/json/types.ha deleted file mode 100644 index 1e1b433..0000000 --- a/vendor/hare-json/encoding/json/types.ha +++ /dev/null @@ -1,50 +0,0 @@ -// License: MPL-2.0 -// (c) 2022 Drew DeVault -use fmt; -use io; - -// An invalid JSON token was encountered at this location (line, column). -export type invalid = !(uint, uint); - -// The maximum nesting limit was reached. -export type limitreached = !void; - -// A tagged union of all possible errors returned from this module. -export type error = !(invalid | limitreached | io::error); - -// The JSON null value. -export type _null = void; - -// The '[' token, signaling the start of a JSON array. -export type arraystart = void; - -// The ']' token, signaling the end of a JSON array. -export type arrayend = void; - -// The '{' token, signaling the start of a JSON object. -export type objstart = void; - -// The '}' token, signaling the end of a JSON object. -export type objend = void; - -// The ':' token. -export type colon = void; - -// The ',' token. -export type comma = void; - -// All tokens which can be returned from the JSON tokenizer. -export type token = (arraystart | arrayend | objstart | - objend | colon | comma | str | f64 | bool | _null); - -// Converts an [[error]] into a human-friendly string. -export fn strerror(err: error) const str = { - static let buf: [53]u8 = [0...]; - match (err) { - case let err: invalid => - return fmt::bsprintf(buf, - "{}:{}: Invalid JSON token encountered", err.0, err.1); - case let err: io::error => - return io::strerror(err); - }; -}; diff --git a/vendor/hare-json/encoding/json/value.ha b/vendor/hare-json/encoding/json/value.ha deleted file mode 100644 index 793915d..0000000 --- a/vendor/hare-json/encoding/json/value.ha +++ /dev/null @@ -1,219 +0,0 @@ -// License: MPL-2.0 -// (c) 2022 Drew DeVault -use hash::fnv; -use strings; - -// TODO: Resize table as appropriate -export def OBJECT_BUCKETS: size = 32; - -export type object = struct { - buckets: [OBJECT_BUCKETS][](str, value), - count: size, -}; - -// A JSON value. -export type value = (f64 | str | bool | _null | []value | object); - -// Initializes a new (empty) JSON object. Call [[finish]] to free associated -// resources when you're done using it. -export fn newobject() object = { - return object { ... }; -}; - -// Gets a value from a JSON object. The return value is borrowed from the -// object. -export fn get(obj: *object, key: str) (*value | void) = { - const hash = fnv::string(key); - const bucket = &obj.buckets[hash % len(obj.buckets)]; - for (let i = 0z; i < len(bucket); i += 1) { - if (bucket[i].0 == key) { - return &bucket[i].1; - }; - }; -}; - -// Sets a value in a JSON object. The key and value will be duplicated. -export fn set(obj: *object, key: const str, val: const value) void = { - put(obj, key, dup(val)); -}; - -// Sets a value in a JSON object. The key will be duplicated. The object will -// assume ownership over the value, without duplicating it. -export fn put(obj: *object, key: const str, val: const value) void = { - const hash = fnv::string(key); - const bucket = &obj.buckets[hash % len(obj.buckets)]; - for (let i = 0z; i < len(bucket); i += 1) { - if (bucket[i].0 == key) { - finish(bucket[i].1); - bucket[i].1 = val; - return; - }; - }; - obj.count += 1; - append(bucket, (strings::dup(key), val)); -}; - -// Deletes values from a JSON object, if they are present. -export fn del(obj: *object, keys: const str...) void = { - for (let i = 0z; i < len(keys); i += 1) { - match (take(obj, keys[i])) { - case let val: value => - finish(val); - case void => void; - }; - }; -}; - -// Deletes a key from a JSON object, returning its previous value, if any. -// The caller is responsible for freeing the value. -export fn take(obj: *object, key: const str) (value | void) = { - const hash = fnv::string(key); - const bucket = &obj.buckets[hash % len(obj.buckets)]; - for (let i = 0z; i < len(bucket); i += 1) { - if (bucket[i].0 == key) { - obj.count -= 1; - free(bucket[i].0); - const val = bucket[i].1; - delete(bucket[i]); - return val; - }; - }; -}; - -// Clears all values from a JSON object, leaving it empty. -export fn reset(obj: *object) void = { - let it = iter(obj); - for (true) match (next(&it)) { - case void => - break; - case let v: (const str, const *value) => - del(obj, v.0); - }; -}; - -// Returns the number of key/value pairs in a JSON object. -export fn count(obj: *object) size = { - return obj.count; -}; - -export type iterator = struct { - obj: *object, - i: size, - j: size, -}; - -// Creates an iterator that enumerates over the key/value pairs in an -// [[object]]. -export fn iter(obj: *object) iterator = { - return iterator { obj = obj, ... }; -}; - -// Returns the next key/value pair from this iterator, or void if none remain. -export fn next(iter: *iterator) ((const str, const *value) | void) = { - for (iter.i < len(iter.obj.buckets); iter.i += 1) { - const bucket = &iter.obj.buckets[iter.i]; - for (iter.j < len(bucket)) { - const key = bucket[iter.j].0; - const val = &bucket[iter.j].1; - iter.j += 1; - return (key, val); - }; - iter.j = 0; - }; -}; - -// Duplicates a JSON value. The caller must pass the return value to [[finish]] -// to free associated resources when they're done using it. -export fn dup(val: value) value = { - match (val) { - case let s: str => - return strings::dup(s); - case let v: []value => - let new: []value = alloc([], len(v)); - for (let i = 0z; i < len(v); i += 1) { - append(new, dup(v[i])); - }; - return new; - case let o: object => - let new = newobject(); - const i = iter(&o); - for (true) { - const pair = match (next(&i)) { - case void => - break; - case let pair: (const str, const *value) => - yield pair; - }; - set(&new, pair.0, *pair.1); - }; - return new; - case => - return val; - }; -}; - -// Checks two JSON values for equality. -export fn equal(a: value, b: value) bool = { - match (a) { - case _null => - return b is _null; - case let a: bool => - return b is bool && a == b as bool; - case let a: f64 => - return b is f64 && a == b as f64; - case let a: str => - return b is str && a == b as str; - case let a: []value => - if (!(b is []value)) return false; - const b = b as []value; - if (len(a) != len(b)) return false; - for (let i = 0z; i < len(a); i += 1) { - if (!equal(a[i], b[i])) { - return false; - }; - }; - return true; - case let a: object => - if (!(b is object)) return false; - let b = b as object; - if (count(&a) != count(&b)) { - return false; - }; - let a = iter(&a); - for (true) match (next(&a)) { - case let a: (const str, const *value) => - match (get(&b, a.0)) { - case let b: *value => - if (!equal(*a.1, *b)) { - return false; - }; - case void => return false; - }; - case void => break; - }; - return true; - }; -}; - -// Frees state associated with a JSON value. -export fn finish(val: value) void = { - match (val) { - case let s: str => - free(s); - case let v: []value => - for (let i = 0z; i < len(v); i += 1) { - finish(v[i]); - }; - free(v); - case let o: object => - for (let i = 0z; i < len(o.buckets); i += 1) { - const bucket = &o.buckets[i]; - for (let j = 0z; j < len(bucket); j += 1) { - free(bucket[j].0); - finish(bucket[j].1); - }; - free(*bucket); - }; - case => void; - }; -}; diff --git a/vendor/hare-logfmt b/vendor/hare-logfmt new file mode 160000 index 0000000..2b4a374 --- /dev/null +++ b/vendor/hare-logfmt @@ -0,0 +1 @@ +Subproject commit 2b4a37459be54c83ac6ac6354cddec3e9fa796bf diff --git a/vendor/hare-logfmt/LICENSE b/vendor/hare-logfmt/LICENSE deleted file mode 100644 index 64e9fec..0000000 --- a/vendor/hare-logfmt/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Blain Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/vendor/hare-logfmt/Makefile b/vendor/hare-logfmt/Makefile deleted file mode 100644 index 1267a8b..0000000 --- a/vendor/hare-logfmt/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -.POSIX: -.SUFFIXES: -HARE=hare -HAREFLAGS= - -DESTDIR= -PREFIX=/usr/local -SRCDIR=$(PREFIX)/src -HARESRCDIR=$(SRCDIR)/hare -THIRDPARTYDIR=$(HARESRCDIR)/third-party - -all: - # no-op - -clean: - # no-op - -check: - $(HARE) test - -install: - mkdir -p $(DESTDIR)$(THIRDPARTYDIR)/log/logfmt - install -m644 * $(DESTDIR)$(THIRDPARTYDIR) - -uninstall: - rm -rf $(DESTDIR)$(THIRDPARTYDIR)/log/logfmt - -.PHONY: all clean check install uninstall \ No newline at end of file diff --git a/vendor/hare-logfmt/README.md b/vendor/hare-logfmt/README.md deleted file mode 100644 index 0c02d90..0000000 --- a/vendor/hare-logfmt/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# hare-logfmt - -A logfmt logger that can be used in [`log::setlogger(*logger) void`](https://docs.harelang.org/log#setlogger) in Hare. - -## Usage - -```hare -use logfmt; -use log; - -export fn main() void = { - // create an instance of the logger - let l = logfmt::new(os::stderr); - - // set the global logger to the logfmt logger - log::setlogger(&l); - - // use the normal log::println function - log::println("request_uri", "/", "method", "POST", "user_id", 123); - log::println("request_uri", "/sign-in", "method", "GET"); - log::println("request_uri", "/dashboard", "method", "GET", "user_id", 123); -}; -``` - -**Output** - -```console -ts=2022-05-12T09:36:27-0400 request_uri=/ method=POST user_id=123 -ts=2022-05-12T09:42:27-0400 request_uri=/sign-in method=GET -ts=2022-05-12T09:52:10-0400 request_uri=/dashboard method=GET user_id=123 -``` - -You can also run `haredoc` to read the module documentation. - -```console -> haredoc -// Implements the log::logger for outputting logs in Logfmt format. -type logfmtlogger = struct { - log::logger, - handle: io::handle, -}; - -// creates a new instace of logfmtlogger to be use with [[log::setlogger]]. -fn new(handle: io::handle) logfmtlogger; -``` \ No newline at end of file diff --git a/vendor/hare-logfmt/log/logfmt/+test.ha b/vendor/hare-logfmt/log/logfmt/+test.ha deleted file mode 100644 index be6d39f..0000000 --- a/vendor/hare-logfmt/log/logfmt/+test.ha +++ /dev/null @@ -1,48 +0,0 @@ -use log; -use io; -use os; -use strings; -use fmt; - -@test fn logfmt() void = { - let s = teststream_open(); - - let l = new(&s); - - log::setlogger(&l); - log::println("request_uri", "/", "method", "POST", "user_id", 123); - - let sbuf = strings::fromutf8(s.buf)!; - - assert(strings::contains(sbuf, "request_uri=/ method=POST user_id=123")); - - free(sbuf); -}; - -const teststream_vtable: io::vtable = io::vtable { - reader = &teststream_read, - writer = &teststream_write, - ... -}; - -type teststream = struct { - stream: io::stream, - buf: []u8, -}; - -fn teststream_open() teststream = teststream { - stream = &teststream_vtable, - ... -}; - -fn teststream_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { - let stream = s: *teststream; - buf = stream.buf; - return len(buf); -}; - -fn teststream_write(s: *io::stream, buf: const []u8) (size | io::error) = { - let stream = s: *teststream; - append(stream.buf, buf...); - return len(buf); -}; \ No newline at end of file diff --git a/vendor/hare-logfmt/log/logfmt/logfmt.ha b/vendor/hare-logfmt/log/logfmt/logfmt.ha deleted file mode 100644 index 496f6de..0000000 --- a/vendor/hare-logfmt/log/logfmt/logfmt.ha +++ /dev/null @@ -1,64 +0,0 @@ -use io; -use log; -use fmt; -use time::date; -use os; -use encoding::utf8; -use strings; - -// Implements the log::logger for outputting logs in Logfmt format. -export type logfmtlogger = struct { - log::logger, - handle: io::handle, -}; - -// creates a new instace of logfmtlogger to be use with [[log::setlogger]]. -export fn new(handle: io::handle) logfmtlogger = { - return logfmtlogger { - println = &log_println, - printfln = &log_printfln, - handle = handle, - }; -}; - -fn log_println(logger: *log::logger, fields: fmt::formattable...) void = { - const logger = logger: *logfmtlogger; - assert(logger.println == &log_println); - - const now = date::now(); - fmt::fprint(logger.handle, "ts="): void; - date::format(logger.handle, date::RFC3339, &now): void; - fmt::fprint(logger.handle, " "): void; - - for (let i = 0z; i < len(fields); i+= 1) { - if (i % 2 == 0) { - fmt::fprint(logger.handle, fields[i]): void; - fmt::fprint(logger.handle, "="): void; - } else { - fmt::fprint(logger.handle, fields[i]): void; - fmt::fprint(logger.handle, " "): void; - }; - }; - fmt::fprintln(logger.handle, ""): void; -}; - -fn log_printfln(logger: *log::logger, fmt: str, fields: fmt::field...) void = { - const logger = logger: *logfmtlogger; - assert(logger.printfln == &log_printfln); - - const now = date::now(); - fmt::fprint(logger.handle, "ts="): void; - date::format(logger.handle, date::RFC3339, &now): void; - fmt::fprint(logger.handle, " "): void; - - for (let i = 0z; i < len(fields); i+= 1) { - if (i % 2 == 0) { - fmt::fprintf(logger.handle, "{}", fields[i]): void; - fmt::fprint(logger.handle, "="): void; - } else { - fmt::fprintf(logger.handle, "{}", fields[i]): void; - fmt::fprint(logger.handle, " "): void; - }; - }; - fmt::fprintln(logger.handle, ""): void; -}; \ No newline at end of file