-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into fix/refacto-form-logic-org-roles-based
- Loading branch information
Showing
19 changed files
with
981 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package daocond | ||
|
||
import ( | ||
"gno.land/p/demo/json" | ||
) | ||
|
||
func And(left Condition, right Condition) Condition { | ||
if left == nil || right == nil { | ||
panic("left or right is nil") | ||
} | ||
return &andCond{left: left, right: right} | ||
} | ||
|
||
type andCond struct { | ||
// XXX: use a slice instead of only two children? | ||
left Condition | ||
right Condition | ||
} | ||
|
||
// NewState implements Condition. | ||
func (a *andCond) NewState() State { | ||
return &andState{left: a.left.NewState(), right: a.right.NewState()} | ||
} | ||
|
||
// Render implements Condition. | ||
func (a *andCond) Render() string { | ||
return "[" + a.left.Render() + " AND " + a.right.Render() + "]" | ||
} | ||
|
||
// RenderJSON implements Condition. | ||
func (a *andCond) RenderJSON() *json.Node { | ||
return json.ObjectNode("", map[string]*json.Node{ | ||
"type": json.StringNode("", "and"), | ||
"left": a.left.RenderJSON(), | ||
"right": a.right.RenderJSON(), | ||
}) | ||
} | ||
|
||
var _ Condition = (*andCond)(nil) | ||
|
||
type andState struct { | ||
left State | ||
right State | ||
} | ||
|
||
// RenderJSON implements State. | ||
func (a *andState) RenderJSON(votes map[string]Vote) *json.Node { | ||
return json.ObjectNode("", map[string]*json.Node{ | ||
"type": json.StringNode("", "and"), | ||
"left": a.left.RenderJSON(votes), | ||
"right": a.right.RenderJSON(votes), | ||
}) | ||
} | ||
|
||
// Eval implements State. | ||
func (a *andState) Eval(votes map[string]Vote) bool { | ||
return a.left.Eval(votes) && a.right.Eval(votes) | ||
} | ||
|
||
// HandleEvent implements State. | ||
func (a *andState) HandleEvent(evt Event, votes map[string]Vote) { | ||
a.left.HandleEvent(evt, votes) | ||
a.right.HandleEvent(evt, votes) | ||
} | ||
|
||
var _ State = (*andState)(nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package daocond | ||
|
||
import ( | ||
"errors" | ||
|
||
"gno.land/p/demo/json" | ||
"gno.land/p/demo/ufmt" | ||
) | ||
|
||
func MembersThreshold(threshold float64, isMemberFn func(memberId string) bool, membersCountFn func() uint64) Condition { | ||
if threshold <= 0 || threshold > 1 { | ||
panic(errors.New("invalid threshold")) | ||
} | ||
if isMemberFn == nil { | ||
panic(errors.New("nil isMemberFn")) | ||
} | ||
if membersCountFn == nil { | ||
panic(errors.New("nil membersCountFn")) | ||
} | ||
return &membersThresholdCond{ | ||
threshold: threshold, | ||
isMemberFn: isMemberFn, | ||
membersCountFn: membersCountFn, | ||
} | ||
} | ||
|
||
type membersThresholdCond struct { | ||
isMemberFn func(memberId string) bool | ||
membersCountFn func() uint64 | ||
threshold float64 | ||
} | ||
|
||
// NewState implements Condition. | ||
func (m *membersThresholdCond) NewState() State { | ||
return &membersThresholdState{ | ||
cond: m, | ||
} | ||
} | ||
|
||
// Render implements Condition. | ||
func (m *membersThresholdCond) Render() string { | ||
return ufmt.Sprintf("%g%% of members", m.threshold*100) | ||
} | ||
|
||
// RenderJSON implements Condition. | ||
func (m *membersThresholdCond) RenderJSON() *json.Node { | ||
return json.ObjectNode("", map[string]*json.Node{ | ||
"type": json.StringNode("", "members-threshold"), | ||
"threshold": json.NumberNode("", m.threshold), | ||
}) | ||
} | ||
|
||
var _ Condition = (*membersThresholdCond)(nil) | ||
|
||
type membersThresholdState struct { | ||
cond *membersThresholdCond | ||
totalYes uint64 | ||
} | ||
|
||
// Eval implements State. | ||
func (m *membersThresholdState) Eval(_ map[string]Vote) bool { | ||
return float64(m.totalYes)/float64(m.cond.membersCountFn()) >= m.cond.threshold | ||
} | ||
|
||
// HandleEvent implements State. | ||
func (m *membersThresholdState) HandleEvent(evt Event, votes map[string]Vote) { | ||
switch evt := evt.(type) { | ||
case *EventVote: | ||
if !m.cond.isMemberFn(evt.VoterID) { | ||
return | ||
} | ||
previousVote := votes[evt.VoterID] | ||
if previousVote == VoteYes && evt.Vote != VoteYes { | ||
m.totalYes -= 1 | ||
} else if previousVote != VoteYes && evt.Vote == VoteYes { | ||
m.totalYes += 1 | ||
} | ||
|
||
case *EventMemberAdded: | ||
if votes[evt.MemberID] == VoteYes { | ||
m.totalYes += 1 | ||
} | ||
|
||
case *EventMemberRemoved: | ||
if votes[evt.MemberID] == VoteYes { | ||
m.totalYes -= 1 | ||
} | ||
} | ||
} | ||
|
||
// RenderJSON implements State. | ||
func (m *membersThresholdState) RenderJSON(_ map[string]Vote) *json.Node { | ||
return json.ObjectNode("", map[string]*json.Node{ | ||
"type": json.StringNode("", "members-threshold"), | ||
"totalYes": json.NumberNode("", float64(m.totalYes)), | ||
}) | ||
} | ||
|
||
var _ State = (*membersThresholdState)(nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package daocond | ||
|
||
import ( | ||
"errors" | ||
|
||
"gno.land/p/demo/json" | ||
"gno.land/p/demo/ufmt" | ||
) | ||
|
||
func MembersThresholdFewVotes(threshold float64, isMemberFn func(memberId string) bool, membersCountFn func() uint64) Condition { | ||
if threshold <= 0 || threshold > 1 { | ||
panic(errors.New("invalid threshold")) | ||
} | ||
if isMemberFn == nil { | ||
panic(errors.New("nil isMemberFn")) | ||
} | ||
if membersCountFn == nil { | ||
panic(errors.New("nil membersCountFn")) | ||
} | ||
return &membersThresholdFewVotesCond{ | ||
threshold: threshold, | ||
isMemberFn: isMemberFn, | ||
membersCountFn: membersCountFn, | ||
} | ||
} | ||
|
||
type membersThresholdFewVotesCond struct { | ||
isMemberFn func(memberId string) bool | ||
membersCountFn func() uint64 | ||
threshold float64 | ||
} | ||
|
||
// NewState implements Condition. | ||
func (m *membersThresholdFewVotesCond) NewState() State { | ||
return &membersThresholdFewVotesState{ | ||
cond: m, | ||
} | ||
} | ||
|
||
// Render implements Condition. | ||
func (m *membersThresholdFewVotesCond) Render() string { | ||
return ufmt.Sprintf("%g%% of members", m.threshold*100) | ||
} | ||
|
||
// RenderJSON implements Condition. | ||
func (m *membersThresholdFewVotesCond) RenderJSON() *json.Node { | ||
return json.ObjectNode("", map[string]*json.Node{ | ||
"type": json.StringNode("", "members-threshold"), | ||
"threshold": json.NumberNode("", m.threshold), | ||
}) | ||
} | ||
|
||
var _ Condition = (*membersThresholdFewVotesCond)(nil) | ||
|
||
type membersThresholdFewVotesState struct { | ||
cond *membersThresholdFewVotesCond | ||
} | ||
|
||
func (m *membersThresholdFewVotesState) totalYes(votes map[string]Vote) uint64 { | ||
totalYes := uint64(0) | ||
for userId, vote := range votes { | ||
if vote != VoteYes { | ||
continue | ||
} | ||
if !m.cond.isMemberFn(userId) { | ||
continue | ||
} | ||
totalYes += 1 | ||
} | ||
return totalYes | ||
} | ||
|
||
// Eval implements State. | ||
func (m *membersThresholdFewVotesState) Eval(votes map[string]Vote) bool { | ||
return float64(m.totalYes(votes))/float64(m.cond.membersCountFn()) >= m.cond.threshold | ||
} | ||
|
||
// HandleEvent implements State. | ||
func (m *membersThresholdFewVotesState) HandleEvent(_ Event, _ map[string]Vote) { | ||
panic(errors.New("not implemented")) | ||
} | ||
|
||
// RenderJSON implements State. | ||
func (m *membersThresholdFewVotesState) RenderJSON(votes map[string]Vote) *json.Node { | ||
return json.ObjectNode("", map[string]*json.Node{ | ||
"type": json.StringNode("", "members-threshold"), | ||
"totalYes": json.NumberNode("", float64(m.totalYes(votes))), | ||
}) | ||
} | ||
|
||
var _ State = (*membersThresholdFewVotesState)(nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package daocond | ||
|
||
import ( | ||
"gno.land/p/demo/json" | ||
) | ||
|
||
func Or(left Condition, right Condition) Condition { | ||
if left == nil || right == nil { | ||
panic("left or right is nil") | ||
} | ||
return &orCond{left: left, right: right} | ||
} | ||
|
||
type orCond struct { | ||
// XXX: use a slice instead of only two children? | ||
left Condition | ||
right Condition | ||
} | ||
|
||
// NewState implements Condition. | ||
func (a *orCond) NewState() State { | ||
return &orState{left: a.left.NewState(), right: a.right.NewState()} | ||
} | ||
|
||
// Render implements Condition. | ||
func (a *orCond) Render() string { | ||
return "[" + a.left.Render() + " OR " + a.right.Render() + "]" | ||
} | ||
|
||
// RenderJSON implements Condition. | ||
func (a *orCond) RenderJSON() *json.Node { | ||
return json.ObjectNode("", map[string]*json.Node{ | ||
"type": json.StringNode("", "or"), | ||
"left": a.left.RenderJSON(), | ||
"right": a.right.RenderJSON(), | ||
}) | ||
} | ||
|
||
var _ Condition = (*andCond)(nil) | ||
|
||
type orState struct { | ||
left State | ||
right State | ||
} | ||
|
||
// Eval implements State. | ||
func (a *orState) Eval(votes map[string]Vote) bool { | ||
return a.left.Eval(votes) || a.right.Eval(votes) | ||
} | ||
|
||
// HandleEvent implements State. | ||
func (a *orState) HandleEvent(evt Event, votes map[string]Vote) { | ||
a.left.HandleEvent(evt, votes) | ||
a.right.HandleEvent(evt, votes) | ||
} | ||
|
||
// RenderJSON implements State. | ||
func (a *orState) RenderJSON(votes map[string]Vote) *json.Node { | ||
return json.ObjectNode("", map[string]*json.Node{ | ||
"type": json.StringNode("", "and"), | ||
"left": a.left.RenderJSON(votes), | ||
"right": a.right.RenderJSON(votes), | ||
}) | ||
} | ||
|
||
var _ State = (*orState)(nil) |
Oops, something went wrong.