Skip to main content

sudachi/analysis/
morpheme.rs

1/*
2 *  Copyright (c) 2021-2026 Works Applications Co., Ltd.
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *   Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 */
16
17use crate::analysis::mlist::MorphemeList;
18use crate::analysis::node::{LatticeNode, PathCost, ResultNode};
19use crate::analysis::Mode;
20use crate::dic::subset::InfoSubset;
21use crate::dic::word_id::WordId;
22use crate::dic::word_info::{WordInfo, WordInfoData, WordInfoResolver};
23use crate::dic::{DictionaryAccess, LexiconAccess};
24use crate::error::{SudachiError, SudachiResult};
25use crate::input_text::InputTextIndex;
26use std::borrow::Cow;
27use std::cell::Ref;
28use std::fmt::{Debug, Display, Formatter};
29use std::ops::Deref;
30
31/// Surface text returned by a morpheme.
32///
33/// Analysis-result morphemes borrow their surface from an `InputBuffer`, while
34/// standalone morphemes expose a dictionary headword. This wrapper keeps both
35/// cases usable through the same `Deref<Target = str>` interface.
36pub enum MorphemeSurface<'a> {
37    Input(Ref<'a, str>),
38    Headword(&'a str),
39}
40
41impl Deref for MorphemeSurface<'_> {
42    type Target = str;
43
44    fn deref(&self) -> &Self::Target {
45        match self {
46            MorphemeSurface::Input(surface) => surface.deref(),
47            MorphemeSurface::Headword(surface) => surface,
48        }
49    }
50}
51
52impl AsRef<str> for MorphemeSurface<'_> {
53    fn as_ref(&self) -> &str {
54        self.deref()
55    }
56}
57
58impl Debug for MorphemeSurface<'_> {
59    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
60        Debug::fmt(self.deref(), f)
61    }
62}
63
64impl Display for MorphemeSurface<'_> {
65    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
66        Display::fmt(self.deref(), f)
67    }
68}
69
70mod private {
71    pub trait Sealed {}
72}
73
74/// Common accessor interface for morphemes.
75///
76/// This trait is implemented by morphemes that are part of an analysis result
77/// (`Morpheme`) and by standalone morphemes materialized from a
78/// dictionary entry (`SingleMorpheme`).
79pub trait MorphemeView: private::Sealed {
80    type Dictionary: DictionaryAccess;
81
82    #[doc(hidden)]
83    fn dict(&self) -> &Self::Dictionary;
84
85    #[doc(hidden)]
86    fn subset(&self) -> InfoSubset;
87
88    /// Returns the begin index in bytes of the morpheme.
89    fn begin(&self) -> usize;
90
91    /// Returns the end index in bytes of the morpheme.
92    fn end(&self) -> usize;
93
94    /// Returns the codepoint offset of the morpheme begin.
95    fn begin_c(&self) -> usize;
96
97    /// Returns the codepoint offset of the morpheme end.
98    fn end_c(&self) -> usize;
99
100    /// Returns text corresponding to the morpheme.
101    fn surface(&self) -> MorphemeSurface<'_>;
102
103    /// Returns the ID of part of speech of the morpheme.
104    fn part_of_speech_id(&self) -> u16 {
105        self.get_word_info().pos_id()
106    }
107
108    /// Returns the word id of morpheme.
109    fn word_id(&self) -> WordId;
110
111    /// Returns the dictionary information for this morpheme.
112    fn get_word_info(&self) -> &WordInfo;
113
114    fn self_morpheme(&self) -> MorphemeRef<'_, Self::Dictionary>;
115
116    fn resolver<'a>(&'a self) -> &'a dyn WordInfoResolver
117    where
118        Self::Dictionary: 'a,
119    {
120        self.dict().lexicon()
121    }
122
123    /// Returns the part of speech.
124    fn part_of_speech<'a>(&'a self) -> &'a [String]
125    where
126        Self::Dictionary: 'a,
127    {
128        self.dict()
129            .grammar()
130            .pos_components(self.part_of_speech_id())
131    }
132
133    /// Returns the dictionary form of morpheme.
134    ///
135    /// "Dictionary form" means a word's lemma and "終止形" in Japanese.
136    fn dictionary_form<'a>(&'a self) -> &'a str
137    where
138        Self::Dictionary: 'a,
139    {
140        self.get_word_info().dictionary_form(self.resolver())
141    }
142
143    /// Returns the morpheme corresponding to this morpheme's dictionary form.
144    ///
145    /// For OOV morphemes, invalid references, and references to the same
146    /// dictionary entry, returns a morpheme equivalent to `self`. For a
147    /// distinct referenced entry, returns a standalone morpheme whose offsets
148    /// are `0..surface.len()` in bytes and `0..surface.chars().count()` in
149    /// codepoints.
150    fn dictionary_form_morpheme(&self) -> SudachiResult<MorphemeRef<'_, Self::Dictionary>>
151    where
152        Self::Dictionary: Clone,
153    {
154        resolve_referenced_form_morpheme(
155            self,
156            InfoSubset::DICTIONARY_FORM,
157            WordInfoData::dictionary_form_word_id,
158        )
159    }
160
161    /// Returns the normalized form of morpheme.
162    ///
163    /// This method returns the form normalizing inconsistent spellings and
164    /// inflected forms.
165    fn normalized_form<'a>(&'a self) -> &'a str
166    where
167        Self::Dictionary: 'a,
168    {
169        self.get_word_info().normalized_form(self.resolver())
170    }
171
172    /// Returns the morpheme corresponding to this morpheme's normalized form.
173    ///
174    /// For OOV morphemes, invalid references, and references to the same
175    /// dictionary entry, returns a morpheme equivalent to `self`. For a
176    /// distinct referenced entry, returns a standalone morpheme whose offsets
177    /// are `0..surface.len()` in bytes and `0..surface.chars().count()` in
178    /// codepoints.
179    fn normalized_form_morpheme(&self) -> SudachiResult<MorphemeRef<'_, Self::Dictionary>>
180    where
181        Self::Dictionary: Clone,
182    {
183        resolve_referenced_form_morpheme(
184            self,
185            InfoSubset::NORMALIZED_FORM,
186            WordInfoData::normalized_form_word_id,
187        )
188    }
189
190    /// Returns the reading form of morpheme.
191    ///
192    /// Returns Japanese syllabaries 'フリガナ' in katakana.
193    fn reading_form<'a>(&'a self) -> &'a str
194    where
195        Self::Dictionary: 'a,
196    {
197        self.get_word_info().reading_form(self.resolver())
198    }
199
200    /// Returns if this morpheme is out of vocabulary.
201    fn is_oov(&self) -> bool {
202        self.word_id().is_oov()
203    }
204
205    /// Returns the dictionary id where the morpheme belongs.
206    ///
207    /// Returns -1 if the morpheme is oov.
208    fn dictionary_id(&self) -> i32 {
209        let wid = self.word_id();
210        if wid.is_oov() {
211            -1
212        } else {
213            wid.dict().as_raw() as i32
214        }
215    }
216
217    fn synonym_group_ids(&self) -> &[i32] {
218        self.get_word_info().synonym_group_ids()
219    }
220
221    /// Returns user-defined data associated with this morpheme.
222    fn user_data(&self) -> &str {
223        self.get_word_info().user_data()
224    }
225}
226
227pub(crate) fn validate_dictionary_word_id<D: DictionaryAccess>(
228    dict: &D,
229    word_id: WordId,
230) -> SudachiResult<()> {
231    if word_id == WordId::INVALID || word_id.is_oov() || word_id.is_special() {
232        return Err(SudachiError::InvalidWordId(word_id));
233    }
234
235    dict.lexicon().get_word_param_checked(word_id).map(|_| ())
236}
237
238#[allow(clippy::result_large_err)]
239fn resolve_referenced_form_morpheme<'a, M, F>(
240    morpheme: &'a M,
241    reference_subset: InfoSubset,
242    form_word_id: F,
243) -> SudachiResult<MorphemeRef<'a, M::Dictionary>>
244where
245    M: MorphemeView + ?Sized,
246    M::Dictionary: Clone,
247    F: FnOnce(&WordInfoData) -> WordId,
248{
249    let word_id = morpheme.word_id();
250    if word_id.is_oov() || word_id.is_special() {
251        return Ok(morpheme.self_morpheme());
252    }
253
254    let word_info = morpheme
255        .dict()
256        .lexicon()
257        .get_word_info_subset(word_id, reference_subset)?;
258    let form_word_id = form_word_id(word_info.borrow_data());
259    if form_word_id == WordId::INVALID
260        || form_word_id == word_id
261        || form_word_id.is_oov()
262        || form_word_id.is_special()
263    {
264        return Ok(morpheme.self_morpheme());
265    }
266
267    let materialized_subset = (morpheme.subset() | InfoSubset::HEADWORD).normalize();
268
269    SingleMorpheme::from_word_id(morpheme.dict().clone(), form_word_id, materialized_subset)
270        .map(Box::new)
271        .map(MorphemeRef::Single)
272}
273
274/// A morpheme as a part of an analysis result.
275pub struct Morpheme<'a, D> {
276    list: &'a MorphemeList<D>,
277    index: usize,
278}
279
280impl<D> Clone for Morpheme<'_, D> {
281    fn clone(&self) -> Self {
282        *self
283    }
284}
285
286impl<D> Copy for Morpheme<'_, D> {}
287
288impl<D: DictionaryAccess + Clone> Morpheme<'_, D> {
289    /// Returns new morpheme list splitting the morpheme with given mode.
290    #[deprecated(note = "use split_into", since = "0.6.1")]
291    pub fn split(&self, mode: Mode) -> SudachiResult<MorphemeList<D>> {
292        #[allow(deprecated)]
293        self.list.split(mode, self.index)
294    }
295}
296
297impl<'a, D: DictionaryAccess> Morpheme<'a, D> {
298    pub(crate) fn for_list(list: &'a MorphemeList<D>, index: usize) -> Self {
299        Morpheme { list, index }
300    }
301
302    #[inline]
303    pub(crate) fn node(&self) -> &ResultNode {
304        self.list.node(self.index)
305    }
306
307    /// Returns the part of speech.
308    pub fn part_of_speech(&self) -> &[String] {
309        <Self as MorphemeView>::part_of_speech(self)
310    }
311
312    /// Returns the begin index in bytes of the morpheme in the original text.
313    pub fn begin(&self) -> usize {
314        self.list.input().to_orig_byte_idx(self.node().begin())
315    }
316
317    /// Returns the end index in bytes of the morpheme in the original text.
318    pub fn end(&self) -> usize {
319        self.list.input().to_orig_byte_idx(self.node().end())
320    }
321
322    /// Returns the codepoint offset of the morpheme begin in the original text.
323    pub fn begin_c(&self) -> usize {
324        self.list.input().to_orig_char_idx(self.node().begin())
325    }
326
327    /// Returns the codepoint offset of the morpheme end in the original text.
328    pub fn end_c(&self) -> usize {
329        self.list.input().to_orig_char_idx(self.node().end())
330    }
331
332    /// Returns a substring of the original text which corresponds to the morpheme.
333    pub fn surface(&self) -> Ref<'_, str> {
334        let inp = self.list.input();
335        Ref::map(inp, |i| i.orig_slice(self.node().bytes_range()))
336    }
337
338    pub fn part_of_speech_id(&self) -> u16 {
339        <Self as MorphemeView>::part_of_speech_id(self)
340    }
341
342    /// Returns the dictionary form of morpheme.
343    ///
344    /// "Dictionary form" means a word's lemma and "終止形" in Japanese.
345    pub fn dictionary_form(&self) -> &str {
346        <Self as MorphemeView>::dictionary_form(self)
347    }
348
349    /// Returns the morpheme corresponding to this morpheme's dictionary form.
350    pub fn dictionary_form_morpheme(&self) -> SudachiResult<MorphemeRef<'_, D>>
351    where
352        D: Clone,
353    {
354        <Self as MorphemeView>::dictionary_form_morpheme(self)
355    }
356
357    /// Returns the normalized form of morpheme.
358    ///
359    /// This method returns the form normalizing inconsistent spellings and
360    /// inflected forms.
361    pub fn normalized_form(&self) -> &str {
362        <Self as MorphemeView>::normalized_form(self)
363    }
364
365    /// Returns the morpheme corresponding to this morpheme's normalized form.
366    pub fn normalized_form_morpheme(&self) -> SudachiResult<MorphemeRef<'_, D>>
367    where
368        D: Clone,
369    {
370        <Self as MorphemeView>::normalized_form_morpheme(self)
371    }
372
373    /// Returns the reading form of morpheme.
374    ///
375    /// Returns Japanese syllabaries 'フリガナ' in katakana.
376    pub fn reading_form(&self) -> &str {
377        <Self as MorphemeView>::reading_form(self)
378    }
379
380    /// Returns if this morpheme is out of vocabulary.
381    pub fn is_oov(&self) -> bool {
382        <Self as MorphemeView>::is_oov(self)
383    }
384
385    /// Returns the word id of morpheme.
386    pub fn word_id(&self) -> WordId {
387        self.node().word_id()
388    }
389
390    /// Returns the dictionary id where the morpheme belongs.
391    ///
392    /// Returns -1 if the morpheme is oov.
393    pub fn dictionary_id(&self) -> i32 {
394        <Self as MorphemeView>::dictionary_id(self)
395    }
396
397    pub fn synonym_group_ids(&self) -> &[i32] {
398        <Self as MorphemeView>::synonym_group_ids(self)
399    }
400
401    /// Returns user-defined data associated with this morpheme.
402    pub fn user_data(&self) -> &str {
403        <Self as MorphemeView>::user_data(self)
404    }
405
406    pub fn get_word_info(&self) -> &WordInfo {
407        self.node().word_info()
408    }
409
410    /// Returns the index of this morpheme.
411    pub fn index(&self) -> usize {
412        self.index
413    }
414
415    /// Splits morpheme and writes sub-morphemes into the provided list.
416    /// The resulting list is _not_ cleared before that.
417    /// Returns true if split has produced any elements.
418    pub fn split_into(&self, mode: Mode, out: &mut MorphemeList<D>) -> SudachiResult<bool> {
419        self.list.split_into(mode, self.index, out)
420    }
421
422    /// Returns total cost from the beginning of the path.
423    pub fn total_cost(&self) -> i32 {
424        self.node().total_cost()
425    }
426}
427
428impl<D> private::Sealed for Morpheme<'_, D> {}
429
430impl<D: DictionaryAccess> MorphemeView for Morpheme<'_, D> {
431    type Dictionary = D;
432
433    fn dict(&self) -> &D {
434        self.list.dict()
435    }
436
437    fn subset(&self) -> InfoSubset {
438        self.list.subset()
439    }
440
441    fn begin(&self) -> usize {
442        Morpheme::begin(self)
443    }
444
445    fn end(&self) -> usize {
446        Morpheme::end(self)
447    }
448
449    fn begin_c(&self) -> usize {
450        Morpheme::begin_c(self)
451    }
452
453    fn end_c(&self) -> usize {
454        Morpheme::end_c(self)
455    }
456
457    fn surface(&self) -> MorphemeSurface<'_> {
458        MorphemeSurface::Input(Morpheme::surface(self))
459    }
460
461    fn word_id(&self) -> WordId {
462        Morpheme::word_id(self)
463    }
464
465    fn get_word_info(&self) -> &WordInfo {
466        Morpheme::get_word_info(self)
467    }
468
469    fn self_morpheme(&self) -> MorphemeRef<'_, D> {
470        MorphemeRef::ListItem(*self)
471    }
472}
473
474impl<D: DictionaryAccess> Debug for Morpheme<'_, D> {
475    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
476        f.debug_struct("Morpheme")
477            .field("surface", &self.surface())
478            .field("pos", &self.part_of_speech())
479            .field("normalized_form", &self.normalized_form())
480            .field("reading_form", &self.reading_form())
481            .field("dictionary_form", &self.dictionary_form())
482            .finish()
483    }
484}
485
486/// A standalone morpheme materialized from a single dictionary entry.
487pub struct SingleMorpheme<D> {
488    dict: D,
489    word_id: WordId,
490    word_info: WordInfo,
491    subset: InfoSubset,
492    begin: usize,
493    end: usize,
494    begin_c: usize,
495    end_c: usize,
496}
497
498impl<D: Clone> Clone for SingleMorpheme<D> {
499    fn clone(&self) -> Self {
500        Self {
501            dict: self.dict.clone(),
502            word_id: self.word_id,
503            word_info: self.word_info.clone(),
504            subset: self.subset,
505            begin: self.begin,
506            end: self.end,
507            begin_c: self.begin_c,
508            end_c: self.end_c,
509        }
510    }
511}
512
513impl<D: DictionaryAccess> SingleMorpheme<D> {
514    /// Creates a standalone morpheme for the exact dictionary entry.
515    ///
516    /// The entry is resolved by `WordId`, not by surface lookup, so homograph
517    /// identity is preserved. `HEADWORD` is always loaded because standalone
518    /// offsets and surface are based on the dictionary headword.
519    pub fn from_word_id(dict: D, word_id: WordId, subset: InfoSubset) -> SudachiResult<Self> {
520        validate_dictionary_word_id(&dict, word_id)?;
521        let subset = (subset | InfoSubset::HEADWORD).normalize();
522        let word_info = dict.lexicon().get_word_info_subset(word_id, subset)?;
523        let surface = word_info.headword(dict.lexicon());
524        let end = surface.len();
525        let end_c = surface.chars().count();
526
527        Ok(Self {
528            dict,
529            word_id,
530            word_info,
531            subset,
532            begin: 0,
533            end,
534            begin_c: 0,
535            end_c,
536        })
537    }
538
539    pub fn oov(
540        dict: D,
541        pos_id: u16,
542        surface: String,
543        reading: String,
544        normalized_form: String,
545        dictionary_form: String,
546    ) -> SudachiResult<Self> {
547        if pos_id as usize >= dict.grammar().pos_list.len() {
548            return Err(SudachiError::InvalidPartOfSpeech(pos_id.to_string()));
549        }
550        let end = surface.len();
551        let end_c = surface.chars().count();
552        let word_id = WordId::oov(pos_id as u32);
553        let word_info = WordInfo::new_with_strings(
554            pos_id as i16,
555            end as i16,
556            word_id,
557            surface,
558            reading,
559            normalized_form,
560            dictionary_form,
561        );
562
563        Ok(Self {
564            dict,
565            word_id,
566            word_info,
567            subset: InfoSubset::all(),
568            begin: 0,
569            end,
570            begin_c: 0,
571            end_c,
572        })
573    }
574
575    pub(crate) fn subset(&self) -> InfoSubset {
576        self.subset
577    }
578
579    pub(crate) fn dict(&self) -> &D {
580        &self.dict
581    }
582
583    /// Returns the begin index in bytes of this standalone morpheme.
584    pub fn begin(&self) -> usize {
585        self.begin
586    }
587
588    /// Returns the end index in bytes of this standalone morpheme.
589    pub fn end(&self) -> usize {
590        self.end
591    }
592
593    /// Returns the codepoint offset of this standalone morpheme begin.
594    pub fn begin_c(&self) -> usize {
595        self.begin_c
596    }
597
598    /// Returns the codepoint offset of this standalone morpheme end.
599    pub fn end_c(&self) -> usize {
600        self.end_c
601    }
602
603    /// Returns the dictionary headword surface.
604    pub fn surface(&self) -> &str {
605        self.word_info.headword(self.dict.lexicon())
606    }
607
608    /// Returns the word id of morpheme.
609    pub fn word_id(&self) -> WordId {
610        self.word_id
611    }
612
613    pub fn get_word_info(&self) -> &WordInfo {
614        &self.word_info
615    }
616}
617
618impl<D: DictionaryAccess + Clone> SingleMorpheme<D> {
619    /// Returns the part of speech.
620    pub fn part_of_speech(&self) -> &[String] {
621        <Self as MorphemeView>::part_of_speech(self)
622    }
623
624    /// Returns standalone sub-morphemes for the requested split mode.
625    ///
626    /// When the dictionary entry has no splits for the mode, returns a clone of
627    /// this morpheme. Offsets match Java's standalone morpheme behavior: a
628    /// single replacement split preserves this morpheme's span, while
629    /// multi-splits advance over each split surface.
630    pub fn split(&self, mode: Mode) -> SudachiResult<Vec<SingleMorpheme<D>>> {
631        let split_subset = match mode {
632            Mode::A => InfoSubset::SPLIT_A,
633            Mode::B => InfoSubset::SPLIT_B,
634            Mode::C => return Ok(vec![self.clone()]),
635        };
636
637        let word_info = if self.subset.contains(split_subset) {
638            Cow::Borrowed(&self.word_info)
639        } else {
640            Cow::Owned(
641                self.dict
642                    .lexicon()
643                    .get_word_info_subset(self.word_id, (self.subset | split_subset).normalize())?,
644            )
645        };
646
647        let splits = if mode == Mode::A {
648            word_info.a_unit_split()
649        } else {
650            word_info.b_unit_split()
651        };
652
653        if splits.is_empty() {
654            return Ok(vec![self.clone()]);
655        }
656
657        if let [word_id] = splits {
658            if *word_id == self.word_id {
659                return Ok(vec![self.clone()]);
660            }
661
662            let mut morpheme =
663                SingleMorpheme::from_word_id(self.dict.clone(), *word_id, self.subset)?;
664            morpheme.begin = self.begin;
665            morpheme.end = self.end;
666            morpheme.begin_c = self.begin_c;
667            morpheme.end_c = self.end_c;
668            return Ok(vec![morpheme]);
669        }
670
671        let mut result = Vec::with_capacity(splits.len());
672        let mut begin = self.begin;
673        let mut begin_c = self.begin_c;
674        for &word_id in splits {
675            let mut morpheme =
676                SingleMorpheme::from_word_id(self.dict.clone(), word_id, self.subset)?;
677            let end = begin + morpheme.surface().len();
678            let end_c = begin_c + morpheme.surface().chars().count();
679            morpheme.begin = begin;
680            morpheme.end = end;
681            morpheme.begin_c = begin_c;
682            morpheme.end_c = end_c;
683            begin = end;
684            begin_c = end_c;
685            result.push(morpheme);
686        }
687
688        Ok(result)
689    }
690
691    pub fn part_of_speech_id(&self) -> u16 {
692        <Self as MorphemeView>::part_of_speech_id(self)
693    }
694
695    /// Returns the dictionary form of morpheme.
696    pub fn dictionary_form(&self) -> &str {
697        <Self as MorphemeView>::dictionary_form(self)
698    }
699
700    /// Returns the morpheme corresponding to this morpheme's dictionary form.
701    pub fn dictionary_form_morpheme(&self) -> SudachiResult<MorphemeRef<'_, D>> {
702        <Self as MorphemeView>::dictionary_form_morpheme(self)
703    }
704
705    /// Returns the normalized form of morpheme.
706    pub fn normalized_form(&self) -> &str {
707        <Self as MorphemeView>::normalized_form(self)
708    }
709
710    /// Returns the morpheme corresponding to this morpheme's normalized form.
711    pub fn normalized_form_morpheme(&self) -> SudachiResult<MorphemeRef<'_, D>> {
712        <Self as MorphemeView>::normalized_form_morpheme(self)
713    }
714
715    /// Returns the reading form of morpheme.
716    pub fn reading_form(&self) -> &str {
717        <Self as MorphemeView>::reading_form(self)
718    }
719
720    /// Returns if this morpheme is out of vocabulary.
721    pub fn is_oov(&self) -> bool {
722        <Self as MorphemeView>::is_oov(self)
723    }
724
725    /// Returns the dictionary id where the morpheme belongs.
726    pub fn dictionary_id(&self) -> i32 {
727        <Self as MorphemeView>::dictionary_id(self)
728    }
729
730    pub fn synonym_group_ids(&self) -> &[i32] {
731        <Self as MorphemeView>::synonym_group_ids(self)
732    }
733
734    /// Returns user-defined data associated with this morpheme.
735    pub fn user_data(&self) -> &str {
736        <Self as MorphemeView>::user_data(self)
737    }
738}
739
740impl<D> private::Sealed for SingleMorpheme<D> {}
741
742impl<D: DictionaryAccess + Clone> MorphemeView for SingleMorpheme<D> {
743    type Dictionary = D;
744
745    fn dict(&self) -> &D {
746        &self.dict
747    }
748
749    fn subset(&self) -> InfoSubset {
750        self.subset
751    }
752
753    fn begin(&self) -> usize {
754        SingleMorpheme::begin(self)
755    }
756
757    fn end(&self) -> usize {
758        SingleMorpheme::end(self)
759    }
760
761    fn begin_c(&self) -> usize {
762        SingleMorpheme::begin_c(self)
763    }
764
765    fn end_c(&self) -> usize {
766        SingleMorpheme::end_c(self)
767    }
768
769    fn surface(&self) -> MorphemeSurface<'_> {
770        MorphemeSurface::Headword(SingleMorpheme::surface(self))
771    }
772
773    fn word_id(&self) -> WordId {
774        SingleMorpheme::word_id(self)
775    }
776
777    fn get_word_info(&self) -> &WordInfo {
778        SingleMorpheme::get_word_info(self)
779    }
780
781    fn self_morpheme(&self) -> MorphemeRef<'_, D> {
782        MorphemeRef::Single(Box::new(self.clone()))
783    }
784}
785
786impl<D: DictionaryAccess + Clone> Debug for SingleMorpheme<D> {
787    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
788        f.debug_struct("SingleMorpheme")
789            .field("surface", &self.surface())
790            .field("pos", &self.part_of_speech())
791            .field("normalized_form", &self.normalized_form())
792            .field("reading_form", &self.reading_form())
793            .field("dictionary_form", &self.dictionary_form())
794            .finish()
795    }
796}
797
798/// A morpheme reference that can be either list-backed or standalone.
799#[non_exhaustive]
800pub enum MorphemeRef<'a, D> {
801    ListItem(Morpheme<'a, D>),
802    Single(Box<SingleMorpheme<D>>),
803}
804
805impl<D: Clone> Clone for MorphemeRef<'_, D> {
806    fn clone(&self) -> Self {
807        match self {
808            MorphemeRef::ListItem(m) => MorphemeRef::ListItem(*m),
809            MorphemeRef::Single(m) => MorphemeRef::Single(m.clone()),
810        }
811    }
812}
813
814impl<D: DictionaryAccess + Clone> MorphemeRef<'_, D> {
815    /// Returns user-defined data associated with this morpheme.
816    pub fn user_data(&self) -> &str {
817        <Self as MorphemeView>::user_data(self)
818    }
819}
820
821impl<D> private::Sealed for MorphemeRef<'_, D> {}
822
823impl<D: DictionaryAccess + Clone> MorphemeView for MorphemeRef<'_, D> {
824    type Dictionary = D;
825
826    fn dict(&self) -> &D {
827        match self {
828            MorphemeRef::ListItem(m) => m.dict(),
829            MorphemeRef::Single(m) => m.dict(),
830        }
831    }
832
833    fn subset(&self) -> InfoSubset {
834        match self {
835            MorphemeRef::ListItem(m) => m.subset(),
836            MorphemeRef::Single(m) => m.subset(),
837        }
838    }
839
840    fn begin(&self) -> usize {
841        match self {
842            MorphemeRef::ListItem(m) => m.begin(),
843            MorphemeRef::Single(m) => m.begin(),
844        }
845    }
846
847    fn end(&self) -> usize {
848        match self {
849            MorphemeRef::ListItem(m) => m.end(),
850            MorphemeRef::Single(m) => m.end(),
851        }
852    }
853
854    fn begin_c(&self) -> usize {
855        match self {
856            MorphemeRef::ListItem(m) => m.begin_c(),
857            MorphemeRef::Single(m) => m.begin_c(),
858        }
859    }
860
861    fn end_c(&self) -> usize {
862        match self {
863            MorphemeRef::ListItem(m) => m.end_c(),
864            MorphemeRef::Single(m) => m.end_c(),
865        }
866    }
867
868    fn surface(&self) -> MorphemeSurface<'_> {
869        match self {
870            MorphemeRef::ListItem(m) => MorphemeSurface::Input(m.surface()),
871            MorphemeRef::Single(m) => MorphemeSurface::Headword(m.surface()),
872        }
873    }
874
875    fn word_id(&self) -> WordId {
876        match self {
877            MorphemeRef::ListItem(m) => m.word_id(),
878            MorphemeRef::Single(m) => m.word_id(),
879        }
880    }
881
882    fn get_word_info(&self) -> &WordInfo {
883        match self {
884            MorphemeRef::ListItem(m) => m.get_word_info(),
885            MorphemeRef::Single(m) => m.get_word_info(),
886        }
887    }
888
889    fn self_morpheme(&self) -> MorphemeRef<'_, D> {
890        self.clone()
891    }
892}
893
894impl<D: DictionaryAccess + Clone> Debug for MorphemeRef<'_, D> {
895    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
896        match self {
897            MorphemeRef::ListItem(m) => Debug::fmt(m, f),
898            MorphemeRef::Single(m) => Debug::fmt(m, f),
899        }
900    }
901}