1 |
package gcss |
2 |
|
3 |
import ( |
4 |
"bytes" |
5 |
"fmt" |
6 |
"io" |
7 |
"strings" |
8 |
) |
9 |
|
10 |
// declaration represents a declaration of CSS. |
11 |
type declaration struct { |
12 |
elementBase |
13 |
property string |
14 |
value string |
15 |
} |
16 |
|
17 |
// WriteTo writes the declaration to the writer. |
18 |
func (dec *declaration) WriteTo(w io.Writer) (int64, error) { |
19 |
return dec.writeTo(w, nil) |
20 |
} |
21 |
|
22 |
// writeTo writes the declaration to the writer. |
23 |
func (dec *declaration) writeTo(w io.Writer, params map[string]string) (int64, error) { |
24 |
bf := new(bytes.Buffer) |
25 |
|
26 |
bf.WriteString(dec.property) |
27 |
bf.WriteString(colon) |
28 |
|
29 |
for i, v := range strings.Split(dec.value, space) { |
30 |
if i > 0 { |
31 |
bf.WriteString(space) |
32 |
} |
33 |
|
34 |
for j, w := range strings.Split(v, comma) { |
35 |
if j > 0 { |
36 |
bf.WriteString(comma) |
37 |
} |
38 |
|
39 |
if strings.HasPrefix(w, dollarMark) { |
40 |
// Writing to the bytes.Buffer never returns an error. |
41 |
dec.writeParamTo(bf, strings.TrimPrefix(w, dollarMark), params) |
42 |
} else { |
43 |
bf.WriteString(w) |
44 |
} |
45 |
} |
46 |
} |
47 |
|
48 |
bf.WriteString(semicolon) |
49 |
|
50 |
n, err := w.Write(bf.Bytes()) |
51 |
|
52 |
return int64(n), err |
53 |
} |
54 |
|
55 |
// writeParam writes the param to the writer. |
56 |
func (dec *declaration) writeParamTo(w io.Writer, name string, params map[string]string) (int64, error) { |
57 |
if s, ok := params[name]; ok { |
58 |
if strings.HasPrefix(s, dollarMark) { |
59 |
if v, ok := dec.Context().vars[strings.TrimPrefix(s, dollarMark)]; ok { |
60 |
return v.WriteTo(w) |
61 |
} |
62 |
return 0, nil |
63 |
} |
64 |
|
65 |
n, err := w.Write([]byte(s)) |
66 |
return int64(n), err |
67 |
} |
68 |
|
69 |
if v, ok := dec.Context().vars[name]; ok { |
70 |
return v.WriteTo(w) |
71 |
} |
72 |
|
73 |
return 0, nil |
74 |
} |
75 |
|
76 |
// declarationPV extracts a declaration property and value |
77 |
// from the line. |
78 |
func declarationPV(ln *line) (string, string, error) { |
79 |
pv := strings.SplitN(strings.TrimSpace(ln.s), space, 2) |
80 |
|
81 |
if len(pv) < 2 { |
82 |
return "", "", fmt.Errorf("declaration's property and value should be divided by a space [line: %d]", ln.no) |
83 |
} |
84 |
|
85 |
if !strings.HasSuffix(pv[0], colon) { |
86 |
return "", "", fmt.Errorf("property should end with a colon [line: %d]", ln.no) |
87 |
} |
88 |
|
89 |
return strings.TrimSuffix(pv[0], colon), pv[1], nil |
90 |
} |
91 |
|
92 |
// newDeclaration creates and returns a declaration. |
93 |
func newDeclaration(ln *line, parent element) (*declaration, error) { |
94 |
property, value, err := declarationPV(ln) |
95 |
|
96 |
if err != nil { |
97 |
return nil, err |
98 |
} |
99 |
|
100 |
if strings.HasSuffix(value, semicolon) { |
101 |
return nil, fmt.Errorf("declaration must not end with %q [line: %d]", semicolon, ln.no) |
102 |
} |
103 |
|
104 |
return &declaration{ |
105 |
elementBase: newElementBase(ln, parent), |
106 |
property: property, |
107 |
value: value, |
108 |
}, nil |
109 |
} |