Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/issue-175-auto #189

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ This is to keep track of different blog post that I refer to sometimes when thin
### Tests

For the tests, we use a shared project `Thoth.Json.Tests` that is referenced by the different runners. This is because we want each runner to only have the minimum amount of dependencies, and also if we include files from outside the `.fsproj` folder, then some generated files by Fable escape from the specify `outDir`.

Some of the tests require specific versions of Node.js, Python, etc. You can enter a shell with pinned versions available using `nix develop`.
7 changes: 7 additions & 0 deletions Thoth.Json.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Thoth.Json", "packages\Thot
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Build", "build\EasyBuild.fsproj", "{1A292FB0-2CCA-41C2-A9E6-EB69A31BAA5C}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Thoth.Json.Auto", "packages\Thoth.Json.Auto\Thoth.Json.Auto.fsproj", "{D7497D92-ABF7-4B81-9DC9-9DA52FD6F5EB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -82,6 +84,10 @@ Global
{1A292FB0-2CCA-41C2-A9E6-EB69A31BAA5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A292FB0-2CCA-41C2-A9E6-EB69A31BAA5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A292FB0-2CCA-41C2-A9E6-EB69A31BAA5C}.Release|Any CPU.Build.0 = Release|Any CPU
{D7497D92-ABF7-4B81-9DC9-9DA52FD6F5EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7497D92-ABF7-4B81-9DC9-9DA52FD6F5EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7497D92-ABF7-4B81-9DC9-9DA52FD6F5EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7497D92-ABF7-4B81-9DC9-9DA52FD6F5EB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7E82EFD6-6C43-4F64-8651-3BBDE9E18871} = {1C828713-FFEF-40AA-B204-0F1753D9626B}
Expand All @@ -94,5 +100,6 @@ Global
{5DD6593A-A2BB-48C8-90A2-5DD3E957CE75} = {61B51532-DABC-4F8E-9F95-50BE75BA59B4}
{81253746-095B-405B-BA26-E8CBDA25C992} = {61B51532-DABC-4F8E-9F95-50BE75BA59B4}
{D03314F6-68F9-46BD-A0D1-75B2B18E76A0} = {1C828713-FFEF-40AA-B204-0F1753D9626B}
{D7497D92-ABF7-4B81-9DC9-9DA52FD6F5EB} = {1C828713-FFEF-40AA-B204-0F1753D9626B}
EndGlobalSection
EndGlobal
25 changes: 25 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
description = "Development environment";

inputs = {
nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2405.*.tar.gz";
};

outputs = { self, nixpkgs }:
let
allSystems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];

forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
pkgs = import nixpkgs { inherit system; };
});
in
{
devShells = forAllSystems ({ pkgs }: {
default =
pkgs.mkShell {
packages = [
pkgs.bashInteractive
pkgs.dotnet-sdk_8
pkgs.nodejs_20
pkgs.python312
];
};
});
};
}
109 changes: 109 additions & 0 deletions packages/Thoth.Json.Auto/Casing.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
module Thoth.Json.Auto.Casing

open System
open System.Text

let private upperFirst (str: string) =
str.[..0].ToUpperInvariant() + str.[1..]

let private dotNetAcronyms =
Set.ofSeq
[
"id"
"ip"
]

let convertCase (source: CaseStyle) (dest: CaseStyle) (text: string) =
if source = dest then
text
else
let words =
match source with
| SnakeCase
| ScreamingSnakeCase ->
text.Split([| '_' |], StringSplitOptions.RemoveEmptyEntries)
|> Seq.toList
| PascalCase
| CamelCase
| DotNetPascalCase
| DotNetCamelCase ->
seq {
let sb = StringBuilder()

for c in text do
if Char.IsUpper c && sb.Length > 0 then
yield sb.ToString()
sb.Clear() |> ignore

sb.Append(c) |> ignore

if sb.Length > 0 then
yield sb.ToString()
}
|> Seq.fold
(fun state next ->
if next.Length > 1 then
next :: state
else
match state with
| [] -> [ next ]
| x :: xs ->
if
x.Length = 1
|| x |> Seq.forall Char.IsUpper
then
(x + next) :: xs
else
next :: x :: xs
)
[]
|> Seq.rev
|> Seq.toList

match dest with
| ScreamingSnakeCase ->
words
|> Seq.map (fun x -> x.ToUpperInvariant())
|> String.concat "_"
| SnakeCase ->
words
|> Seq.map (fun x -> x.ToLowerInvariant())
|> String.concat "_"
| PascalCase ->
words
|> Seq.map (fun x -> x.ToLowerInvariant() |> upperFirst)
|> String.concat ""
| CamelCase ->
words
|> Seq.mapi (fun i x ->
if i = 0 then
x.ToLowerInvariant()
else
x.ToLowerInvariant() |> upperFirst
)
|> String.concat ""
| DotNetPascalCase ->
words
|> Seq.map (fun x ->
let u = x.ToLowerInvariant()

if Set.contains u dotNetAcronyms then
u.ToUpperInvariant()
else
upperFirst u
)
|> String.concat ""
| DotNetCamelCase ->
words
|> Seq.mapi (fun i x ->
if i = 0 then
x.ToLowerInvariant()
else
let u = x.ToLowerInvariant()

if Set.contains u dotNetAcronyms then
u.ToUpperInvariant()
else
upperFirst u
)
|> String.concat ""
Loading
Loading