themes: add support for optional field theme value links
Theme attribute values can refer to another theme key instead of defining a value. Add support for optionally defining the theme key's field by appending a ".fg" or ".bg" suffix to the link's key.memfd
parent
9c0ee76ff4
commit
fa96a4e905
|
@ -65,7 +65,7 @@ pub fn fg_color(context: &Context, key: &'static str) -> Color {
|
||||||
.get(t)
|
.get(t)
|
||||||
.unwrap_or(&context.settings.terminal.themes.dark),
|
.unwrap_or(&context.settings.terminal.themes.dark),
|
||||||
};
|
};
|
||||||
unlink_fg(theme, &Cow::from(key))
|
unlink_fg(theme, &ColorField::Fg, &Cow::from(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -81,7 +81,7 @@ pub fn bg_color(context: &Context, key: &'static str) -> Color {
|
||||||
.get(t)
|
.get(t)
|
||||||
.unwrap_or(&context.settings.terminal.themes.dark),
|
.unwrap_or(&context.settings.terminal.themes.dark),
|
||||||
};
|
};
|
||||||
unlink_bg(theme, &Cow::from(key))
|
unlink_bg(theme, &ColorField::Bg, &Cow::from(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -106,8 +106,8 @@ fn unlink<'k, 't: 'k>(
|
||||||
key: &'k Cow<'static, str>,
|
key: &'k Cow<'static, str>,
|
||||||
) -> ThemeAttribute {
|
) -> ThemeAttribute {
|
||||||
ThemeAttribute {
|
ThemeAttribute {
|
||||||
fg: unlink_fg(theme, key),
|
fg: unlink_fg(theme, &ColorField::Fg, key),
|
||||||
bg: unlink_bg(theme, key),
|
bg: unlink_bg(theme, &ColorField::Bg, key),
|
||||||
attrs: unlink_attrs(theme, key),
|
attrs: unlink_attrs(theme, key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,12 +115,25 @@ fn unlink<'k, 't: 'k>(
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unlink_fg<'k, 't: 'k>(
|
fn unlink_fg<'k, 't: 'k>(
|
||||||
theme: &'t HashMap<Cow<'static, str>, ThemeAttributeInner>,
|
theme: &'t HashMap<Cow<'static, str>, ThemeAttributeInner>,
|
||||||
|
mut field: &'k ColorField,
|
||||||
mut key: &'k Cow<'static, str>,
|
mut key: &'k Cow<'static, str>,
|
||||||
) -> Color {
|
) -> Color {
|
||||||
loop {
|
loop {
|
||||||
match &theme[key].fg {
|
match field {
|
||||||
ThemeValue::Link(ref new_key) => key = new_key,
|
ColorField::LikeSelf | ColorField::Fg => match &theme[key].fg {
|
||||||
ThemeValue::Value(val) => return *val,
|
ThemeValue::Link(ref new_key, ref new_field) => {
|
||||||
|
key = new_key;
|
||||||
|
field = new_field
|
||||||
|
}
|
||||||
|
ThemeValue::Value(val) => return *val,
|
||||||
|
},
|
||||||
|
ColorField::Bg => match &theme[key].bg {
|
||||||
|
ThemeValue::Link(ref new_key, ref new_field) => {
|
||||||
|
key = new_key;
|
||||||
|
field = new_field
|
||||||
|
}
|
||||||
|
ThemeValue::Value(val) => return *val,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,12 +141,25 @@ fn unlink_fg<'k, 't: 'k>(
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unlink_bg<'k, 't: 'k>(
|
fn unlink_bg<'k, 't: 'k>(
|
||||||
theme: &'t HashMap<Cow<'static, str>, ThemeAttributeInner>,
|
theme: &'t HashMap<Cow<'static, str>, ThemeAttributeInner>,
|
||||||
|
mut field: &'k ColorField,
|
||||||
mut key: &'k Cow<'static, str>,
|
mut key: &'k Cow<'static, str>,
|
||||||
) -> Color {
|
) -> Color {
|
||||||
loop {
|
loop {
|
||||||
match &theme[key].bg {
|
match field {
|
||||||
ThemeValue::Link(ref new_key) => key = new_key,
|
ColorField::LikeSelf | ColorField::Bg => match &theme[key].bg {
|
||||||
ThemeValue::Value(val) => return *val,
|
ThemeValue::Link(ref new_key, ref new_field) => {
|
||||||
|
key = new_key;
|
||||||
|
field = new_field
|
||||||
|
}
|
||||||
|
ThemeValue::Value(val) => return *val,
|
||||||
|
},
|
||||||
|
ColorField::Fg => match &theme[key].fg {
|
||||||
|
ThemeValue::Link(ref new_key, ref new_field) => {
|
||||||
|
key = new_key;
|
||||||
|
field = new_field
|
||||||
|
}
|
||||||
|
ThemeValue::Value(val) => return *val,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +171,7 @@ fn unlink_attrs<'k, 't: 'k>(
|
||||||
) -> Attr {
|
) -> Attr {
|
||||||
loop {
|
loop {
|
||||||
match &theme[key].attrs {
|
match &theme[key].attrs {
|
||||||
ThemeValue::Link(ref new_key) => key = new_key,
|
ThemeValue::Link(ref new_key, ()) => key = new_key,
|
||||||
ThemeValue::Value(val) => return *val,
|
ThemeValue::Value(val) => return *val,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,15 +268,44 @@ impl Default for ThemeAttributeInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// Holds either an actual value or refers to the key name of the attribute that holds the value.
|
pub enum ColorField {
|
||||||
pub enum ThemeValue<T> {
|
// Like self, i.e. either Fg or Bg
|
||||||
Value(T),
|
LikeSelf,
|
||||||
Link(Cow<'static, str>),
|
Fg,
|
||||||
|
Bg,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<&'static str> for ThemeValue<T> {
|
/// The field a ThemeValue::Link refers to.
|
||||||
|
trait ThemeLink {
|
||||||
|
type LinkType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A color value that's a link can either refer to .fg or .bg field
|
||||||
|
impl ThemeLink for Color {
|
||||||
|
type LinkType = ColorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An attr value that's a link can only refer to an .attr field
|
||||||
|
impl ThemeLink for Attr {
|
||||||
|
type LinkType = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
/// Holds either an actual value or refers to the key name of the attribute that holds the value.
|
||||||
|
enum ThemeValue<T: ThemeLink> {
|
||||||
|
Value(T),
|
||||||
|
Link(Cow<'static, str>, T::LinkType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for ThemeValue<Color> {
|
||||||
fn from(from: &'static str) -> Self {
|
fn from(from: &'static str) -> Self {
|
||||||
ThemeValue::Link(from.into())
|
ThemeValue::Link(from.into(), ColorField::LikeSelf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for ThemeValue<Attr> {
|
||||||
|
fn from(from: &'static str) -> Self {
|
||||||
|
ThemeValue::Link(from.into(), ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +342,7 @@ impl<'de> Deserialize<'de> for ThemeValue<Attr> {
|
||||||
if let Ok(c) = Attr::from_string_de::<'de, D, String>(s.clone()) {
|
if let Ok(c) = Attr::from_string_de::<'de, D, String>(s.clone()) {
|
||||||
Ok(ThemeValue::Value(c))
|
Ok(ThemeValue::Value(c))
|
||||||
} else {
|
} else {
|
||||||
Ok(ThemeValue::Link(s.into()))
|
Ok(ThemeValue::Link(s.into(), ()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(de::Error::custom("invalid theme attribute value"))
|
Err(de::Error::custom("invalid theme attribute value"))
|
||||||
|
@ -295,14 +350,32 @@ impl<'de> Deserialize<'de> for ThemeValue<Attr> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Serialize> Serialize for ThemeValue<T> {
|
impl Serialize for ThemeValue<Attr> {
|
||||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
ThemeValue::Value(s) => s.serialize(serializer),
|
ThemeValue::Value(s) => s.serialize(serializer),
|
||||||
ThemeValue::Link(s) => serializer.serialize_str(s.as_ref()),
|
ThemeValue::Link(s, ()) => serializer.serialize_str(s.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for ThemeValue<Color> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
ThemeValue::Value(s) => s.serialize(serializer),
|
||||||
|
ThemeValue::Link(s, ColorField::LikeSelf) => serializer.serialize_str(s.as_ref()),
|
||||||
|
ThemeValue::Link(s, ColorField::Fg) => {
|
||||||
|
serializer.serialize_str(format!("{}.fg", s).as_ref())
|
||||||
|
}
|
||||||
|
ThemeValue::Link(s, ColorField::Bg) => {
|
||||||
|
serializer.serialize_str(format!("{}.bg", s).as_ref())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,8 +388,18 @@ impl<'de> Deserialize<'de> for ThemeValue<Color> {
|
||||||
if let Ok(s) = <String>::deserialize(deserializer) {
|
if let Ok(s) = <String>::deserialize(deserializer) {
|
||||||
if let Ok(c) = Color::from_string_de::<'de, D>(s.clone()) {
|
if let Ok(c) = Color::from_string_de::<'de, D>(s.clone()) {
|
||||||
Ok(ThemeValue::Value(c))
|
Ok(ThemeValue::Value(c))
|
||||||
|
} else if s.ends_with(".fg") {
|
||||||
|
Ok(ThemeValue::Link(
|
||||||
|
s[..s.len() - 3].to_string().into(),
|
||||||
|
ColorField::Fg,
|
||||||
|
))
|
||||||
|
} else if s.ends_with(".bg") {
|
||||||
|
Ok(ThemeValue::Link(
|
||||||
|
s[..s.len() - 3].to_string().into(),
|
||||||
|
ColorField::Bg,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(ThemeValue::Link(s.into()))
|
Ok(ThemeValue::Link(s.into(), ColorField::LikeSelf))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(de::Error::custom("invalid theme color value"))
|
Err(de::Error::custom("invalid theme color value"))
|
||||||
|
@ -431,7 +514,7 @@ impl Themes {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.chain(theme.iter().filter_map(|(key, a)| {
|
.chain(theme.iter().filter_map(|(key, a)| {
|
||||||
if let ThemeValue::Link(ref r) = a.fg {
|
if let ThemeValue::Link(ref r, _) = a.fg {
|
||||||
if !hash_set.contains(&r.as_ref()) {
|
if !hash_set.contains(&r.as_ref()) {
|
||||||
Some((Some(key), "fg link", r.as_ref()))
|
Some((Some(key), "fg link", r.as_ref()))
|
||||||
} else {
|
} else {
|
||||||
|
@ -442,7 +525,7 @@ impl Themes {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.chain(theme.iter().filter_map(|(key, a)| {
|
.chain(theme.iter().filter_map(|(key, a)| {
|
||||||
if let ThemeValue::Link(ref r) = a.bg {
|
if let ThemeValue::Link(ref r, _) = a.bg {
|
||||||
if !hash_set.contains(&r.as_ref()) {
|
if !hash_set.contains(&r.as_ref()) {
|
||||||
Some((Some(key), "bg link", r.as_ref()))
|
Some((Some(key), "bg link", r.as_ref()))
|
||||||
} else {
|
} else {
|
||||||
|
@ -453,7 +536,7 @@ impl Themes {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.chain(theme.iter().filter_map(|(key, a)| {
|
.chain(theme.iter().filter_map(|(key, a)| {
|
||||||
if let ThemeValue::Link(ref r) = a.attrs {
|
if let ThemeValue::Link(ref r, _) = a.attrs {
|
||||||
if !hash_set.contains(&r.as_ref()) {
|
if !hash_set.contains(&r.as_ref()) {
|
||||||
Some((Some(key), "attrs link", r.as_ref()))
|
Some((Some(key), "attrs link", r.as_ref()))
|
||||||
} else {
|
} else {
|
||||||
|
@ -526,8 +609,8 @@ impl Themes {
|
||||||
format!(
|
format!(
|
||||||
"\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n",
|
"\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n",
|
||||||
k,
|
k,
|
||||||
toml::to_string(&unlink_fg(&theme, k)).unwrap(),
|
toml::to_string(&unlink_fg(&theme, &ColorField::Fg, k)).unwrap(),
|
||||||
toml::to_string(&unlink_bg(&theme, k)).unwrap(),
|
toml::to_string(&unlink_bg(&theme, &ColorField::Bg, k)).unwrap(),
|
||||||
toml::to_string(&unlink_attrs(&theme, k)).unwrap(),
|
toml::to_string(&unlink_attrs(&theme, k)).unwrap(),
|
||||||
)
|
)
|
||||||
.chars(),
|
.chars(),
|
||||||
|
@ -927,8 +1010,8 @@ impl Serialize for Themes {
|
||||||
dark.insert(
|
dark.insert(
|
||||||
k.clone(),
|
k.clone(),
|
||||||
ThemeAttribute {
|
ThemeAttribute {
|
||||||
fg: unlink_fg(&self.dark, k),
|
fg: unlink_fg(&self.dark, &ColorField::Fg, k),
|
||||||
bg: unlink_bg(&self.dark, k),
|
bg: unlink_bg(&self.dark, &ColorField::Bg, k),
|
||||||
attrs: unlink_attrs(&self.dark, k),
|
attrs: unlink_attrs(&self.dark, k),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -938,8 +1021,8 @@ impl Serialize for Themes {
|
||||||
light.insert(
|
light.insert(
|
||||||
k.clone(),
|
k.clone(),
|
||||||
ThemeAttribute {
|
ThemeAttribute {
|
||||||
fg: unlink_fg(&self.light, k),
|
fg: unlink_fg(&self.light, &ColorField::Fg, k),
|
||||||
bg: unlink_bg(&self.light, k),
|
bg: unlink_bg(&self.light, &ColorField::Bg, k),
|
||||||
attrs: unlink_attrs(&self.light, k),
|
attrs: unlink_attrs(&self.light, k),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -952,8 +1035,8 @@ impl Serialize for Themes {
|
||||||
new_map.insert(
|
new_map.insert(
|
||||||
k.clone(),
|
k.clone(),
|
||||||
ThemeAttribute {
|
ThemeAttribute {
|
||||||
fg: unlink_fg(&t, k),
|
fg: unlink_fg(&t, &ColorField::Fg, k),
|
||||||
bg: unlink_bg(&t, k),
|
bg: unlink_bg(&t, &ColorField::Bg, k),
|
||||||
attrs: unlink_attrs(&t, k),
|
attrs: unlink_attrs(&t, k),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -971,30 +1054,45 @@ impl Serialize for Themes {
|
||||||
fn is_cyclic(
|
fn is_cyclic(
|
||||||
theme: &HashMap<Cow<'static, str>, ThemeAttributeInner>,
|
theme: &HashMap<Cow<'static, str>, ThemeAttributeInner>,
|
||||||
) -> std::result::Result<(), String> {
|
) -> std::result::Result<(), String> {
|
||||||
|
#[derive(Hash, Copy, Clone, PartialEq, Eq)]
|
||||||
enum Course {
|
enum Course {
|
||||||
Fg,
|
Fg,
|
||||||
Bg,
|
Bg,
|
||||||
Attrs,
|
Attrs,
|
||||||
}
|
}
|
||||||
fn is_cyclic_util<'a>(
|
fn is_cyclic_util<'a>(
|
||||||
course: &Course,
|
course: Course,
|
||||||
k: &'a Cow<'static, str>,
|
k: &'a Cow<'static, str>,
|
||||||
visited: &mut HashMap<&'a Cow<'static, str>, bool>,
|
visited: &mut HashMap<(&'a Cow<'static, str>, Course), bool>,
|
||||||
stack: &mut HashMap<&'a Cow<'static, str>, bool>,
|
stack: &mut HashMap<(&'a Cow<'static, str>, Course), bool>,
|
||||||
path: &mut SmallVec<[&'a Cow<'static, str>; 16]>,
|
path: &mut SmallVec<[(&'a Cow<'static, str>, Course); 16]>,
|
||||||
theme: &'a HashMap<Cow<'static, str>, ThemeAttributeInner>,
|
theme: &'a HashMap<Cow<'static, str>, ThemeAttributeInner>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if !visited[k] {
|
if !visited[&(k, course)] {
|
||||||
visited.entry(k).and_modify(|e| *e = true);
|
visited.entry((k, course)).and_modify(|e| *e = true);
|
||||||
stack.entry(k).and_modify(|e| *e = true);
|
stack.entry((k, course)).and_modify(|e| *e = true);
|
||||||
|
|
||||||
match course {
|
match course {
|
||||||
Course::Fg => match theme[k].fg {
|
Course::Fg => match theme[k].fg {
|
||||||
ThemeValue::Link(ref l) => {
|
ThemeValue::Link(ref l, ColorField::LikeSelf)
|
||||||
path.push(l);
|
| ThemeValue::Link(ref l, ColorField::Fg) => {
|
||||||
if !visited[l] && is_cyclic_util(course, l, visited, stack, path, theme) {
|
path.push((l, Course::Fg));
|
||||||
|
if !visited[&(l, Course::Fg)]
|
||||||
|
&& is_cyclic_util(course, l, visited, stack, path, theme)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
} else if stack[l] {
|
} else if stack[&(l, Course::Fg)] {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
ThemeValue::Link(ref l, ColorField::Bg) => {
|
||||||
|
path.push((l, Course::Bg));
|
||||||
|
if !visited[&(l, Course::Bg)]
|
||||||
|
&& is_cyclic_util(Course::Bg, l, visited, stack, path, theme)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
} else if stack[&(l, Course::Bg)] {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
path.pop();
|
path.pop();
|
||||||
|
@ -1002,11 +1100,25 @@ fn is_cyclic(
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Course::Bg => match theme[k].bg {
|
Course::Bg => match theme[k].bg {
|
||||||
ThemeValue::Link(ref l) => {
|
ThemeValue::Link(ref l, ColorField::LikeSelf)
|
||||||
path.push(l);
|
| ThemeValue::Link(ref l, ColorField::Bg) => {
|
||||||
if !visited[l] && is_cyclic_util(course, l, visited, stack, path, theme) {
|
path.push((l, Course::Bg));
|
||||||
|
if !visited[&(l, Course::Bg)]
|
||||||
|
&& is_cyclic_util(Course::Bg, l, visited, stack, path, theme)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
} else if stack[l] {
|
} else if stack[&(l, Course::Bg)] {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
ThemeValue::Link(ref l, ColorField::Fg) => {
|
||||||
|
path.push((l, Course::Fg));
|
||||||
|
if !visited[&(l, Course::Fg)]
|
||||||
|
&& is_cyclic_util(Course::Fg, l, visited, stack, path, theme)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
} else if stack[&(l, Course::Fg)] {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
path.pop();
|
path.pop();
|
||||||
|
@ -1014,11 +1126,13 @@ fn is_cyclic(
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Course::Attrs => match theme[k].attrs {
|
Course::Attrs => match theme[k].attrs {
|
||||||
ThemeValue::Link(ref l) => {
|
ThemeValue::Link(ref l, _) => {
|
||||||
path.push(l);
|
path.push((l, course));
|
||||||
if !visited[l] && is_cyclic_util(course, l, visited, stack, path, theme) {
|
if !visited[&(l, course)]
|
||||||
|
&& is_cyclic_util(course, l, visited, stack, path, theme)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
} else if stack[l] {
|
} else if stack[&(l, course)] {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
path.pop();
|
path.pop();
|
||||||
|
@ -1027,27 +1141,39 @@ fn is_cyclic(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stack.entry(k).and_modify(|e| *e = false);
|
stack.entry((k, course)).and_modify(|e| *e = false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut path = SmallVec::new();
|
let mut path = SmallVec::new();
|
||||||
let mut visited = theme
|
let mut visited = theme
|
||||||
.keys()
|
.keys()
|
||||||
.map(|k| (k, false))
|
.map(|k| {
|
||||||
.collect::<HashMap<&Cow<'static, str>, bool>>();
|
std::iter::once(((k, Course::Fg), false))
|
||||||
|
.chain(std::iter::once(((k, Course::Bg), false)))
|
||||||
|
.chain(std::iter::once(((k, Course::Attrs), false)))
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect::<HashMap<(&Cow<'static, str>, Course), bool>>();
|
||||||
|
|
||||||
let mut stack = theme
|
let mut stack = visited.clone();
|
||||||
.keys()
|
|
||||||
.map(|k| (k, false))
|
|
||||||
.collect::<HashMap<&Cow<'static, str>, bool>>();
|
|
||||||
for k in theme.keys() {
|
for k in theme.keys() {
|
||||||
for course in [Course::Fg, Course::Bg, Course::Attrs].iter() {
|
for &course in [Course::Fg, Course::Bg, Course::Attrs].iter() {
|
||||||
path.push(k);
|
path.push((k, course));
|
||||||
if is_cyclic_util(course, k, &mut visited, &mut stack, &mut path, &theme) {
|
if is_cyclic_util(course, k, &mut visited, &mut stack, &mut path, &theme) {
|
||||||
let path = path
|
let path = path
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|k| k.to_string())
|
.map(|(k, c)| {
|
||||||
|
format!(
|
||||||
|
"{}.{}",
|
||||||
|
k,
|
||||||
|
match c {
|
||||||
|
Course::Fg => "fg",
|
||||||
|
Course::Bg => "bg",
|
||||||
|
Course::Attrs => "attrs",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
|
|
Loading…
Reference in New Issue