1use crate::error::LoadPaletteErr;
7use crate::palette;
8use crate::palette::{Colors, Palette};
9use ratatui::style::Color;
10use std::borrow::Cow;
11use std::io;
12
13pub fn store_palette(pal: &Palette, mut buf: impl io::Write) -> Result<(), io::Error> {
15 writeln!(buf, "[theme]")?;
16 writeln!(buf, "name={}", pal.theme_name)?;
17 writeln!(buf, "theme={}", pal.theme)?;
18 writeln!(buf)?;
19 writeln!(buf, "[palette]")?;
20 writeln!(buf, "name={}", pal.name)?;
21 writeln!(buf, "docs={}", pal.doc.replace('\n', "\\n"))?;
22 writeln!(buf, "generator={}", pal.generator)?;
23 writeln!(buf,)?;
24 writeln!(buf, "[color]")?;
25 for c in Colors::array() {
26 writeln!(
27 buf,
28 "{}={}, {}",
29 *c, pal.color[*c as usize][0], pal.color[*c as usize][3]
30 )?;
31 }
32 writeln!(buf,)?;
33 writeln!(buf, "[reference]")?;
34 for (r, i) in pal.aliased.as_ref() {
35 writeln!(buf, "{}={}", r, i)?;
36 }
37 Ok(())
38}
39
40pub fn load_palette(mut r: impl io::Read) -> Result<Palette, io::Error> {
42 let mut buf = String::new();
43 r.read_to_string(&mut buf)?;
44
45 enum S {
46 Start,
47 Theme,
48 Palette,
49 Color,
50 Reference,
51 Fail(u8),
52 }
53
54 let mut pal = Palette::default();
55 let mut dark = 63u8;
56
57 let mut state = S::Start;
58 'm: for l in buf.lines() {
59 let l = l.trim();
60 match state {
61 S::Start => {
62 if l == "[theme]" {
63 state = S::Theme;
64 } else if l == "[palette]" {
65 state = S::Palette;
66 } else {
67 state = S::Fail(1);
68 break 'm;
69 }
70 }
71 S::Theme => {
72 if l == "[palette]" {
73 state = S::Palette;
74 } else if l.is_empty() || l.starts_with("#") {
75 } else if l.starts_with("name") {
77 if let Some(s) = l.split('=').nth(1) {
78 pal.theme_name = Cow::Owned(s.trim().to_string());
79 }
80 } else if l.starts_with("theme") {
81 if let Some(s) = l.split('=').nth(1) {
82 pal.theme = Cow::Owned(s.trim().to_string());
83 }
84 } else {
85 state = S::Fail(2);
86 break 'm;
87 }
88 }
89 S::Palette => {
90 if l == "[color]" {
91 state = S::Color;
92 } else if l.is_empty() || l.starts_with("#") {
93 } else if l.starts_with("name") {
95 if let Some(s) = l.split('=').nth(1) {
96 pal.name = Cow::Owned(s.trim().to_string());
97 }
98 } else if l.starts_with("docs") {
99 if let Some(s) = l.split('=').nth(1) {
100 let doc = s.trim().replace("\\n", "\n");
101 pal.doc = Cow::Owned(doc);
102 }
103 } else if l.starts_with("generator") {
104 if let Some(s) = l.split('=').nth(1) {
105 pal.generator = Cow::Owned(s.trim().to_string());
106 if s.starts_with("light-dark") {
107 if let Some(s) = l.split(':').nth(1) {
108 dark = s.trim().parse::<u8>().unwrap_or(63);
109 }
110 }
111 }
112 } else if l.starts_with("dark") {
113 if let Some(s) = l.split('=').nth(1) {
114 if let Ok(v) = s.trim().parse::<u8>() {
115 dark = v;
116 } else {
117 }
119 }
120 } else {
121 state = S::Fail(3);
122 break 'm;
123 }
124 }
125 S::Color => {
126 if l == "[reference]" {
127 state = S::Reference;
128 } else if l.is_empty() || l.starts_with("#") {
129 } else {
131 let mut kv = l.split('=');
132 let cn = if let Some(v) = kv.next() {
133 let Ok(c) = v.trim().parse::<Colors>() else {
134 state = S::Fail(4);
135 break 'm;
136 };
137 c
138 } else {
139 state = S::Fail(5);
140 break 'm;
141 };
142 let (c0, c3) = if let Some(v) = kv.next() {
143 let mut vv = v.split(',');
144 let c0 = if let Some(v) = vv.next() {
145 let Ok(v) = v.trim().parse::<Color>() else {
146 state = S::Fail(6);
147 break 'm;
148 };
149 v
150 } else {
151 state = S::Fail(7);
152 break 'm;
153 };
154 let c3 = if let Some(v) = vv.next() {
155 let Ok(v) = v.trim().parse::<Color>() else {
156 state = S::Fail(8);
157 break 'm;
158 };
159 v
160 } else {
161 state = S::Fail(9);
162 break 'm;
163 };
164 (c0, c3)
165 } else {
166 state = S::Fail(10);
167 break 'm;
168 };
169
170 if cn == Colors::TextLight || cn == Colors::TextDark {
171 pal.color[cn as usize] =
172 Palette::interpolatec2(c0, c3, Color::default(), Color::default())
173 } else {
174 pal.color[cn as usize] = Palette::interpolatec(c0, c3, dark);
175 }
176 }
177 }
178 S::Reference => {
179 let mut kv = l.split('=');
180 let rn = if let Some(v) = kv.next() {
181 v
182 } else {
183 state = S::Fail(11);
184 break 'm;
185 };
186 let ci = if let Some(v) = kv.next() {
187 if let Ok(ci) = v.parse::<palette::ColorIdx>() {
188 ci
189 } else {
190 state = S::Fail(12);
191 break 'm;
192 }
193 } else {
194 state = S::Fail(13);
195 break 'm;
196 };
197 pal.add_aliased(rn, ci);
198 }
199 S::Fail(_) => {
200 unreachable!()
201 }
202 }
203 }
204
205 match state {
206 S::Fail(n) => Err(io::Error::other(LoadPaletteErr(n))),
207 S::Start => Err(io::Error::other(LoadPaletteErr(100))),
208 S::Theme => Err(io::Error::other(LoadPaletteErr(101))),
209 S::Palette => Err(io::Error::other(LoadPaletteErr(102))),
210 S::Color | S::Reference => Ok(pal),
211 }
212}