change get_queued_cards() to no longer return congrats info
This commit is contained in:
parent
1d2e89d206
commit
57ec4cc7b5
@ -2,8 +2,9 @@
|
|||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This file contains experimental scheduler changes, and is not currently
|
This file contains experimental scheduler changes:
|
||||||
used by Anki.
|
|
||||||
|
https://betas.ankiweb.net/2021-scheduler.html
|
||||||
|
|
||||||
It uses the same DB schema as the V2 scheduler, and 'schedVer' remains
|
It uses the same DB schema as the V2 scheduler, and 'schedVer' remains
|
||||||
as '2' internally.
|
as '2' internally.
|
||||||
@ -11,7 +12,7 @@ as '2' internally.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import List, Literal, Sequence, Tuple, Union
|
from typing import List, Literal, Sequence, Tuple
|
||||||
|
|
||||||
import anki._backend.backend_pb2 as _pb
|
import anki._backend.backend_pb2 as _pb
|
||||||
from anki.cards import Card, CardId
|
from anki.cards import Card, CardId
|
||||||
@ -19,7 +20,6 @@ from anki.collection import OpChanges
|
|||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.decks import DeckId
|
from anki.decks import DeckId
|
||||||
from anki.errors import DBError
|
from anki.errors import DBError
|
||||||
from anki.scheduler.base import CongratsInfo
|
|
||||||
from anki.scheduler.legacy import SchedulerBaseWithLegacy
|
from anki.scheduler.legacy import SchedulerBaseWithLegacy
|
||||||
from anki.types import assert_exhaustive
|
from anki.types import assert_exhaustive
|
||||||
from anki.utils import intTime
|
from anki.utils import intTime
|
||||||
@ -44,19 +44,11 @@ class Scheduler(SchedulerBaseWithLegacy):
|
|||||||
*,
|
*,
|
||||||
fetch_limit: int = 1,
|
fetch_limit: int = 1,
|
||||||
intraday_learning_only: bool = False,
|
intraday_learning_only: bool = False,
|
||||||
) -> Union[QueuedCards, CongratsInfo]:
|
) -> QueuedCards:
|
||||||
"Returns one or more card ids, or the congratulations screen info."
|
"Returns zero or more pending cards, and the remaining counts. Idempotent."
|
||||||
info = self.col._backend.get_queued_cards(
|
return self.col._backend.get_queued_cards(
|
||||||
fetch_limit=fetch_limit, intraday_learning_only=intraday_learning_only
|
fetch_limit=fetch_limit, intraday_learning_only=intraday_learning_only
|
||||||
)
|
)
|
||||||
kind = info.WhichOneof("value")
|
|
||||||
if kind == "queued_cards":
|
|
||||||
return info.queued_cards
|
|
||||||
elif kind == "congrats_info":
|
|
||||||
return info.congrats_info
|
|
||||||
else:
|
|
||||||
assert_exhaustive(kind)
|
|
||||||
assert False
|
|
||||||
|
|
||||||
def next_states(self, card_id: CardId) -> NextStates:
|
def next_states(self, card_id: CardId) -> NextStates:
|
||||||
"New states corresponding to each answer button press."
|
"New states corresponding to each answer button press."
|
||||||
@ -111,29 +103,23 @@ class Scheduler(SchedulerBaseWithLegacy):
|
|||||||
|
|
||||||
def getCard(self) -> Optional[Card]:
|
def getCard(self) -> Optional[Card]:
|
||||||
"""Fetch the next card from the queue. None if finished."""
|
"""Fetch the next card from the queue. None if finished."""
|
||||||
response = self.get_queued_cards()
|
try:
|
||||||
if isinstance(response, QueuedCards):
|
queued_card = self.get_queued_cards().cards[0]
|
||||||
backend_card = response.cards[0].card
|
except IndexError:
|
||||||
card = Card(self.col)
|
|
||||||
card._load_from_backend_card(backend_card)
|
|
||||||
card.startTimer()
|
|
||||||
return card
|
|
||||||
else:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
card = Card(self.col)
|
||||||
|
card._load_from_backend_card(queued_card.card)
|
||||||
|
card.startTimer()
|
||||||
|
return card
|
||||||
|
|
||||||
def _is_finished(self) -> bool:
|
def _is_finished(self) -> bool:
|
||||||
"Don't use this, it is a stop-gap until this code is refactored."
|
"Don't use this, it is a stop-gap until this code is refactored."
|
||||||
info = self.get_queued_cards()
|
return not self.get_queued_cards().cards
|
||||||
return isinstance(info, CongratsInfo)
|
|
||||||
|
|
||||||
def counts(self, card: Optional[Card] = None) -> Tuple[int, int, int]:
|
def counts(self, card: Optional[Card] = None) -> Tuple[int, int, int]:
|
||||||
info = self.get_queued_cards()
|
info = self.get_queued_cards()
|
||||||
if isinstance(info, CongratsInfo):
|
return (info.new_count, info.learning_count, info.review_count)
|
||||||
counts = [0, 0, 0]
|
|
||||||
else:
|
|
||||||
counts = [info.new_count, info.learning_count, info.review_count]
|
|
||||||
|
|
||||||
return tuple(counts) # type: ignore
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def newCount(self) -> int:
|
def newCount(self) -> int:
|
||||||
|
@ -11,25 +11,13 @@ import re
|
|||||||
import unicodedata as ucd
|
import unicodedata as ucd
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import (
|
from typing import Any, Callable, List, Literal, Match, Optional, Sequence, Tuple, cast
|
||||||
Any,
|
|
||||||
Callable,
|
|
||||||
List,
|
|
||||||
Literal,
|
|
||||||
Match,
|
|
||||||
Optional,
|
|
||||||
Sequence,
|
|
||||||
Tuple,
|
|
||||||
Union,
|
|
||||||
cast,
|
|
||||||
)
|
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
from anki import hooks
|
from anki import hooks
|
||||||
from anki.cards import Card, CardId
|
from anki.cards import Card, CardId
|
||||||
from anki.collection import Config, OpChanges, OpChangesWithCount
|
from anki.collection import Config, OpChanges, OpChangesWithCount
|
||||||
from anki.scheduler import CongratsInfo
|
|
||||||
from anki.scheduler.v3 import CardAnswer, NextStates, QueuedCards
|
from anki.scheduler.v3 import CardAnswer, NextStates, QueuedCards
|
||||||
from anki.scheduler.v3 import Scheduler as V3Scheduler
|
from anki.scheduler.v3 import Scheduler as V3Scheduler
|
||||||
from anki.tags import MARKED_TAG
|
from anki.tags import MARKED_TAG
|
||||||
@ -240,7 +228,7 @@ class Reviewer:
|
|||||||
def _get_next_v3_card(self) -> None:
|
def _get_next_v3_card(self) -> None:
|
||||||
assert isinstance(self.mw.col.sched, V3Scheduler)
|
assert isinstance(self.mw.col.sched, V3Scheduler)
|
||||||
output = self.mw.col.sched.get_queued_cards()
|
output = self.mw.col.sched.get_queued_cards()
|
||||||
if isinstance(output, CongratsInfo):
|
if not output.cards:
|
||||||
return
|
return
|
||||||
self._v3 = V3CardInfo.from_queue(output)
|
self._v3 = V3CardInfo.from_queue(output)
|
||||||
self.card = Card(self.mw.col, backend_card=self._v3.top_card().card)
|
self.card = Card(self.mw.col, backend_card=self._v3.top_card().card)
|
||||||
|
@ -132,7 +132,7 @@ service SchedulingService {
|
|||||||
rpc StateIsLeech(SchedulingState) returns (Bool);
|
rpc StateIsLeech(SchedulingState) returns (Bool);
|
||||||
rpc AnswerCard(CardAnswer) returns (OpChanges);
|
rpc AnswerCard(CardAnswer) returns (OpChanges);
|
||||||
rpc UpgradeScheduler(Empty) returns (Empty);
|
rpc UpgradeScheduler(Empty) returns (Empty);
|
||||||
rpc GetQueuedCards(GetQueuedCardsIn) returns (GetQueuedCardsOut);
|
rpc GetQueuedCards(GetQueuedCardsIn) returns (QueuedCards);
|
||||||
}
|
}
|
||||||
|
|
||||||
service DecksService {
|
service DecksService {
|
||||||
@ -1563,13 +1563,6 @@ message QueuedCards {
|
|||||||
uint32 review_count = 4;
|
uint32 review_count = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetQueuedCardsOut {
|
|
||||||
oneof value {
|
|
||||||
QueuedCards queued_cards = 1;
|
|
||||||
CongratsInfoOut congrats_info = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message OpChanges {
|
message OpChanges {
|
||||||
bool card = 1;
|
bool card = 1;
|
||||||
bool note = 2;
|
bool note = 2;
|
||||||
|
@ -176,9 +176,10 @@ impl SchedulingService for Backend {
|
|||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_queued_cards(&self, input: pb::GetQueuedCardsIn) -> Result<pb::GetQueuedCardsOut> {
|
fn get_queued_cards(&self, input: pb::GetQueuedCardsIn) -> Result<pb::QueuedCards> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.get_queued_cards(input.fetch_limit as usize, input.intraday_learning_only)
|
col.get_queued_cards(input.fetch_limit as usize, input.intraday_learning_only)
|
||||||
|
.map(Into::into)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -693,7 +693,7 @@ mod test {
|
|||||||
col.storage.db_scalar::<u32>("select count() from graves")?,
|
col.storage.db_scalar::<u32>("select count() from graves")?,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
assert_eq!(col.next_card()?.is_some(), false);
|
assert_eq!(col.get_next_card()?.is_some(), false);
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -704,7 +704,7 @@ mod test {
|
|||||||
col.storage.db_scalar::<u32>("select count() from graves")?,
|
col.storage.db_scalar::<u32>("select count() from graves")?,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
assert_eq!(col.next_card()?.is_some(), true);
|
assert_eq!(col.get_next_card()?.is_some(), true);
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -732,7 +732,7 @@ mod test {
|
|||||||
col.storage.db_scalar::<u32>("select count() from graves")?,
|
col.storage.db_scalar::<u32>("select count() from graves")?,
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
assert_eq!(col.next_card()?.is_some(), false);
|
assert_eq!(col.get_next_card()?.is_some(), false);
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ pub mod test_helpers {
|
|||||||
where
|
where
|
||||||
F: FnOnce(&NextCardStates) -> CardState,
|
F: FnOnce(&NextCardStates) -> CardState,
|
||||||
{
|
{
|
||||||
let queued = self.next_card()?.unwrap();
|
let queued = self.get_next_card()?.unwrap();
|
||||||
let new_state = get_state(&queued.next_states);
|
let new_state = get_state(&queued.next_states);
|
||||||
self.answer_card(&CardAnswer {
|
self.answer_card(&CardAnswer {
|
||||||
card_id: queued.card.id,
|
card_id: queued.card.id,
|
||||||
|
@ -37,7 +37,7 @@ impl QueueEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub(crate) enum QueueEntryKind {
|
pub enum QueueEntryKind {
|
||||||
New,
|
New,
|
||||||
Learning,
|
Learning,
|
||||||
Review,
|
Review,
|
||||||
|
@ -16,7 +16,7 @@ pub(crate) use main::{MainQueueEntry, MainQueueEntryKind};
|
|||||||
|
|
||||||
use self::undo::QueueUpdate;
|
use self::undo::QueueUpdate;
|
||||||
use super::{states::NextCardStates, timing::SchedTimingToday};
|
use super::{states::NextCardStates, timing::SchedTimingToday};
|
||||||
use crate::{backend_proto as pb, prelude::*, timestamp::TimestampSecs};
|
use crate::{prelude::*, timestamp::TimestampSecs};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct CardQueues {
|
pub(crate) struct CardQueues {
|
||||||
@ -32,26 +32,81 @@ pub(crate) struct CardQueues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub(crate) struct Counts {
|
pub struct Counts {
|
||||||
pub new: usize,
|
pub new: usize,
|
||||||
pub learning: usize,
|
pub learning: usize,
|
||||||
pub review: usize,
|
pub review: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct QueuedCard {
|
pub struct QueuedCard {
|
||||||
pub card: Card,
|
pub card: Card,
|
||||||
pub kind: QueueEntryKind,
|
pub kind: QueueEntryKind,
|
||||||
pub next_states: NextCardStates,
|
pub next_states: NextCardStates,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct QueuedCards {
|
#[derive(Debug)]
|
||||||
|
pub struct QueuedCards {
|
||||||
pub cards: Vec<QueuedCard>,
|
pub cards: Vec<QueuedCard>,
|
||||||
pub new_count: usize,
|
pub new_count: usize,
|
||||||
pub learning_count: usize,
|
pub learning_count: usize,
|
||||||
pub review_count: usize,
|
pub review_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
pub fn get_next_card(&mut self) -> Result<Option<QueuedCard>> {
|
||||||
|
self.get_queued_cards(1, false)
|
||||||
|
.map(|queued| queued.cards.get(0).cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_queued_cards(
|
||||||
|
&mut self,
|
||||||
|
fetch_limit: usize,
|
||||||
|
intraday_learning_only: bool,
|
||||||
|
) -> Result<QueuedCards> {
|
||||||
|
let queues = self.get_queues()?;
|
||||||
|
let counts = queues.counts();
|
||||||
|
let entries: Vec<_> = if intraday_learning_only {
|
||||||
|
queues
|
||||||
|
.intraday_now_iter()
|
||||||
|
.chain(queues.intraday_ahead_iter())
|
||||||
|
.map(Into::into)
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
queues.iter().take(fetch_limit).collect()
|
||||||
|
};
|
||||||
|
let cards: Vec<_> = entries
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| {
|
||||||
|
let card = self
|
||||||
|
.storage
|
||||||
|
.get_card(entry.card_id())?
|
||||||
|
.ok_or(AnkiError::NotFound)?;
|
||||||
|
if card.mtime != entry.mtime() {
|
||||||
|
return Err(AnkiError::invalid_input(
|
||||||
|
"bug: card modified without updating queue",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixme: pass in card instead of id
|
||||||
|
let next_states = self.get_next_card_states(card.id)?;
|
||||||
|
|
||||||
|
Ok(QueuedCard {
|
||||||
|
card,
|
||||||
|
next_states,
|
||||||
|
kind: entry.kind(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<_>>()?;
|
||||||
|
Ok(QueuedCards {
|
||||||
|
cards,
|
||||||
|
new_count: counts.new,
|
||||||
|
learning_count: counts.learning,
|
||||||
|
review_count: counts.review,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CardQueues {
|
impl CardQueues {
|
||||||
/// An iterator over the card queues, in the order the cards will
|
/// An iterator over the card queues, in the order the cards will
|
||||||
/// be presented.
|
/// be presented.
|
||||||
@ -103,26 +158,6 @@ impl CardQueues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
pub(crate) fn get_queued_cards(
|
|
||||||
&mut self,
|
|
||||||
fetch_limit: usize,
|
|
||||||
intraday_learning_only: bool,
|
|
||||||
) -> Result<pb::GetQueuedCardsOut> {
|
|
||||||
if let Some(next_cards) = self.next_cards(fetch_limit, intraday_learning_only)? {
|
|
||||||
Ok(pb::GetQueuedCardsOut {
|
|
||||||
value: Some(pb::get_queued_cards_out::Value::QueuedCards(
|
|
||||||
next_cards.into(),
|
|
||||||
)),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(pb::GetQueuedCardsOut {
|
|
||||||
value: Some(pb::get_queued_cards_out::Value::CongratsInfo(
|
|
||||||
self.congrats_info()?,
|
|
||||||
)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is automatically done when transact() is called for everything
|
/// This is automatically done when transact() is called for everything
|
||||||
/// except card answers, so unless you are modifying state outside of a
|
/// except card answers, so unless you are modifying state outside of a
|
||||||
/// transaction, you probably don't need this.
|
/// transaction, you probably don't need this.
|
||||||
@ -177,74 +212,13 @@ impl Collection {
|
|||||||
|
|
||||||
Ok(self.state.card_queues.as_mut().unwrap())
|
Ok(self.state.card_queues.as_mut().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_cards(
|
|
||||||
&mut self,
|
|
||||||
fetch_limit: usize,
|
|
||||||
intraday_learning_only: bool,
|
|
||||||
) -> Result<Option<QueuedCards>> {
|
|
||||||
let queues = self.get_queues()?;
|
|
||||||
let counts = queues.counts();
|
|
||||||
let entries: Vec<_> = if intraday_learning_only {
|
|
||||||
queues
|
|
||||||
.intraday_now_iter()
|
|
||||||
.chain(queues.intraday_ahead_iter())
|
|
||||||
.map(Into::into)
|
|
||||||
.collect()
|
|
||||||
} else {
|
|
||||||
queues.iter().take(fetch_limit).collect()
|
|
||||||
};
|
|
||||||
if entries.is_empty() {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
let cards: Vec<_> = entries
|
|
||||||
.into_iter()
|
|
||||||
.map(|entry| {
|
|
||||||
let card = self
|
|
||||||
.storage
|
|
||||||
.get_card(entry.card_id())?
|
|
||||||
.ok_or(AnkiError::NotFound)?;
|
|
||||||
if card.mtime != entry.mtime() {
|
|
||||||
return Err(AnkiError::invalid_input(
|
|
||||||
"bug: card modified without updating queue",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixme: pass in card instead of id
|
|
||||||
let next_states = self.get_next_card_states(card.id)?;
|
|
||||||
|
|
||||||
Ok(QueuedCard {
|
|
||||||
card,
|
|
||||||
next_states,
|
|
||||||
kind: entry.kind(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<_>>()?;
|
|
||||||
Ok(Some(QueuedCards {
|
|
||||||
cards,
|
|
||||||
new_count: counts.new,
|
|
||||||
learning_count: counts.learning,
|
|
||||||
review_count: counts.review,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// test helpers
|
// test helpers
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl Collection {
|
impl Collection {
|
||||||
pub(crate) fn next_card(&mut self) -> Result<Option<QueuedCard>> {
|
|
||||||
Ok(self
|
|
||||||
.next_cards(1, false)?
|
|
||||||
.map(|mut resp| resp.cards.pop().unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_queue_single(&mut self) -> Result<QueuedCards> {
|
|
||||||
self.next_cards(1, false)?.ok_or(AnkiError::NotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn counts(&mut self) -> [usize; 3] {
|
pub(crate) fn counts(&mut self) -> [usize; 3] {
|
||||||
self.get_queue_single()
|
self.get_queued_cards(1, false)
|
||||||
.map(|q| [q.new_count, q.learning_count, q.review_count])
|
.map(|q| [q.new_count, q.learning_count, q.review_count])
|
||||||
.unwrap_or([0; 3])
|
.unwrap_or([0; 3])
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ mod test {
|
|||||||
col.storage.update_deck_conf(&conf)?;
|
col.storage.update_deck_conf(&conf)?;
|
||||||
|
|
||||||
// get the first card
|
// get the first card
|
||||||
let queued = col.next_card()?.unwrap();
|
let queued = col.get_next_card()?.unwrap();
|
||||||
let cid = queued.card.id;
|
let cid = queued.card.id;
|
||||||
let sibling_cid = col.storage.all_card_ids_of_note_in_order(nid)?[1];
|
let sibling_cid = col.storage.all_card_ids_of_note_in_order(nid)?[1];
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ mod test {
|
|||||||
let deck = col.get_deck(DeckId(1))?.unwrap();
|
let deck = col.get_deck(DeckId(1))?.unwrap();
|
||||||
assert_eq!(deck.common.review_studied, 1);
|
assert_eq!(deck.common.review_studied, 1);
|
||||||
|
|
||||||
assert_eq!(col.next_card()?.is_some(), false);
|
assert_eq!(col.get_next_card()?.is_some(), false);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
@ -177,7 +177,7 @@ mod test {
|
|||||||
|
|
||||||
let deck = col.get_deck(DeckId(1))?.unwrap();
|
let deck = col.get_deck(DeckId(1))?.unwrap();
|
||||||
assert_eq!(deck.common.review_studied, 0);
|
assert_eq!(deck.common.review_studied, 0);
|
||||||
assert_eq!(col.next_card()?.is_some(), true);
|
assert_eq!(col.get_next_card()?.is_some(), true);
|
||||||
assert_eq!(col.counts(), [0, 0, 1]);
|
assert_eq!(col.counts(), [0, 0, 1]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -135,7 +135,7 @@ fn fuzz_range(interval: f32, factor: f32, minimum: f32) -> (f32, f32) {
|
|||||||
(interval - delta, interval + delta + 1.0)
|
(interval - delta, interval + delta + 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NextCardStates {
|
pub struct NextCardStates {
|
||||||
pub current: CardState,
|
pub current: CardState,
|
||||||
pub again: CardState,
|
pub again: CardState,
|
||||||
|
Loading…
Reference in New Issue
Block a user