Type safety
Give each name its own type, so a post TNID passed as a user TNID doesn't compile. At runtime, parsing rejects the wrong name.
One type per name
Each name gets its own TNID type:
use tnid::{NameStr, Tnid, TnidName};
struct User;
impl TnidName for User {
const ID_NAME: NameStr<'static> = NameStr::new_const("user");
}
let user_id = Tnid::<User>::new_v0();
fn delete_user(id: Tnid<User>) { /* ... */ }
delete_user(user_id); // OK; passing a Tnid<Post> here is a compile errorimport { Tnid, type TnidType } from "@tnid/core";
const UserId = Tnid("user");
type UserId = TnidType<typeof UserId>;
const userId: UserId = UserId.new_v0();
function deleteUser(id: UserId) { /* ... */ }
deleteUser(userId); // OK; passing a PostId, or a plain string, is a type errorInvalid names such as "User" or "users" fail to compile too.
At runtime
Parsing a string from a request, file or database checks the name:
Tnid::<User>::parse_tnid_string("post.Br2flcNDfF6LYICnT") // returns an errorUserId.parseTnidString("post.Br2flcNDfF6LYICnT") // throws- Name unknown in advance (generic logging, admin tools): parse into the library's
DynamicTnidtype, which accepts any valid TNID and lets you read its name. - Languages whose type system can't carry the name: rely on this runtime check wherever TNIDs enter your code.