Finish API
This commit is contained in:
parent
d5e0dcfab3
commit
d5228319bd
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -262,6 +262,7 @@ paket-files/
|
||||||
|
|
||||||
# FAKE - F# Make
|
# FAKE - F# Make
|
||||||
.fake/
|
.fake/
|
||||||
|
.ionide/
|
||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
.idea/
|
.idea/
|
||||||
|
|
|
@ -15,29 +15,36 @@ module CuidTests =
|
||||||
]
|
]
|
||||||
|
|
||||||
[<Tests>]
|
[<Tests>]
|
||||||
let fromStringTests =
|
let ofStringTests =
|
||||||
testList "Cuid.fromString" [
|
testList "Cuid.ofString" [
|
||||||
test "fails when string is null" {
|
test "fails when string is null" {
|
||||||
let x = Cuid.fromString null
|
let x = Cuid.ofString null
|
||||||
Expect.isError x "Parsing should have returned an error"
|
Expect.isError x "Parsing should have returned an error"
|
||||||
let msg = match x with Error y -> y | _ -> ""
|
let msg = match x with Error y -> y | _ -> ""
|
||||||
Expect.equal msg "string was null" "Expected error message not returned"
|
Expect.equal msg "string was null" "Expected error message not returned"
|
||||||
}
|
}
|
||||||
test "fails when string is not 25 characters" {
|
test "fails when string is not 25 characters" {
|
||||||
let x = Cuid.fromString "c12345566677893508"
|
let x = Cuid.ofString "c12345566677893508"
|
||||||
Expect.isError x "Parsing should have returned an error"
|
Expect.isError x "Parsing should have returned an error"
|
||||||
let msg = match x with Error y -> y | _ -> ""
|
let msg = match x with Error y -> y | _ -> ""
|
||||||
Expect.equal msg "string was not 25 characters (length 18)" "Expected error message not returned"
|
Expect.equal msg "string was not 25 characters (length 18)" "Expected error message not returned"
|
||||||
}
|
}
|
||||||
test "fails when string does not start with c" {
|
test "fails when string does not start with c" {
|
||||||
let x = Cuid.fromString "djld2cyuq0000t3rmniod1foy"
|
let x = Cuid.ofString "djld2cyuq0000t3rmniod1foy"
|
||||||
Expect.isError x "Parsing should have returned an error"
|
Expect.isError x "Parsing should have returned an error"
|
||||||
let msg = match x with Error y -> y | _ -> ""
|
let msg = match x with Error y -> y | _ -> ""
|
||||||
Expect.equal msg """string did not start with "c" ("djld2cyuq0000t3rmniod1foy")"""
|
Expect.equal msg """string did not start with "c" ("djld2cyuq0000t3rmniod1foy")"""
|
||||||
"Expected error message not returned"
|
"Expected error message not returned"
|
||||||
}
|
}
|
||||||
|
test "fails when string is not valid base-36" {
|
||||||
|
let x = Cuid.ofString "cjld2*yuq0/00t3r#niod1foy"
|
||||||
|
Expect.isError x "Parsing should have returned an error"
|
||||||
|
let msg = match x with Error y -> y | _ -> ""
|
||||||
|
Expect.equal msg """string was not in base-36 format ("cjld2*yuq0/00t3r#niod1foy")"""
|
||||||
|
"Expected error message not returned"
|
||||||
|
}
|
||||||
test "succeeds" {
|
test "succeeds" {
|
||||||
let x = Cuid.fromString "cjld2cyuq0000t3rmniod1foy"
|
let x = Cuid.ofString "cjld2cyuq0000t3rmniod1foy"
|
||||||
Expect.isOk x "Parsing should have returned Ok"
|
Expect.isOk x "Parsing should have returned Ok"
|
||||||
let cuid = match x with Ok y -> y | _ -> Cuid ""
|
let cuid = match x with Ok y -> y | _ -> Cuid ""
|
||||||
Expect.equal cuid (Cuid "cjld2cyuq0000t3rmniod1foy") "CUID value not correct"
|
Expect.equal cuid (Cuid "cjld2cyuq0000t3rmniod1foy") "CUID value not correct"
|
||||||
|
@ -53,6 +60,30 @@ module CuidTests =
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[<Tests>]
|
||||||
|
let isValidTests =
|
||||||
|
testList "Cuid.isValid" [
|
||||||
|
test "succeeds when the string is a valid CUID" {
|
||||||
|
Expect.isTrue ((Cuid.generateString >> Cuid.isValid) ()) "A valid CUID should have returned true"
|
||||||
|
}
|
||||||
|
test "succeeds when the string is not a valid CUID" {
|
||||||
|
Expect.isFalse (Cuid.isValid "abc") "An invalid CUID should have returned false"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
[<Tests>]
|
||||||
|
let validationMessageTests =
|
||||||
|
testList "Cuid.validationMessage" [
|
||||||
|
test "succeeds when the string is a valid CUID" {
|
||||||
|
Expect.equal ((Cuid.generateString >> Cuid.validationMessage) ()) ""
|
||||||
|
"A valid CUID should have returned an empty validation message"
|
||||||
|
}
|
||||||
|
test "succeeds when the string is an invalid CUID" {
|
||||||
|
Expect.equal (Cuid.validationMessage null) "string was null"
|
||||||
|
"An invalid CUID should have returned its validation error message"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
[<Tests>]
|
[<Tests>]
|
||||||
let generateStringTests =
|
let generateStringTests =
|
||||||
testList "Cuid.generateString" [
|
testList "Cuid.generateString" [
|
||||||
|
@ -77,28 +108,34 @@ module SlugTests =
|
||||||
]
|
]
|
||||||
|
|
||||||
[<Tests>]
|
[<Tests>]
|
||||||
let fromStringTests =
|
let ofStringTests =
|
||||||
testList "Slug.fromString" [
|
testList "Slug.ofString" [
|
||||||
test "fails when string is null" {
|
test "fails when string is null" {
|
||||||
let x = Slug.fromString null
|
let x = Slug.ofString null
|
||||||
Expect.isError x "Parsing should have returned an error"
|
Expect.isError x "Parsing should have returned an error"
|
||||||
let msg = match x with Error y -> y | _ -> ""
|
let msg = match x with Error y -> y | _ -> ""
|
||||||
Expect.equal msg "string was null" "Expected error message not returned"
|
Expect.equal msg "string was null" "Expected error message not returned"
|
||||||
}
|
}
|
||||||
test "fails when string is less than 7 characters" {
|
test "fails when string is less than 7 characters" {
|
||||||
let x = Slug.fromString "12345"
|
let x = Slug.ofString "12345"
|
||||||
Expect.isError x "Parsing should have returned an error"
|
Expect.isError x "Parsing should have returned an error"
|
||||||
let msg = match x with Error y -> y | _ -> ""
|
let msg = match x with Error y -> y | _ -> ""
|
||||||
Expect.equal msg "string must be at least 7 characters (length 5)" "Expected error message not returned"
|
Expect.equal msg "string must be at least 7 characters (length 5)" "Expected error message not returned"
|
||||||
}
|
}
|
||||||
test "fails when string is more than 10 characters" {
|
test "fails when string is more than 10 characters" {
|
||||||
let x = Slug.fromString "abcdefghijklmnop"
|
let x = Slug.ofString "abcdefghijklmnop"
|
||||||
Expect.isError x "Parsing should have returned an error"
|
Expect.isError x "Parsing should have returned an error"
|
||||||
let msg = match x with Error y -> y | _ -> ""
|
let msg = match x with Error y -> y | _ -> ""
|
||||||
Expect.equal msg "string must not exceed 10 characters (length 16)" "Expected error message not returned"
|
Expect.equal msg "string must not exceed 10 characters (length 16)" "Expected error message not returned"
|
||||||
}
|
}
|
||||||
|
test "fails when string is not valid base-36" {
|
||||||
|
let x = Slug.ofString "cj*q0/0#d"
|
||||||
|
Expect.isError x "Parsing should have returned an error"
|
||||||
|
let msg = match x with Error y -> y | _ -> ""
|
||||||
|
Expect.equal msg """string was not in base-36 format ("cj*q0/0#d")""" "Expected error message not returned"
|
||||||
|
}
|
||||||
test "succeeds" {
|
test "succeeds" {
|
||||||
let x = Slug.fromString "cyuq0000t"
|
let x = Slug.ofString "cyuq0000t"
|
||||||
Expect.isOk x "Parsing should have returned Ok"
|
Expect.isOk x "Parsing should have returned Ok"
|
||||||
let slug = match x with Ok y -> y | _ -> Slug ""
|
let slug = match x with Ok y -> y | _ -> Slug ""
|
||||||
Expect.equal slug (Slug "cyuq0000t") "Slug value not correct"
|
Expect.equal slug (Slug "cyuq0000t") "Slug value not correct"
|
||||||
|
@ -114,6 +151,30 @@ module SlugTests =
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[<Tests>]
|
||||||
|
let isValidTests =
|
||||||
|
testList "Slug.isValid" [
|
||||||
|
test "succeeds when the string is a valid Slug" {
|
||||||
|
Expect.isTrue ((Slug.generateString >> Slug.isValid) ()) "A valid Slug should have returned true"
|
||||||
|
}
|
||||||
|
test "succeeds when the string is not a valid Slug" {
|
||||||
|
Expect.isFalse (Slug.isValid "12345") "An invalid Slug should have returned false"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
[<Tests>]
|
||||||
|
let validationMessageTests =
|
||||||
|
testList "Slug.validationMessage" [
|
||||||
|
test "succeeds when the string is a valid Slug" {
|
||||||
|
Expect.equal ((Slug.generateString >> Slug.validationMessage) ()) ""
|
||||||
|
"A valid Slug should have returned an empty validation message"
|
||||||
|
}
|
||||||
|
test "succeeds when the string is an invalid Slug" {
|
||||||
|
Expect.equal (Slug.validationMessage null) "string was null"
|
||||||
|
"An invalid Slug should have returned its validation error message"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
[<Tests>]
|
[<Tests>]
|
||||||
let generateStringTests =
|
let generateStringTests =
|
||||||
testList "Slug.generateString" [
|
testList "Slug.generateString" [
|
||||||
|
|
|
@ -2,6 +2,17 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<VersionPrefix>1.0.0</VersionPrefix>
|
||||||
|
<PackageReleaseNotes>Initial release</PackageReleaseNotes>
|
||||||
|
<Authors>danieljsummers</Authors>
|
||||||
|
<Company>Bit Badger Solutions</Company>
|
||||||
|
<PackageProjectUrl>https://github.com/danieljsummers/FunctionalCuid</PackageProjectUrl>
|
||||||
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
|
<RepositoryUrl>https://github.com/danieljsummers/FunctionalCuid</RepositoryUrl>
|
||||||
|
<RepositoryType>Git</RepositoryType>
|
||||||
|
<Copyright>MIT License</Copyright>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<PackageTags>Cuid Slug F-Sharp</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Library.fs" />
|
<Compile Include="Library.fs" />
|
||||||
|
|
|
@ -103,6 +103,12 @@ module private Support =
|
||||||
| _ when chars > str.Length -> str
|
| _ when chars > str.Length -> str
|
||||||
| _ -> str.[str.Length - chars..]
|
| _ -> str.[str.Length - chars..]
|
||||||
|
|
||||||
|
/// Convert a result to a boolean
|
||||||
|
let resultToBool x = match x with Ok _ -> true | Error _ -> false
|
||||||
|
|
||||||
|
/// Get the error message for a result with a string error message
|
||||||
|
let errMsg x = match x with Ok _ -> "" | Error msg -> msg
|
||||||
|
|
||||||
|
|
||||||
/// Public functions for the CUID type
|
/// Public functions for the CUID type
|
||||||
module Cuid =
|
module Cuid =
|
||||||
|
@ -135,8 +141,7 @@ module Cuid =
|
||||||
/// - be 25 characters long
|
/// - be 25 characters long
|
||||||
/// - start with "c"
|
/// - start with "c"
|
||||||
/// - be base-36 format ([0-9|a-z])
|
/// - be base-36 format ([0-9|a-z])
|
||||||
// TODO: extract these validations out so we can provide a "validate" function for C#/VB.NET
|
let ofString (x : string) =
|
||||||
let fromString (x : string) =
|
|
||||||
match x with
|
match x with
|
||||||
| null -> Error "string was null"
|
| null -> Error "string was null"
|
||||||
| _ when x.Length <> 25 -> (sprintf "string was not 25 characters (length %i)" >> Error) x.Length
|
| _ when x.Length <> 25 -> (sprintf "string was not 25 characters (length %i)" >> Error) x.Length
|
||||||
|
@ -147,6 +152,14 @@ module Cuid =
|
||||||
/// Get the string representation of a CUID
|
/// Get the string representation of a CUID
|
||||||
let toString x = match x with Cuid y -> y
|
let toString x = match x with Cuid y -> y
|
||||||
|
|
||||||
|
/// Is the string a valid CUID?
|
||||||
|
[<CompiledName "IsValid">]
|
||||||
|
let isValid = ofString >> resultToBool
|
||||||
|
|
||||||
|
/// Get the validation message for a CUID string
|
||||||
|
[<CompiledName "ValidationMessage">]
|
||||||
|
let validationMessage = ofString >> errMsg
|
||||||
|
|
||||||
/// Generate a CUID as a string
|
/// Generate a CUID as a string
|
||||||
[<CompiledName "GenerateString">]
|
[<CompiledName "GenerateString">]
|
||||||
let generateString = generate >> toString
|
let generateString = generate >> toString
|
||||||
|
@ -177,8 +190,7 @@ module Slug =
|
||||||
/// Create a Slug from a string
|
/// Create a Slug from a string
|
||||||
///
|
///
|
||||||
/// The string must be between 7 and 10 characters long and base-36 format ([0-9|a-z])
|
/// The string must be between 7 and 10 characters long and base-36 format ([0-9|a-z])
|
||||||
// TODO: extract these validations out so we can provide a "validate" function for C#/VB.NET
|
let ofString (x : string) =
|
||||||
let fromString (x : string) =
|
|
||||||
match x with
|
match x with
|
||||||
| null -> Error "string was null"
|
| null -> Error "string was null"
|
||||||
| _ when x.Length < 7 -> (sprintf "string must be at least 7 characters (length %i)" >> Error) x.Length
|
| _ when x.Length < 7 -> (sprintf "string must be at least 7 characters (length %i)" >> Error) x.Length
|
||||||
|
@ -189,6 +201,14 @@ module Slug =
|
||||||
/// Get the string representation of a Slug
|
/// Get the string representation of a Slug
|
||||||
let toString x = match x with Slug y -> y
|
let toString x = match x with Slug y -> y
|
||||||
|
|
||||||
|
/// Is the string a valid Slug?
|
||||||
|
[<CompiledName "IsValid">]
|
||||||
|
let isValid = ofString >> resultToBool
|
||||||
|
|
||||||
|
/// Get the validation message for a Slug string
|
||||||
|
[<CompiledName "ValidationMessage">]
|
||||||
|
let validationMessage = ofString >> errMsg
|
||||||
|
|
||||||
/// Generate a Slug as a string
|
/// Generate a Slug as a string
|
||||||
[<CompiledName "GenerateString">]
|
[<CompiledName "GenerateString">]
|
||||||
let generateString = generate >> toString
|
let generateString = generate >> toString
|
||||||
|
|
34
README.md
34
README.md
|
@ -25,23 +25,45 @@ open Cuid
|
||||||
let generatedCuid = Cuid.generate ()
|
let generatedCuid = Cuid.generate ()
|
||||||
|
|
||||||
/// Creating a CUID from a string you already know. This string must be 25
|
/// Creating a CUID from a string you already know. This string must be 25
|
||||||
/// characters long and start with "c".
|
/// characters long, start with "c", and be valid base-36 (0-9 and a-z).
|
||||||
let cuidFromString =
|
let cuidOfString =
|
||||||
match Cuid.fromString "cjz362bgd00005cus6t21gba0" with
|
match Cuid.ofString "cjz362bgd00005cus6t21gba0" with
|
||||||
| Error msg -> invalidOp msg
|
| Error msg -> invalidOp msg
|
||||||
| Ok cuid -> cuid
|
| Ok cuid -> cuid
|
||||||
|
|
||||||
|
/// Establish a valid CUID string using isValid and validationMessage instead.
|
||||||
|
let validatedCuidString =
|
||||||
|
let str = "abcdefg"
|
||||||
|
match Cuid.isValid str with
|
||||||
|
| true -> str
|
||||||
|
| false -> (Cuid.validationMessage >> invalidOp) str
|
||||||
|
|
||||||
|
/// Get the validation error for a CUID (empty string if CUID is valid).
|
||||||
|
let cuidErrorMsg = Cuid.validationMessage "howdy"
|
||||||
|
|
||||||
/// The string representation of a CUID
|
/// The string representation of a CUID
|
||||||
let cuidString = Cuid.generateString ()
|
let cuidString = Cuid.generateString ()
|
||||||
```
|
```
|
||||||
|
|
||||||
For the `Slug` type, just replace `Cuid` with `Slug`; the validation rules for `Slug.fromString` are simply that the string has to be between 7 and 10 characters long.
|
For the `Slug` type, just replace `Cuid` with `Slug`; the validation rules for `Slug.ofString` are that the string has to be between 7 and 10 base-36 characters long.
|
||||||
|
|
||||||
For C# and VB.NET, the syntax is a bit different. Instead of `Cuid` as it reads above, it will appear as `CuidModule`; and, as `generateString` is the most likely function (method) called from those languages, its compiled name uses Pascal case. The same holds true for the `Slug` modules as well. A C# example...
|
For C# and VB.NET, the syntax is a bit different. Instead of `Cuid` as it reads above, it will appear as `CuidModule`; and, as `generateString`, `isValid`, and `validationMessage` are the most likely functions (methods) called from those languages, their compiled names use Pascal case. The same holds true for the `Slug` modules as well. A C# example...
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using Cuid;
|
using Cuid;
|
||||||
// ...
|
// ...
|
||||||
var cuid = CuidModule.GenerateString();
|
var cuid = CuidModule.GenerateString();
|
||||||
var slug = SlugModule.GenerateString();
|
|
||||||
|
// example from an MVC controller
|
||||||
|
public IActionResult Get(string cuid)
|
||||||
|
{
|
||||||
|
if (CuidModule.IsValid(cuid))
|
||||||
|
{
|
||||||
|
// do something with it
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound($"Could not find ID {cuid}; {CuidModule.ValidationMessage(cuid)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in New Issue
Block a user