diff --git a/src/00_preliminary/01_general/01_auxiliary.jl b/src/00_preliminary/01_general/01_auxiliary.jl index e14632d..530d16c 100644 --- a/src/00_preliminary/01_general/01_auxiliary.jl +++ b/src/00_preliminary/01_general/01_auxiliary.jl @@ -1,3 +1,15 @@ +export ⊕ +export uniquesort! + +const uniquesort! = unique! ∘ sort! + +function isalphanumeric(char::AbstractChar) + '0' ≤ char ≤ '9' && return true + 'a' ≤ char ≤ 'z' && return true + 'A' ≤ char ≤ 'Z' && return true + return false +end + # TODO: Update when Unitful.jl is incorporated into package. function ⊕(levels...) 10log10( diff --git a/src/00_preliminary/01_general/02_text_cases.jl b/src/00_preliminary/01_general/02_text_cases.jl index e69de29..f479ca8 100644 --- a/src/00_preliminary/01_general/02_text_cases.jl +++ b/src/00_preliminary/01_general/02_text_cases.jl @@ -0,0 +1,127 @@ +export textcase +export titletext +export snaketext +export pascaltext +# public keeptokens + +const textcaseseps = ( + snake = '_', + space = ' ', + kebab = '-' +) + +""" +Text conversions +""" +const keeptokens = [ + "a" + "to" + "the" + "NSW" +] |> uniquesort! + +function _textcase_casify_token( + newcase::AbstractString, + token::AbstractString; + keeptokens::AbstractVector{<:AbstractString} = keeptokens +) + token = lowercase(token) + + idx = findall(token .== lowercase.(keeptokens)) + length(idx) > 1 && error("Non-unique `keeptokens` specified.") + return if !isempty(idx) + keeptoken = keeptokens[idx |> only] + if newcase == "Pascal" + uppercase(keeptoken[1]) * (length(keeptoken) > 1 ? keeptoken[2:end] : "") + else + keeptoken + end + elseif isuppercase(newcase[1]) + uppercase(token[1]) * ( + length(token) > 1 ? token[2:end] : "" + ) + else + token + end +end + +function _textcase_verify_case(::Val{C}) where {C} + @assert C isa Symbol + return String(C) +end + +function textcase( + v::V, + text::AbstractString; + keeptokens::AbstractVector{<:AbstractString} = keeptokens +) where { + V <: Union{ + Val{:snake}, Val{:space}, Val{:kebab}, + Val{:Snake}, Val{:Space}, Val{:Kebab}, + Val{:Pascal} + } +} + tempsep = textcaseseps.snake + + # Convert camel word separations to snake separations + camel_regexes = [ + "[a-z][A-Z]" + "[0-9][A-Z]" + "[a-z][0-9]" + "[A-Z][0-9]" + ] .|> Regex + for camel_regex = camel_regexes + text = replace(text, + [ + text[idxs] => text[idxs[begin]] * tempsep * text[idxs[end]] + for idxs in findall(camel_regex, text, overlap = true) + ]... + ) + end + + # Normalise existing separators + normaliser_regex = r"(_| |-)(_| |-)" + while contains(text, normaliser_regex) + text = replace(text, normaliser_regex => tempsep) + end + text = replace(text, "-" => tempsep) + text = replace(text, " " => tempsep) + + # Remove non-alphanumeric symbols + is_alphanumeric_or_tempsep(char::AbstractChar) = (isalphanumeric(char) || char == tempsep) + text = filter(is_alphanumeric_or_tempsep, text) + + # Tokenise + texts = split(text, tempsep) + + newcase = _textcase_verify_case(v) + texts = _textcase_casify_token.(newcase, texts, keeptokens = keeptokens) + + sep = if newcase == "Pascal" + "" + else + textcaseseps[newcase |> lowercase |> Symbol] + end + return join(texts, sep) +end + +function textcase(::Val{:camel}, text::AbstractString; keeptokens = keeptokens) + text = textcase(Val(:Pascal), text; keeptokens = keeptokens) + return lowercase(text[1]) * (length(text) > 1 ? text[2:end] : "") +end + +function textcase(::Val{:pascal}, args...; kw...) + return textcase(Val(:Pascal), args...; kw...) +end + +function textcase(::Val{:title}, args...; kw...) + return textcase(Val(:Space), args...; kw...) +end + +titletext(text::AbstractString) = textcase(Val(:title), text) +snaketext(text::AbstractString) = textcase(Val(:snake), text) +pascaltext(text::AbstractString) = textcase(Val(:pascal), text) + +prettytext(text::AbstractString) = titletext(text) +prettytext(text::Symbol) = text |> String |> prettytext +prettytext(::Val{T}) where {T} = T |> prettytext \ No newline at end of file diff --git a/src/00_preliminary/02_modelling/01_model_naming.jl b/src/00_preliminary/02_modelling/01_model_naming.jl index 46bd90a..fea6f24 100644 --- a/src/00_preliminary/02_modelling/01_model_naming.jl +++ b/src/00_preliminary/02_modelling/01_model_naming.jl @@ -2,13 +2,11 @@ export ModelName struct ModelName{m} end -ModelName(name::Symbol) = ModelName{name}() - -# ModelName(m::AbstractString) = ModelName{m |> snakecase |> Symbol}() -# ModelName(m::Symbol) = ModelName(m |> String) +ModelName(m::AbstractString) = ModelName{m |> pascaltext |> Symbol}() +ModelName(m::Symbol) = ModelName(m |> String) Symbol(::ModelName{M}) where {M} = M -String(model::ModelName) = model |> ModelName |> String +String(model::ModelName) = model |> Symbol |> String -# titletext(model::ModelName) = model |> String |> titletext -# snaketext(model::ModelName) = model |> String |> snaketext \ No newline at end of file +titletext(model::ModelName) = model |> Symbol |> titletext +snaketext(model::ModelName) = model |> Symbol |> snaketext diff --git a/src/OceanSonar.jl b/src/OceanSonar.jl index 8c8bcfb..6296103 100644 --- a/src/OceanSonar.jl +++ b/src/OceanSonar.jl @@ -6,7 +6,11 @@ export print_tree using AbstractTrees: print_tree import AbstractTrees: children -import Base: getproperty, show +import Base: + getproperty, + show, + String, + Symbol using InteractiveUtils: subtypes diff --git a/test/00_preliminary/text_cases.jl b/test/00_preliminary/text_cases.jl new file mode 100644 index 0000000..e871f53 --- /dev/null +++ b/test/00_preliminary/text_cases.jl @@ -0,0 +1,20 @@ +using OceanSonar +using Test + +texts = ( + Space = "Say 32 Big Goodbyes to 1 Cruel NSW 1st World", + space = "say 32 big goodbyes to 1 cruel NSW 1st world", + Snake = "Say_32_Big_Goodbyes_to_1_Cruel_NSW_1st_World", + snake = "say_32_big_goodbyes_to_1_cruel_NSW_1st_world", + Kebab = "Say-32-Big-Goodbyes-to-1-Cruel-NSW-1st-World", + kebab = "say-32-big-goodbyes-to-1-cruel-NSW-1st-world", + pascal = "Say32BigGoodbyesTo1CruelNSW1stWorld", + camel = "say32BigGoodbyesTo1CruelNSW1stWorld", +) + +@testset "From $oldcase" for (oldcase, oldtext) in pairs(texts) + @testset "To $newcase" for (newcase, newtext) in pairs(texts) + context = textcase(newcase |> Symbol |> Val, oldtext) + @test context == newtext + end +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 3b48d28..a3c5393 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,6 +10,12 @@ using SafeTestsets @safetestset "Docstrings" include("00_preliminary/01_code_quality/docstrings.jl") end + name = "Preliminary Tests" + @time @testset "$name" begin + @info "Testing $name" + @safetestset "Text Cases" include("00_preliminary/text_cases.jl") + end + name = "Postliminary Tests" @time @testset "$name" begin @info "Testing $name"