diff --git a/.travis.yml b/.travis.yml index f149166..81416bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,5 +17,5 @@ notifications: email: false after_success: - - julia -e 'cd(Pkg.dir("MetadataUtils")); Pkg.add("Coverage"); using Coverage; + - julia -e 'cd(Pkg.dir("FieldProperties")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' diff --git a/Project.toml b/Project.toml index 7b69715..b47ef9d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FieldProperties" uuid = "127e4479-4c95-493a-afaa-609d38175b70" authors = ["zchristensen "] -version = "0.3.3" +version = "0.3.4" [deps] Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" diff --git a/src/FieldProperties.jl b/src/FieldProperties.jl index a254207..16b0cb3 100644 --- a/src/FieldProperties.jl +++ b/src/FieldProperties.jl @@ -35,6 +35,7 @@ export nested, propdefault, propdoc, + propdoclist, propname, proptype, status, @@ -45,6 +46,10 @@ export include("defprop.jl") include("assignprops.jl") include("properties.jl") +include("proptype.jl") +include("propdefault.jl") +include("propconvert.jl") +include("propdoc.jl") include("public.jl") include("nested.jl") include("dictextension.jl") diff --git a/src/assignprops.jl b/src/assignprops.jl index 73b1c4b..f99b57f 100644 --- a/src/assignprops.jl +++ b/src/assignprops.jl @@ -10,7 +10,7 @@ function get_optional_properties_expr!(a, x::Expr) end end -get_optional_properties_expr!(a, x::AbstractArray) = append!(a, x[2:end]) +get_optional_properties_expr!(a, x::AbstractArray) = append!(a, esc.(x[2:end])) function parse_assignment(x::Expr) if x.head === :call @@ -34,10 +34,10 @@ function chain_ifelse!(blk::Expr, condition::Expr, trueout) chain_ifelse!(blk.args[end], condition, trueout) end elseif blk.head === :elseif - if blk.args[end] isa Expr - chain_ifelse!(blk.args[end], condition, trueout) - else + if length(blk.args) == 2 push!(blk.args, Expr(:elseif, condition, trueout)) + else + chain_ifelse!(blk.args[end], condition, trueout) end end end @@ -125,11 +125,7 @@ function def_sym2prop(struct_type, p, f) for (f_i, p_i) in zip(f,p) chain_ifelse!( blk, - Expr(:call, - :(===), - to_property_name(p_i), - esc(:s) - ), + Expr(:call, :(===), to_property_name(p_i), esc(:s)), Expr(:return, esc(to_property(p_i))) ) end @@ -158,7 +154,6 @@ function def_prop2field(struct_type, p, f) Expr(:return, f_i) ) end - final_out!(blk, Expr(:return, esc(:nothing))) Expr(:function, Expr(:call, diff --git a/src/propconvert.jl b/src/propconvert.jl new file mode 100644 index 0000000..77b8d47 --- /dev/null +++ b/src/propconvert.jl @@ -0,0 +1,14 @@ +""" + propconvert(p, v[, context]) + propconvert(p, s, v[, context]) + +Ensures the `v` is the appropriate type for property `p` given `x`. If it isn't +then `propconvert` attempts to convert to the "correct type". The "correct type" +is determined by `proptype(p, x)`. +""" +propconvert(p::AbstractProperty, v) = propconvert(p, propname(p), v) +propconvert(p::AbstractProperty, s, v) = _propconvert(p, s, v, proptype(p)) +propconvert(p::AbstractProperty, s, v, x) = _propconvert(p, s, v, proptype(p, x)) +_propconvert(p::AbstractProperty, s, v::V, ::Type{T}) where {T,V<:T} = v +_propconvert(p::AbstractProperty, s, v::V, ::Type{T}) where {T,V} = convert(T, v) + diff --git a/src/propdefault.jl b/src/propdefault.jl new file mode 100644 index 0000000..084246c --- /dev/null +++ b/src/propdefault.jl @@ -0,0 +1,10 @@ +""" + propdefault(p[, c]) + +Returns the default value for property `p` given the optinal context `c`. +""" +propdefault(p::AbstractProperty) = propdefault(p, not_property) +propdefault(::Type{P}) where {P<:AbstractProperty} = propdefault(P, not_property) +propdefault(p::AbstractProperty, context) = propdefault(typeof(p), context) +propdefault(::Type{<:AbstractProperty}, context) = not_property + diff --git a/src/propdoc.jl b/src/propdoc.jl new file mode 100644 index 0000000..e864f21 --- /dev/null +++ b/src/propdoc.jl @@ -0,0 +1,31 @@ +""" + propdoc(x) -> String + +Returns documentation for property `x`. +""" +propdoc(::T) where {T} = propdoc(T) +propdoc(::Type{P}) where {P<:AbstractProperty} = _extract_doc(Base.Docs.doc(P)) +function propdoc(::Type{T}) where {T} + pnames = assigned_fields(T) + return NamedTuple{pnames}(([propdoc(sym2prop(T, p)) for p in pnames]...,)) +end + +_extract_doc(x::Markdown.MD) = _extract_doc(x.content) +_extract_doc(x::AbstractArray) = isempty(x) ? "" : _extract_doc(first(x)) +_extract_doc(x::Markdown.Paragraph) = _extract_doc(x.content) +_extract_doc(x::String) = x + +""" + propdoclist(::T) -> String + +Returns a markdown list of properties, where `T` is a type that has been assigned +properties (see [`@assignprops`](@ref)) +""" +function propdoclist(x) + out = "" + for (p, d) in pairs(propdoc(x)) + out = out * "* $p: $d\n" + end + return out +end + diff --git a/src/properties.jl b/src/properties.jl index 2eb92b4..8aaad6c 100644 --- a/src/properties.jl +++ b/src/properties.jl @@ -48,58 +48,6 @@ Returns the symbolic name of a property. propname(::P) where {P} = propname(P) propname(::Type{<:AbstractProperty{name}}) where {name} = name -""" - propdefault(p[, c]) - -Returns the default value for property `p` given the optinal context `c`. -""" -propdefault(p::AbstractProperty) = propdefault(p, not_property) -propdefault(::Type{P}) where {P<:AbstractProperty} = propdefault(P, not_property) -propdefault(p::AbstractProperty, context) = propdefault(typeof(p), context) -propdefault(::Type{<:AbstractProperty}, context) = not_property - -""" - proptype(p[, context]) -> Type - -Return the appropriate type for property `p` given `context`. This method allows -unique type restrictions given different types for `context`. -""" -proptype(p::AbstractProperty) = proptype(typeof(p)) -proptype(::Type{P}) where {P<:AbstractProperty} = proptype(P, not_property) -proptype(p::AbstractProperty, context) = proptype(typeof(p), context) -proptype(::Type{<:AbstractProperty}, context) = Any - -""" - propdoc(x) - -Returns documentation for property `x`. -""" -propdoc(::T) where {T} = propdoc(T) -propdoc(::Type{P}) where {P<:AbstractProperty} = _extract_doc(Base.Docs.doc(P)) -function propdoc(::Type{T}) where {T} - pnames = assigned_fields(T) - return NamedTuple{pnames}(([propdoc(sym2prop(T, p)) for p in pnames]...,)) -end - -_extract_doc(x::Markdown.MD) = _extract_doc(x.content) -_extract_doc(x::AbstractArray) = isempty(x) ? "" : _extract_doc(first(x)) -_extract_doc(x::Markdown.Paragraph) = _extract_doc(x.content) -_extract_doc(x::String) = x - -""" - propconvert(p, v[, context]) - propconvert(p, s, v[, context]) - -Ensures the `v` is the appropriate type for property `p` given `x`. If it isn't -then `propconvert` attempts to convert to the "correct type". The "correct type" -is determined by `proptype(p, x)`. -""" -propconvert(p::AbstractProperty, v) = propconvert(p, propname(p), v) -propconvert(p::AbstractProperty, s, v) = _propconvert(p, s, v, proptype(p)) -propconvert(p::AbstractProperty, s, v, x) = _propconvert(p, s, v, proptype(p, x)) -_propconvert(p::AbstractProperty, s, v::V, ::Type{T}) where {T,V<:T} = v -_propconvert(p::AbstractProperty, s, v::V, ::Type{T}) where {T,V} = convert(T, v) - """ prop2field(x, p) -> Symbol diff --git a/src/proptype.jl b/src/proptype.jl new file mode 100644 index 0000000..297287e --- /dev/null +++ b/src/proptype.jl @@ -0,0 +1,11 @@ +""" + proptype(p[, context]) -> Type + +Return the appropriate type for property `p` given `context`. This method allows +unique type restrictions given different types for `context`. +""" +proptype(p::AbstractProperty) = proptype(typeof(p)) +proptype(::Type{P}) where {P<:AbstractProperty} = proptype(P, not_property) +proptype(p::AbstractProperty, context) = proptype(typeof(p), context) +proptype(::Type{<:AbstractProperty}, context) = Any +