v
v copied to clipboard
scoped attribures
Describe the feature
Structs and Structfields support having attributes, as an example:
struct User {
id int [skip]
name string [required]
created_on time.Time [skip]
}
It should be required to scope these attributes to the package that defines the behavior and acts on the attribute value.
Example:
[orm.table: 'users']
struct User {
id int [orm.primary; orm.sql: serial; json.skip]
name string [orm.required; json.required]
created_on time.Time [required; json.skip]
}
Note that prefixing is just the simplest variant, other syntactic options could fit better.
Use Case
A simple use case would be using the same struct for orm and json.
Lets say we want to build a simple rest api with CRUD operations, the data should be kept in a database.
We can define a struct the following:
[table: 'users']
struct User {
id int [primary; sql: serial]
name string [required]
created_on time.Time [required]
}
Now that would suit perfect the database, however all json decode operations would require custom logic to ignore the id
and created_on
fields passed in by the client.
e.g. the create operation:
fn (mut app App) create() vweb.Result {
user = json.decode(User, app.req.data) or {
app.set_status(400, '')
return app.json({
'error': 'Failed to decode json, error: $err'
})
}
// we do not allow the clients to specify id or created_on
user.id = 0
user.created_on = time.utc()
...
}
Proposed Solution
Instead of having "global" tags, like skip
or required
, it should be mandatory to scope the tag to the handling package:
[orm.table: 'users']
struct User {
id int [orm.primary; orm.sql: serial; json.skip]
name string [orm.required; json.required]
created_on time.Time [orm.required; json.skip]
}
Other Information
An alternative would be that developers create two structs, one for the "wire", and one for the database.
That has these two (major) negatives:
- Data has to be continuously copied back and forth.
- Maintenance: an attribute that was added in the database type does not land on the wire by default
Acknowledgements
- [x] I may be able to implement this feature request
- [X] This feature might incur a breaking change
Version used
v0.3.3
Environment details (OS name and version, etc.)
Processor: 10 cpus, 64bit, little endian, Apple M1 Max
CC version: Apple clang version 14.0.0 (clang-1400.0.29.202)
getwd: /Users/Tobias
vmodules: /Users/Tobias/.vmodules
vroot: /Users/Tobias/vlang/v0.3.3
vexe: /Users/Tobias/vlang/v0.3.3/v
vexe mtime: 2023-01-30 17:54:17
is vroot writable: true
is vmodules writable: true
V full version: V 0.3.3 d1f57ea
Git version: git version 2.39.2
Git vroot status: Error: fatal: not a git repository (or any of the parent directories): .git
.git/config present: false
thirdparty/tcc status: thirdparty-macos-amd64 46662e20
I 100% agree.
But I'd keep the current syntax: [json:required]
edit
hm that would actually conflict with [json:field_name]
but we can force using quotes for those: [json:'field_name']
.
Quotes are also good, so it would look like this?
[json:'field_name', required]
How about skipping? should we also use golangs format:
[json:-]
or rather this:
[json:skip]
Instead of having a system that can break and go unnoticed with a simple typo, we could structure attributes to make them make more sense and be more robust, like in Java, C#, Kotlin, etc.
Here is an example of how it could look like:
// in json/json.v
// Bodyless attribute
attribute Skip
// Attribute with value
attribute SerialName(string)
// in main.v
import json
struct Foo {
bar int [json.SerialName('Bar')]
baz string [json.Skip]
}
Instead of having a system that can break and go unnoticed with a simple typo, we could structure attributes to make them make more sense and be more robust, like in Java, C#, Kotlin, etc.
Here is an example of how it could look like:
// in json/json.v // Bodyless attribute attribute Skip // Attribute with value attribute SerialName(string)
// in main.v import json struct Foo { bar int [json.SerialName('Bar')] baz string [json.Skip] }
at the end of the day, attribute would be a struct?(ref. to gen)
and how would we get the arguments?
yep I think making attribute a struct would be good:
// in json.v
attribute SerialName {
name string
}
attribute Skip {}
// in main.v
struct Foo {
bar int [json.SerialName{'Bar'}]
baz string [json.Skip]
}
or maybe better:
attribute is a reserved struct
// in json.v
attribute {
name string
skip bool
required bool
}
// in main.v
struct Foo {
bar int [json{name:'Bar'}]
baz string [json{skip:true}]
}
attribute My_attribute(string, int, f32)
well this could be abstracted to a struct, eg...
struct Foo {
bar int [My_attribute("test", 1, 1.3)]
baz string
}
V would build a struct that way...
struct My_attribute {
pub:
value1 string
value2 int
value3 f32
}
fn (f Foo) test() {
$for t_ in Test.attributes {
t_ is My_attribute {
....
}
}
}
yep I think making attribute a struct would be good:
// in json.v attribute SerialName { name string } attribute Skip {}
// in main.v struct Foo { bar int [json.SerialName{'Bar'}] baz string [json.Skip] }
or maybe better: attribute is a reserved struct
// in json.v attribute { name string skip bool required bool }
// in main.v struct Foo { bar int [json{name:'Bar'}] baz string [json{skip:true}] }
it seems simpler
How about [attribute]
attribute?
[attribute]
struct SerialName {
name string
}
[attribute]
struct Skip {}
it doesn't add special syntax
How about
[attribute]
attribute?[attribute] struct SerialName { name string } [attribute] struct Skip {}
it doesn't add special syntax
I suggested this in the discord server, and i agree. It would be much simpler, but a little verbose for bigger attributes, which won't be common anyway.
One big question i have is how will empty attributes be used? [json.Skip]
or [json.Skip{}]
?
edit: the attribute
attribute would also be the only allowed non struct attribute, also.
Its a weak opinion but I would rather vote for one struct defining all attributes than multiple structs defining multiple attributes, the reason is readability and maintainability. The reader of the source code has just to find this one struct instead of finding multiple scattered around the source code.
I don't care if it is like
attributes {
name string
skip bool
required bool
}
or
[attributes]
struct Attributes {
name string
skip bool
required bool
}
yes, but what about the builtiin attributes, will they remain as they are in a form of use reserved for them?
yes, but what about the builtiin attributes, will they remain as they are in a form of use reserved for them?
Could be like normal attributes:
[MarkUsed]
fn foo() {}