gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

rule.go (4951B)


      1 package css
      2 
      3 import (
      4 	"fmt"
      5 	"strings"
      6 )
      7 
      8 const (
      9 	indentSpace = 2
     10 )
     11 
     12 // RuleKind represents a Rule kind
     13 type RuleKind int
     14 
     15 // Rule kinds
     16 const (
     17 	QualifiedRule RuleKind = iota
     18 	AtRule
     19 )
     20 
     21 // At Rules than have Rules inside their block instead of Declarations
     22 var atRulesWithRulesBlock = []string{
     23 	"@document", "@font-feature-values", "@keyframes", "@media", "@supports",
     24 }
     25 
     26 // Rule represents a parsed CSS rule
     27 type Rule struct {
     28 	Kind RuleKind
     29 
     30 	// At Rule name (eg: "@media")
     31 	Name string
     32 
     33 	// Raw prelude
     34 	Prelude string
     35 
     36 	// Qualified Rule selectors parsed from prelude
     37 	Selectors []string
     38 
     39 	// Style properties
     40 	Declarations []*Declaration
     41 
     42 	// At Rule embedded rules
     43 	Rules []*Rule
     44 
     45 	// Current rule embedding level
     46 	EmbedLevel int
     47 }
     48 
     49 // NewRule instanciates a new Rule
     50 func NewRule(kind RuleKind) *Rule {
     51 	return &Rule{
     52 		Kind: kind,
     53 	}
     54 }
     55 
     56 // Returns string representation of rule kind
     57 func (kind RuleKind) String() string {
     58 	switch kind {
     59 	case QualifiedRule:
     60 		return "Qualified Rule"
     61 	case AtRule:
     62 		return "At Rule"
     63 	default:
     64 		return "WAT"
     65 	}
     66 }
     67 
     68 // EmbedsRules returns true if this rule embeds another rules
     69 func (rule *Rule) EmbedsRules() bool {
     70 	if rule.Kind == AtRule {
     71 		for _, atRuleName := range atRulesWithRulesBlock {
     72 			if rule.Name == atRuleName {
     73 				return true
     74 			}
     75 		}
     76 	}
     77 
     78 	return false
     79 }
     80 
     81 // Equal returns true if both rules are equals
     82 func (rule *Rule) Equal(other *Rule) bool {
     83 	if (rule.Kind != other.Kind) ||
     84 		(rule.Prelude != other.Prelude) ||
     85 		(rule.Name != other.Name) {
     86 		return false
     87 	}
     88 
     89 	if (len(rule.Selectors) != len(other.Selectors)) ||
     90 		(len(rule.Declarations) != len(other.Declarations)) ||
     91 		(len(rule.Rules) != len(other.Rules)) {
     92 		return false
     93 	}
     94 
     95 	for i, sel := range rule.Selectors {
     96 		if sel != other.Selectors[i] {
     97 			return false
     98 		}
     99 	}
    100 
    101 	for i, decl := range rule.Declarations {
    102 		if !decl.Equal(other.Declarations[i]) {
    103 			return false
    104 		}
    105 	}
    106 
    107 	for i, rule := range rule.Rules {
    108 		if !rule.Equal(other.Rules[i]) {
    109 			return false
    110 		}
    111 	}
    112 
    113 	return true
    114 }
    115 
    116 // Diff returns a string representation of rules differences
    117 func (rule *Rule) Diff(other *Rule) []string {
    118 	result := []string{}
    119 
    120 	if rule.Kind != other.Kind {
    121 		result = append(result, fmt.Sprintf("Kind: %s | %s", rule.Kind.String(), other.Kind.String()))
    122 	}
    123 
    124 	if rule.Prelude != other.Prelude {
    125 		result = append(result, fmt.Sprintf("Prelude: \"%s\" | \"%s\"", rule.Prelude, other.Prelude))
    126 	}
    127 
    128 	if rule.Name != other.Name {
    129 		result = append(result, fmt.Sprintf("Name: \"%s\" | \"%s\"", rule.Name, other.Name))
    130 	}
    131 
    132 	if len(rule.Selectors) != len(other.Selectors) {
    133 		result = append(result, fmt.Sprintf("Selectors: %v | %v", strings.Join(rule.Selectors, ", "), strings.Join(other.Selectors, ", ")))
    134 	} else {
    135 		for i, sel := range rule.Selectors {
    136 			if sel != other.Selectors[i] {
    137 				result = append(result, fmt.Sprintf("Selector: \"%s\" | \"%s\"", sel, other.Selectors[i]))
    138 			}
    139 		}
    140 	}
    141 
    142 	if len(rule.Declarations) != len(other.Declarations) {
    143 		result = append(result, fmt.Sprintf("Declarations Nb: %d | %d", len(rule.Declarations), len(other.Declarations)))
    144 	} else {
    145 		for i, decl := range rule.Declarations {
    146 			if !decl.Equal(other.Declarations[i]) {
    147 				result = append(result, fmt.Sprintf("Declaration: \"%s\" | \"%s\"", decl.String(), other.Declarations[i].String()))
    148 			}
    149 		}
    150 	}
    151 
    152 	if len(rule.Rules) != len(other.Rules) {
    153 		result = append(result, fmt.Sprintf("Rules Nb: %d | %d", len(rule.Rules), len(other.Rules)))
    154 	} else {
    155 
    156 		for i, rule := range rule.Rules {
    157 			if !rule.Equal(other.Rules[i]) {
    158 				result = append(result, fmt.Sprintf("Rule: \"%s\" | \"%s\"", rule.String(), other.Rules[i].String()))
    159 			}
    160 		}
    161 	}
    162 
    163 	return result
    164 }
    165 
    166 // Returns the string representation of a rule
    167 func (rule *Rule) String() string {
    168 	result := ""
    169 
    170 	if rule.Kind == QualifiedRule {
    171 		for i, sel := range rule.Selectors {
    172 			if i != 0 {
    173 				result += ", "
    174 			}
    175 			result += sel
    176 		}
    177 	} else {
    178 		// AtRule
    179 		result += fmt.Sprintf("%s", rule.Name)
    180 
    181 		if rule.Prelude != "" {
    182 			if result != "" {
    183 				result += " "
    184 			}
    185 			result += fmt.Sprintf("%s", rule.Prelude)
    186 		}
    187 	}
    188 
    189 	if (len(rule.Declarations) == 0) && (len(rule.Rules) == 0) {
    190 		result += ";"
    191 	} else {
    192 		result += " {\n"
    193 
    194 		if rule.EmbedsRules() {
    195 			for _, subRule := range rule.Rules {
    196 				result += fmt.Sprintf("%s%s\n", rule.indent(), subRule.String())
    197 			}
    198 		} else {
    199 			for _, decl := range rule.Declarations {
    200 				result += fmt.Sprintf("%s%s\n", rule.indent(), decl.String())
    201 			}
    202 		}
    203 
    204 		result += fmt.Sprintf("%s}", rule.indentEndBlock())
    205 	}
    206 
    207 	return result
    208 }
    209 
    210 // Returns identation spaces for declarations and rules
    211 func (rule *Rule) indent() string {
    212 	result := ""
    213 
    214 	for i := 0; i < ((rule.EmbedLevel + 1) * indentSpace); i++ {
    215 		result += " "
    216 	}
    217 
    218 	return result
    219 }
    220 
    221 // Returns identation spaces for end of block character
    222 func (rule *Rule) indentEndBlock() string {
    223 	result := ""
    224 
    225 	for i := 0; i < (rule.EmbedLevel * indentSpace); i++ {
    226 		result += " "
    227 	}
    228 
    229 	return result
    230 }